diff --git a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less
index 48db03d791657..96b8c3442f44a 100644
--- a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less
@@ -164,6 +164,31 @@
}
}
}
+
+ .products-grid.wishlist {
+ .product-item-actions {
+ .action {
+ &.edit,
+ &.delete {
+ .lib-icon-font(
+ @icon-edit,
+ @_icon-font-size: 18px,
+ @_icon-font-line-height: 20px,
+ @_icon-font-text-hide: true,
+ @_icon-font-color: @minicart-icons-color,
+ @_icon-font-color-hover: @primary__color,
+ @_icon-font-color-active: @minicart-icons-color
+ );
+ }
+
+ &.delete {
+ .lib-icon-font-symbol(
+ @_icon-font-content: @icon-trash
+ );
+ }
+ }
+ }
+ }
}
//
@@ -211,15 +236,7 @@
&:last-child {
margin-right: 0;
}
-
- &.edit {
- float: left;
- }
-
- &.delete {
- float: right;
- }
-
+
&.edit,
&.delete {
margin-top: 7px;
diff --git a/app/design/frontend/Magento/luma/composer.json b/app/design/frontend/Magento/luma/composer.json
index 0a82ce6fdea2e..f5ba64ad9ed57 100644
--- a/app/design/frontend/Magento/luma/composer.json
+++ b/app/design/frontend/Magento/luma/composer.json
@@ -7,7 +7,7 @@
"magento/framework": "101.0.*"
},
"type": "magento2-theme",
- "version": "100.2.6",
+ "version": "100.2.7",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/design/frontend/Magento/luma/web/css/source/_forms.less b/app/design/frontend/Magento/luma/web/css/source/_forms.less
index 6701d5f9e9d21..fc637384e7a49 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_forms.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less
@@ -118,6 +118,18 @@
.lib-form-validation-note();
}
+ .product-options-wrapper {
+ .date {
+ &.required {
+ div[for*='options'] {
+ &.mage-error {
+ display: none !important;
+ }
+ }
+ }
+ }
+ }
+
// TEMP
.field .tooltip {
diff --git a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less
index 0e34d7b87387d..834423912e8a1 100644
--- a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less
+++ b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less
@@ -59,7 +59,7 @@
.modal-custom {
.action-close {
- .lib-css(margin, @indent__m);
+ .lib-css(margin, 15px);
}
}
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 05fd34a178ded..5e7d4f67b8b23 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/composer.json b/composer.json
index d04bf269d4485..8df1d26295124 100644
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,7 @@
"name": "magento/magento2ce",
"description": "Magento 2 (Open Source)",
"type": "project",
- "version": "2.2.8-dev",
+ "version": "2.2.9-dev",
"license": [
"OSL-3.0",
"AFL-3.0"
@@ -88,124 +88,125 @@
},
"replace": {
"magento/module-marketplace": "100.2.4",
- "magento/module-admin-notification": "100.2.5",
+ "magento/module-admin-notification": "100.2.6",
"magento/module-advanced-pricing-import-export": "100.2.5",
- "magento/module-analytics": "100.2.4",
+ "magento/module-analytics": "100.2.5",
"magento/module-authorization": "100.2.3",
- "magento/module-authorizenet": "100.2.3",
- "magento/module-backend": "100.2.7",
- "magento/module-backup": "100.2.6",
- "magento/module-braintree": "100.2.7",
- "magento/module-bundle": "100.2.6",
- "magento/module-bundle-import-export": "100.2.4",
- "magento/module-cache-invalidate": "100.2.3",
- "magento/module-captcha": "100.2.4",
- "magento/module-catalog": "102.0.7",
+ "magento/module-authorizenet": "100.2.4",
+ "magento/module-backend": "100.2.8",
+ "magento/module-backup": "100.2.7",
+ "magento/module-braintree": "100.2.8",
+ "magento/module-bundle": "100.2.7",
+ "magento/module-bundle-import-export": "100.2.5",
+ "magento/module-cache-invalidate": "100.2.4",
+ "magento/module-captcha": "100.2.5",
+ "magento/module-catalog": "102.0.8",
"magento/module-catalog-analytics": "100.2.3",
- "magento/module-catalog-import-export": "100.2.6",
- "magento/module-catalog-inventory": "100.2.6",
- "magento/module-catalog-rule": "101.0.6",
+ "magento/module-catalog-import-export": "100.2.7",
+ "magento/module-catalog-inventory": "100.2.7",
+ "magento/module-catalog-rule": "101.0.7",
"magento/module-catalog-rule-configurable": "100.2.3",
- "magento/module-catalog-search": "100.2.6",
- "magento/module-catalog-url-rewrite": "100.2.6",
- "magento/module-catalog-widget": "100.2.4",
- "magento/module-checkout": "100.2.7",
- "magento/module-checkout-agreements": "100.2.3",
- "magento/module-cms": "102.0.7",
+ "magento/module-catalog-search": "100.2.7",
+ "magento/module-catalog-url-rewrite": "100.2.7",
+ "magento/module-catalog-widget": "100.2.5",
+ "magento/module-checkout": "100.2.8",
+ "magento/module-checkout-agreements": "100.2.4",
+ "magento/module-cms": "102.0.8",
"magento/module-cms-url-rewrite": "100.2.3",
- "magento/module-config": "101.0.7",
- "magento/module-configurable-import-export": "100.2.4",
- "magento/module-configurable-product": "100.2.7",
+ "magento/module-config": "101.0.8",
+ "magento/module-configurable-import-export": "100.2.5",
+ "magento/module-configurable-product": "100.2.8",
"magento/module-configurable-product-sales": "100.2.4",
- "magento/module-contact": "100.2.4",
+ "magento/module-contact": "100.2.5",
"magento/module-cookie": "100.2.3",
- "magento/module-cron": "100.2.5",
- "magento/module-currency-symbol": "100.2.3",
- "magento/module-customer": "101.0.7",
+ "magento/module-cron": "100.2.6",
+ "magento/module-currency-symbol": "100.2.4",
+ "magento/module-customer": "101.0.8",
"magento/module-customer-analytics": "100.2.3",
- "magento/module-customer-import-export": "100.2.5",
- "magento/module-deploy": "100.2.6",
- "magento/module-developer": "100.2.5",
+ "magento/module-customer-import-export": "100.2.6",
+ "magento/module-deploy": "100.2.7",
+ "magento/module-developer": "100.2.6",
"magento/module-dhl": "100.2.4",
- "magento/module-directory": "100.2.6",
- "magento/module-downloadable": "100.2.6",
+ "magento/module-directory": "100.2.7",
+ "magento/module-downloadable": "100.2.7",
"magento/module-downloadable-import-export": "100.2.3",
- "magento/module-eav": "101.0.6",
- "magento/module-email": "100.2.5",
+ "magento/module-eav": "101.0.7",
+ "magento/module-email": "100.2.6",
"magento/module-encryption-key": "100.2.3",
"magento/module-fedex": "100.2.4",
- "magento/module-gift-message": "100.2.3",
+ "magento/module-gift-message": "100.2.4",
"magento/module-google-adwords": "100.2.3",
- "magento/module-google-analytics": "100.2.5",
+ "magento/module-google-analytics": "100.2.6",
"magento/module-google-optimizer": "100.2.4",
- "magento/module-grouped-import-export": "100.2.3",
- "magento/module-grouped-product": "100.2.5",
- "magento/module-import-export": "100.2.7",
- "magento/module-indexer": "100.2.5",
- "magento/module-instant-purchase": "100.2.3",
- "magento/module-integration": "100.2.5",
- "magento/module-layered-navigation": "100.2.4",
+ "magento/module-grouped-import-export": "100.2.4",
+ "magento/module-grouped-product": "100.2.6",
+ "magento/module-import-export": "100.2.8",
+ "magento/module-indexer": "100.2.6",
+ "magento/module-instant-purchase": "100.2.4",
+ "magento/module-integration": "100.2.6",
+ "magento/module-layered-navigation": "100.2.5",
"magento/module-media-storage": "100.2.3",
- "magento/module-msrp": "100.2.3",
- "magento/module-multishipping": "100.2.4",
- "magento/module-new-relic-reporting": "100.2.5",
- "magento/module-newsletter": "100.2.6",
+ "magento/module-msrp": "100.2.4",
+ "magento/module-multishipping": "100.2.5",
+ "magento/module-new-relic-reporting": "100.2.6",
+ "magento/module-newsletter": "100.2.7",
"magento/module-offline-payments": "100.2.3",
- "magento/module-offline-shipping": "100.2.5",
- "magento/module-page-cache": "100.2.4",
- "magento/module-payment": "100.2.5",
- "magento/module-paypal": "100.2.5",
- "magento/module-persistent": "100.2.3",
+ "magento/module-offline-shipping": "100.2.6",
+ "magento/module-page-cache": "100.2.5",
+ "magento/module-payment": "100.2.6",
+ "magento/module-paypal": "100.2.6",
+ "magento/module-paypal-captcha": "100.2.0",
+ "magento/module-persistent": "100.2.4",
"magento/module-product-alert": "100.2.4",
- "magento/module-product-video": "100.2.5",
- "magento/module-quote": "101.0.6",
+ "magento/module-product-video": "100.2.6",
+ "magento/module-quote": "101.0.7",
"magento/module-quote-analytics": "100.2.3",
"magento/module-release-notification": "100.2.4",
- "magento/module-reports": "100.2.7",
+ "magento/module-reports": "100.2.8",
"magento/module-require-js": "100.2.4",
- "magento/module-review": "100.2.7",
+ "magento/module-review": "100.2.8",
"magento/module-review-analytics": "100.2.3",
"magento/module-robots": "100.2.4",
"magento/module-rss": "100.2.3",
- "magento/module-rule": "100.2.4",
- "magento/module-sales": "101.0.6",
+ "magento/module-rule": "100.2.5",
+ "magento/module-sales": "101.0.7",
"magento/module-sales-analytics": "100.2.3",
"magento/module-sales-inventory": "100.2.3",
- "magento/module-sales-rule": "101.0.5",
+ "magento/module-sales-rule": "101.0.6",
"magento/module-sales-sequence": "100.2.3",
"magento/module-sample-data": "100.2.5",
- "magento/module-search": "100.2.6",
- "magento/module-security": "100.2.4",
- "magento/module-send-friend": "100.2.3",
- "magento/module-shipping": "100.2.7",
- "magento/module-signifyd": "100.2.4",
- "magento/module-sitemap": "100.2.6",
- "magento/module-store": "100.2.6",
+ "magento/module-search": "100.2.7",
+ "magento/module-security": "100.2.5",
+ "magento/module-send-friend": "100.2.4",
+ "magento/module-shipping": "100.2.8",
+ "magento/module-signifyd": "100.2.5",
+ "magento/module-sitemap": "100.2.7",
+ "magento/module-store": "100.2.7",
"magento/module-swagger-webapi": "100.2.1",
"magento/module-swagger": "100.2.5",
- "magento/module-swatches": "100.2.5",
+ "magento/module-swatches": "100.2.6",
"magento/module-swatches-layered-navigation": "100.2.3",
- "magento/module-tax": "100.2.7",
+ "magento/module-tax": "100.2.8",
"magento/module-tax-import-export": "100.2.3",
- "magento/module-theme": "100.2.7",
+ "magento/module-theme": "100.2.8",
"magento/module-translation": "100.2.6",
- "magento/module-ui": "101.0.7",
- "magento/module-ups": "100.2.5",
- "magento/module-url-rewrite": "101.0.6",
- "magento/module-user": "101.0.5",
- "magento/module-usps": "100.2.5",
+ "magento/module-ui": "101.0.8",
+ "magento/module-ups": "100.2.6",
+ "magento/module-url-rewrite": "101.0.7",
+ "magento/module-user": "101.0.6",
+ "magento/module-usps": "100.2.6",
"magento/module-variable": "100.2.6",
"magento/module-vault": "101.0.5",
"magento/module-version": "100.2.3",
- "magento/module-webapi": "100.2.5",
+ "magento/module-webapi": "100.2.6",
"magento/module-webapi-security": "100.2.4",
- "magento/module-weee": "100.2.4",
- "magento/module-widget": "101.0.5",
- "magento/module-wishlist": "101.0.5",
+ "magento/module-weee": "100.2.5",
+ "magento/module-widget": "101.0.6",
+ "magento/module-wishlist": "101.0.6",
"magento/module-wishlist-analytics": "100.2.3",
- "magento/theme-adminhtml-backend": "100.2.5",
- "magento/theme-frontend-blank": "100.2.5",
- "magento/theme-frontend-luma": "100.2.6",
+ "magento/theme-adminhtml-backend": "100.2.6",
+ "magento/theme-frontend-blank": "100.2.6",
+ "magento/theme-frontend-luma": "100.2.7",
"magento/language-de_de": "100.2.0",
"magento/language-en_us": "100.2.0",
"magento/language-es_es": "100.2.0",
@@ -213,7 +214,7 @@
"magento/language-nl_nl": "100.2.0",
"magento/language-pt_br": "100.2.0",
"magento/language-zh_hans_cn": "100.2.0",
- "magento/framework": "101.0.7",
+ "magento/framework": "101.0.8",
"trentrichardson/jquery-timepicker-addon": "1.4.3",
"components/jquery": "1.11.0",
"blueimp/jquery-file-upload": "5.6.14",
diff --git a/composer.lock b/composer.lock
index 384ff14618a80..acdfe237272f1 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d57f148fd874b8685bf73bbf0a0ea75a",
+ "content-hash": "442a108524a23e16cbb70c8957cd3097",
"packages": [
{
"name": "braintree/braintree_php",
@@ -204,16 +204,16 @@
},
{
"name": "composer/ca-bundle",
- "version": "1.1.2",
+ "version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
- "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0"
+ "reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0",
- "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/558f321c52faeb4828c03e7dc0cfe39a09e09a2d",
+ "reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d",
"shasum": ""
},
"require": {
@@ -256,7 +256,7 @@
"ssl",
"tls"
],
- "time": "2018-08-08T08:57:40+00:00"
+ "time": "2019-01-28T09:30:10+00:00"
},
{
"name": "composer/composer",
@@ -337,16 +337,16 @@
},
{
"name": "composer/semver",
- "version": "1.4.2",
+ "version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
- "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573"
+ "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573",
- "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573",
+ "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
+ "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
"shasum": ""
},
"require": {
@@ -395,28 +395,27 @@
"validation",
"versioning"
],
- "time": "2016-08-30T16:08:34+00:00"
+ "time": "2019-03-19T17:25:45+00:00"
},
{
"name": "composer/spdx-licenses",
- "version": "1.4.0",
+ "version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/composer/spdx-licenses.git",
- "reference": "cb17687e9f936acd7e7245ad3890f953770dec1b"
+ "reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/cb17687e9f936acd7e7245ad3890f953770dec1b",
- "reference": "cb17687e9f936acd7e7245ad3890f953770dec1b",
+ "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d",
+ "reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d",
"shasum": ""
},
"require": {
- "php": "^5.3.2 || ^7.0"
+ "php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5",
- "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7"
},
"type": "library",
"extra": {
@@ -456,7 +455,7 @@
"spdx",
"validator"
],
- "time": "2018-04-30T10:33:04+00:00"
+ "time": "2019-03-26T10:23:26+00:00"
},
{
"name": "container-interop/container-interop",
@@ -491,23 +490,23 @@
},
{
"name": "justinrainbow/json-schema",
- "version": "5.2.7",
+ "version": "5.2.8",
"source": {
"type": "git",
"url": "https://github.com/justinrainbow/json-schema.git",
- "reference": "8560d4314577199ba51bf2032f02cd1315587c23"
+ "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/8560d4314577199ba51bf2032f02cd1315587c23",
- "reference": "8560d4314577199ba51bf2032f02cd1315587c23",
+ "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4",
+ "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^2.1",
+ "friendsofphp/php-cs-fixer": "~2.2.20",
"json-schema/json-schema-test-suite": "1.2.0",
"phpunit/phpunit": "^4.8.35"
},
@@ -553,7 +552,7 @@
"json",
"schema"
],
- "time": "2018-02-14T22:26:30+00:00"
+ "time": "2019-01-14T23:55:14+00:00"
},
{
"name": "magento/composer",
@@ -719,16 +718,16 @@
},
{
"name": "monolog/monolog",
- "version": "1.23.0",
+ "version": "1.24.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4"
+ "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
- "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
+ "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
"shasum": ""
},
"require": {
@@ -793,7 +792,7 @@
"logging",
"psr-3"
],
- "time": "2017-06-19T01:22:40+00:00"
+ "time": "2018-11-05T09:00:11+00:00"
},
{
"name": "oyejorge/less.php",
@@ -859,16 +858,16 @@
},
{
"name": "paragonie/random_compat",
- "version": "v2.0.17",
+ "version": "v2.0.18",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
- "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d"
+ "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d",
- "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
+ "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
"shasum": ""
},
"require": {
@@ -904,28 +903,33 @@
"pseudorandom",
"random"
],
- "time": "2018-07-04T16:31:37+00:00"
+ "time": "2019-01-03T20:59:08+00:00"
},
{
"name": "pelago/emogrifier",
- "version": "v2.0.0",
+ "version": "v2.1.1",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/emogrifier.git",
- "reference": "8babf8ddbf348f26b29674e2f84db66ff7e3d95e"
+ "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/8babf8ddbf348f26b29674e2f84db66ff7e3d95e",
- "reference": "8babf8ddbf348f26b29674e2f84db66ff7e3d95e",
+ "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/8ee7fb5ad772915451ed3415c1992bd3697d4983",
+ "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983",
"shasum": ""
},
"require": {
- "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0"
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0",
+ "symfony/css-selector": "^3.4.0 || ^4.0.0"
},
"require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.2.0",
+ "phpmd/phpmd": "^2.6.0",
"phpunit/phpunit": "^4.8.0",
- "squizlabs/php_codesniffer": "^3.1.0"
+ "squizlabs/php_codesniffer": "^3.3.2"
},
"type": "library",
"extra": {
@@ -935,7 +939,7 @@
},
"autoload": {
"psr-4": {
- "Pelago\\": "Classes/"
+ "Pelago\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -953,10 +957,6 @@
{
"name": "Jaime Prado"
},
- {
- "name": "Roman Ožana",
- "email": "ozana@omdesign.cz"
- },
{
"name": "Oliver Klee",
"email": "github@oliverklee.de"
@@ -964,6 +964,10 @@
{
"name": "Zoli Szabó",
"email": "zoli.szabo+github@gmail.com"
+ },
+ {
+ "name": "Jake Hotson",
+ "email": "jake@qzdesign.co.uk"
}
],
"description": "Converts CSS styles into inline style attributes in your HTML code",
@@ -973,20 +977,20 @@
"email",
"pre-processing"
],
- "time": "2018-01-05T23:30:21+00:00"
+ "time": "2018-12-10T10:36:30+00:00"
},
{
"name": "phpseclib/phpseclib",
- "version": "2.0.11",
+ "version": "2.0.15",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b"
+ "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7053f06f91b3de78e143d430e55a8f7889efc08b",
- "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/11cf67cf78dc4acb18dc9149a57be4aee5036ce0",
+ "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0",
"shasum": ""
},
"require": {
@@ -1065,7 +1069,7 @@
"x.509",
"x509"
],
- "time": "2018-04-15T16:55:05+00:00"
+ "time": "2019-03-10T16:53:45+00:00"
},
{
"name": "psr/container",
@@ -1168,16 +1172,16 @@
},
{
"name": "psr/log",
- "version": "1.0.2",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
- "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
- "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
"shasum": ""
},
"require": {
@@ -1211,7 +1215,7 @@
"psr",
"psr-3"
],
- "time": "2016-10-10T12:19:37+00:00"
+ "time": "2018-11-20T15:27:04+00:00"
},
{
"name": "ramsey/uuid",
@@ -1436,16 +1440,16 @@
},
{
"name": "symfony/console",
- "version": "v2.8.46",
+ "version": "v2.8.49",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789"
+ "reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789",
- "reference": "aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789",
+ "url": "https://api.github.com/repos/symfony/console/zipball/cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12",
+ "reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12",
"shasum": ""
},
"require": {
@@ -1493,7 +1497,60 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2018-09-30T03:33:07+00:00"
+ "time": "2018-11-20T15:55:20+00:00"
+ },
+ {
+ "name": "symfony/css-selector",
+ "version": "v3.4.23",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
+ "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony CssSelector Component",
+ "homepage": "https://symfony.com",
+ "time": "2019-01-16T09:39:14+00:00"
},
{
"name": "symfony/debug",
@@ -1554,16 +1611,16 @@
},
{
"name": "symfony/event-dispatcher",
- "version": "v2.8.46",
+ "version": "v2.8.49",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12"
+ "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/84ae343f39947aa084426ed1138bb96bf94d1f12",
- "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
+ "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0",
"shasum": ""
},
"require": {
@@ -1610,20 +1667,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2018-07-26T09:03:18+00:00"
+ "time": "2018-11-21T14:20:20+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "d69930fc337d767607267d57c20a7403d0a822a4"
+ "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/d69930fc337d767607267d57c20a7403d0a822a4",
- "reference": "d69930fc337d767607267d57c20a7403d0a822a4",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/acf99758b1df8e9295e6b85aa69f294565c9fedb",
+ "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb",
"shasum": ""
},
"require": {
@@ -1660,20 +1717,20 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
- "time": "2018-10-02T12:28:39+00:00"
+ "time": "2019-02-04T21:34:32+00:00"
},
{
"name": "symfony/finder",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d"
+ "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/54ba444dddc5bd5708a34bd095ea67c6eb54644d",
- "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/fcdde4aa38f48190ce70d782c166f23930084f9b",
+ "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b",
"shasum": ""
},
"require": {
@@ -1709,20 +1766,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
- "time": "2018-10-03T08:46:40+00:00"
+ "time": "2019-02-22T14:44:53+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.9.0",
+ "version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
+ "reference": "82ebae02209c21113908c229e9883c419720738a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
- "reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
+ "reference": "82ebae02209c21113908c229e9883c419720738a",
"shasum": ""
},
"require": {
@@ -1734,7 +1791,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9-dev"
+ "dev-master": "1.11-dev"
}
},
"autoload": {
@@ -1767,20 +1824,20 @@
"polyfill",
"portable"
],
- "time": "2018-08-06T14:22:27+00:00"
+ "time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.9.0",
+ "version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8"
+ "reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8",
- "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
+ "reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
"shasum": ""
},
"require": {
@@ -1792,7 +1849,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9-dev"
+ "dev-master": "1.11-dev"
}
},
"autoload": {
@@ -1826,20 +1883,20 @@
"portable",
"shim"
],
- "time": "2018-08-06T14:22:27+00:00"
+ "time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/process",
- "version": "v2.8.46",
+ "version": "v2.8.49",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6"
+ "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6",
- "reference": "f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6",
+ "url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8",
+ "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8",
"shasum": ""
},
"require": {
@@ -1875,7 +1932,7 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "time": "2018-09-06T17:11:15+00:00"
+ "time": "2018-11-11T11:18:13+00:00"
},
{
"name": "tedivm/jshrink",
@@ -2294,16 +2351,16 @@
},
{
"name": "zendframework/zend-db",
- "version": "2.9.3",
+ "version": "2.10.0",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-db.git",
- "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9"
+ "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-db/zipball/5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9",
- "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9",
+ "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e",
+ "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e",
"shasum": ""
},
"require": {
@@ -2314,7 +2371,7 @@
"phpunit/phpunit": "^5.7.25 || ^6.4.4",
"zendframework/zend-coding-standard": "~1.0.0",
"zendframework/zend-eventmanager": "^2.6.2 || ^3.0",
- "zendframework/zend-hydrator": "^1.1 || ^2.1",
+ "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0",
"zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3"
},
"suggest": {
@@ -2348,7 +2405,7 @@
"db",
"zf"
],
- "time": "2018-04-09T13:21:36+00:00"
+ "time": "2019-02-25T11:37:45+00:00"
},
{
"name": "zendframework/zend-di",
@@ -2555,16 +2612,16 @@
},
{
"name": "zendframework/zend-filter",
- "version": "2.8.0",
+ "version": "2.9.1",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-filter.git",
- "reference": "7b997dbe79459f1652deccc8786d7407fb66caa9"
+ "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/7b997dbe79459f1652deccc8786d7407fb66caa9",
- "reference": "7b997dbe79459f1652deccc8786d7407fb66caa9",
+ "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f",
+ "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f",
"shasum": ""
},
"require": {
@@ -2577,12 +2634,14 @@
"require-dev": {
"pear/archive_tar": "^1.4.3",
"phpunit/phpunit": "^5.7.23 || ^6.4.3",
+ "psr/http-factory": "^1.0",
"zendframework/zend-coding-standard": "~1.0.0",
"zendframework/zend-crypt": "^3.2.1",
"zendframework/zend-servicemanager": "^2.7.8 || ^3.3",
"zendframework/zend-uri": "^2.6"
},
"suggest": {
+ "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters",
"zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters",
"zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality",
"zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality",
@@ -2591,8 +2650,8 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.8.x-dev",
- "dev-develop": "2.9.x-dev"
+ "dev-master": "2.9.x-dev",
+ "dev-develop": "2.10.x-dev"
},
"zf": {
"component": "Zend\\Filter",
@@ -2614,25 +2673,25 @@
"filter",
"zf"
],
- "time": "2018-04-11T16:20:04+00:00"
+ "time": "2018-12-17T16:00:04+00:00"
},
{
"name": "zendframework/zend-form",
- "version": "2.12.0",
+ "version": "2.13.0",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-form.git",
- "reference": "565fb4f4bb3e0dbeea0173c923c4a8be77de9441"
+ "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-form/zipball/565fb4f4bb3e0dbeea0173c923c4a8be77de9441",
- "reference": "565fb4f4bb3e0dbeea0173c923c4a8be77de9441",
+ "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da",
+ "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0",
- "zendframework/zend-hydrator": "^1.1 || ^2.1",
+ "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0",
"zendframework/zend-inputfilter": "^2.8",
"zendframework/zend-stdlib": "^2.7 || ^3.0"
},
@@ -2666,8 +2725,8 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.12.x-dev",
- "dev-develop": "2.13.x-dev"
+ "dev-master": "2.13.x-dev",
+ "dev-develop": "2.14.x-dev"
},
"zf": {
"component": "Zend\\Form",
@@ -2692,20 +2751,20 @@
"form",
"zf"
],
- "time": "2018-05-16T18:49:44+00:00"
+ "time": "2018-12-11T22:51:29+00:00"
},
{
"name": "zendframework/zend-http",
- "version": "2.8.2",
+ "version": "2.8.4",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-http.git",
- "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b"
+ "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-http/zipball/2c8aed3d25522618573194e7cc51351f8cd4a45b",
- "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b",
+ "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807",
+ "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807",
"shasum": ""
},
"require": {
@@ -2747,7 +2806,7 @@
"zend",
"zf"
],
- "time": "2018-08-13T18:47:03+00:00"
+ "time": "2019-02-07T17:47:08+00:00"
},
{
"name": "zendframework/zend-hydrator",
@@ -2877,34 +2936,38 @@
},
{
"name": "zendframework/zend-inputfilter",
- "version": "2.8.2",
+ "version": "2.10.0",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-inputfilter.git",
- "reference": "3f02179e014d9ef0faccda2ad6c65d38adc338d8"
+ "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/3f02179e014d9ef0faccda2ad6c65d38adc338d8",
- "reference": "3f02179e014d9ef0faccda2ad6c65d38adc338d8",
+ "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c",
+ "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0",
- "zendframework/zend-filter": "^2.6",
+ "zendframework/zend-filter": "^2.9.1",
"zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1",
"zendframework/zend-stdlib": "^2.7 || ^3.0",
- "zendframework/zend-validator": "^2.10.1"
+ "zendframework/zend-validator": "^2.11"
},
"require-dev": {
"phpunit/phpunit": "^5.7.23 || ^6.4.3",
+ "psr/http-message": "^1.0",
"zendframework/zend-coding-standard": "~1.0.0"
},
+ "suggest": {
+ "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads"
+ },
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.8.x-dev",
- "dev-develop": "2.9.x-dev"
+ "dev-master": "2.10.x-dev",
+ "dev-develop": "2.11.x-dev"
},
"zf": {
"component": "Zend\\InputFilter",
@@ -2926,7 +2989,7 @@
"inputfilter",
"zf"
],
- "time": "2018-05-14T17:38:03+00:00"
+ "time": "2019-01-30T16:58:51+00:00"
},
{
"name": "zendframework/zend-json",
@@ -3163,16 +3226,16 @@
},
{
"name": "zendframework/zend-math",
- "version": "2.7.0",
+ "version": "2.7.1",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-math.git",
- "reference": "f4358090d5d23973121f1ed0b376184b66d9edec"
+ "reference": "1abce074004dacac1a32cd54de94ad47ef960d38"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-math/zipball/f4358090d5d23973121f1ed0b376184b66d9edec",
- "reference": "f4358090d5d23973121f1ed0b376184b66d9edec",
+ "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38",
+ "reference": "1abce074004dacac1a32cd54de94ad47ef960d38",
"shasum": ""
},
"require": {
@@ -3209,7 +3272,7 @@
"math",
"zf2"
],
- "time": "2016-04-07T16:29:53+00:00"
+ "time": "2018-12-04T15:34:17+00:00"
},
{
"name": "zendframework/zend-mime",
@@ -3851,16 +3914,16 @@
},
{
"name": "zendframework/zend-uri",
- "version": "2.6.1",
+ "version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-uri.git",
- "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f"
+ "reference": "b2785cd38fe379a784645449db86f21b7739b1ee"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/3b6463645c6766f78ce537c70cb4fdabee1e725f",
- "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f",
+ "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee",
+ "reference": "b2785cd38fe379a784645449db86f21b7739b1ee",
"shasum": ""
},
"require": {
@@ -3875,8 +3938,8 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6.x-dev",
- "dev-develop": "2.7.x-dev"
+ "dev-master": "2.7.x-dev",
+ "dev-develop": "2.8.x-dev"
}
},
"autoload": {
@@ -3894,20 +3957,20 @@
"uri",
"zf"
],
- "time": "2018-04-30T13:40:08+00:00"
+ "time": "2019-02-27T21:39:04+00:00"
},
{
"name": "zendframework/zend-validator",
- "version": "2.10.2",
+ "version": "2.11.1",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-validator.git",
- "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9"
+ "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9",
- "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9",
+ "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3",
+ "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3",
"shasum": ""
},
"require": {
@@ -3917,6 +3980,7 @@
},
"require-dev": {
"phpunit/phpunit": "^6.0.8 || ^5.7.15",
+ "psr/http-message": "^1.0",
"zendframework/zend-cache": "^2.6.1",
"zendframework/zend-coding-standard": "~1.0.0",
"zendframework/zend-config": "^2.6",
@@ -3930,6 +3994,7 @@
"zendframework/zend-uri": "^2.5"
},
"suggest": {
+ "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators",
"zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator",
"zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator",
"zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages",
@@ -3942,8 +4007,8 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.10.x-dev",
- "dev-develop": "2.11.x-dev"
+ "dev-master": "2.11.x-dev",
+ "dev-develop": "2.12.x-dev"
},
"zf": {
"component": "Zend\\Validator",
@@ -3965,25 +4030,26 @@
"validator",
"zf2"
],
- "time": "2018-02-01T17:05:33+00:00"
+ "time": "2019-01-29T22:26:39+00:00"
},
{
"name": "zendframework/zend-view",
- "version": "2.10.0",
+ "version": "2.11.2",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-view.git",
- "reference": "4478cc5dd960e2339d88b363ef99fa278700e80e"
+ "reference": "4f5cb653ed4c64bb8d9bf05b294300feb00c67f2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zendframework/zend-view/zipball/4478cc5dd960e2339d88b363ef99fa278700e80e",
- "reference": "4478cc5dd960e2339d88b363ef99fa278700e80e",
+ "url": "https://api.github.com/repos/zendframework/zend-view/zipball/4f5cb653ed4c64bb8d9bf05b294300feb00c67f2",
+ "reference": "4f5cb653ed4c64bb8d9bf05b294300feb00c67f2",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0",
"zendframework/zend-eventmanager": "^2.6.2 || ^3.0",
+ "zendframework/zend-json": "^2.6.1 || ^3.0",
"zendframework/zend-loader": "^2.5",
"zendframework/zend-stdlib": "^2.7 || ^3.0"
},
@@ -3999,10 +4065,9 @@
"zendframework/zend-filter": "^2.6.1",
"zendframework/zend-http": "^2.5.4",
"zendframework/zend-i18n": "^2.6",
- "zendframework/zend-json": "^2.6.1",
"zendframework/zend-log": "^2.7",
"zendframework/zend-modulemanager": "^2.7.1",
- "zendframework/zend-mvc": "^2.7 || ^3.0",
+ "zendframework/zend-mvc": "^2.7.14 || ^3.0",
"zendframework/zend-navigation": "^2.5",
"zendframework/zend-paginator": "^2.5",
"zendframework/zend-permissions-acl": "^2.6",
@@ -4019,8 +4084,8 @@
"zendframework/zend-filter": "Zend\\Filter component",
"zendframework/zend-http": "Zend\\Http component",
"zendframework/zend-i18n": "Zend\\I18n component",
- "zendframework/zend-json": "Zend\\Json component",
"zendframework/zend-mvc": "Zend\\Mvc component",
+ "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up",
"zendframework/zend-navigation": "Zend\\Navigation component",
"zendframework/zend-paginator": "Zend\\Paginator component",
"zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component",
@@ -4033,8 +4098,8 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.10.x-dev",
- "dev-develop": "2.11.x-dev"
+ "dev-master": "2.11.x-dev",
+ "dev-develop": "2.12.x-dev"
}
},
"autoload": {
@@ -4052,7 +4117,7 @@
"view",
"zf2"
],
- "time": "2018-01-17T22:21:50+00:00"
+ "time": "2019-02-19T17:40:15+00:00"
}
],
"packages-dev": [
@@ -4161,16 +4226,16 @@
},
{
"name": "behat/gherkin",
- "version": "v4.4.5",
+ "version": "v4.6.0",
"source": {
"type": "git",
"url": "https://github.com/Behat/Gherkin.git",
- "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74"
+ "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74",
- "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74",
+ "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07",
+ "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07",
"shasum": ""
},
"require": {
@@ -4178,8 +4243,8 @@
},
"require-dev": {
"phpunit/phpunit": "~4.5|~5",
- "symfony/phpunit-bridge": "~2.7|~3",
- "symfony/yaml": "~2.3|~3"
+ "symfony/phpunit-bridge": "~2.7|~3|~4",
+ "symfony/yaml": "~2.3|~3|~4"
},
"suggest": {
"symfony/yaml": "If you want to parse features, represented in YAML files"
@@ -4216,35 +4281,32 @@
"gherkin",
"parser"
],
- "time": "2016-10-30T11:50:56+00:00"
+ "time": "2019-01-16T14:22:17+00:00"
},
{
"name": "codeception/codeception",
- "version": "2.3.9",
+ "version": "2.4.5",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Codeception.git",
- "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e"
+ "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e",
- "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e",
+ "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5fee32d5c82791548931cbc34806b4de6aa1abfc",
+ "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc",
"shasum": ""
},
"require": {
- "behat/gherkin": "~4.4.0",
- "codeception/stub": "^1.0",
+ "behat/gherkin": "^4.4.0",
+ "codeception/phpunit-wrapper": "^6.0.9|^7.0.6",
+ "codeception/stub": "^2.0",
"ext-json": "*",
"ext-mbstring": "*",
"facebook/webdriver": ">=1.1.3 <2.0",
"guzzlehttp/guzzle": ">=4.1.4 <7.0",
"guzzlehttp/psr7": "~1.0",
- "php": ">=5.4.0 <8.0",
- "phpunit/php-code-coverage": ">=2.2.4 <6.0",
- "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0",
- "sebastian/comparator": ">1.1 <3.0",
- "sebastian/diff": ">=1.4 <3.0",
+ "php": ">=5.6.0 <8.0",
"symfony/browser-kit": ">=2.7 <5.0",
"symfony/console": ">=2.7 <5.0",
"symfony/css-selector": ">=2.7 <5.0",
@@ -4310,27 +4372,70 @@
"functional testing",
"unit testing"
],
- "time": "2018-02-26T23:29:41+00:00"
+ "time": "2018-08-01T07:21:49+00:00"
},
{
- "name": "codeception/stub",
- "version": "1.0.4",
+ "name": "codeception/phpunit-wrapper",
+ "version": "6.0.16",
"source": {
"type": "git",
- "url": "https://github.com/Codeception/Stub.git",
- "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805"
+ "url": "https://github.com/Codeception/phpunit-wrapper.git",
+ "reference": "299e3aece31489ed962e6c39fe2fb6f3bbd2eb16"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805",
- "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805",
+ "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/299e3aece31489ed962e6c39fe2fb6f3bbd2eb16",
+ "reference": "299e3aece31489ed962e6c39fe2fb6f3bbd2eb16",
"shasum": ""
},
"require": {
- "phpunit/phpunit-mock-objects": ">2.3 <7.0"
+ "phpunit/php-code-coverage": ">=4.0.4 <6.0",
+ "phpunit/phpunit": ">=5.7.27 <6.5.13",
+ "sebastian/comparator": ">=1.2.4 <3.0",
+ "sebastian/diff": ">=1.4 <4.0"
+ },
+ "replace": {
+ "codeception/phpunit-wrapper": "*"
},
"require-dev": {
- "phpunit/phpunit": ">=4.8 <8.0"
+ "codeception/specify": "*",
+ "vlucas/phpdotenv": "^3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Codeception\\PHPUnit\\": "src\\"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Davert",
+ "email": "davert.php@resend.cc"
+ }
+ ],
+ "description": "PHPUnit classes used by Codeception",
+ "time": "2019-02-26T20:47:56+00:00"
+ },
+ {
+ "name": "codeception/stub",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Codeception/Stub.git",
+ "reference": "853657f988942f7afb69becf3fd0059f192c705a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Codeception/Stub/zipball/853657f988942f7afb69becf3fd0059f192c705a",
+ "reference": "853657f988942f7afb69becf3fd0059f192c705a",
+ "shasum": ""
+ },
+ "require": {
+ "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3"
},
"type": "library",
"autoload": {
@@ -4343,20 +4448,20 @@
"MIT"
],
"description": "Flexible Stub wrapper for PHPUnit's Mock Builder",
- "time": "2018-05-17T09:31:08+00:00"
+ "time": "2019-03-02T15:35:10+00:00"
},
{
"name": "composer/xdebug-handler",
- "version": "1.3.0",
+ "version": "1.3.2",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
- "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c"
+ "reference": "d17708133b6c276d6e42ef887a877866b909d892"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/b8e9745fb9b06ea6664d8872c4505fb16df4611c",
- "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/d17708133b6c276d6e42ef887a877866b909d892",
+ "reference": "d17708133b6c276d6e42ef887a877866b909d892",
"shasum": ""
},
"require": {
@@ -4387,38 +4492,82 @@
"Xdebug",
"performance"
],
- "time": "2018-08-31T19:07:57+00:00"
+ "time": "2019-01-28T20:25:53+00:00"
},
{
"name": "consolidation/annotated-command",
- "version": "2.9.1",
+ "version": "2.12.0",
"source": {
"type": "git",
"url": "https://github.com/consolidation/annotated-command.git",
- "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac"
+ "reference": "512a2e54c98f3af377589de76c43b24652bcb789"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac",
- "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac",
+ "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789",
+ "reference": "512a2e54c98f3af377589de76c43b24652bcb789",
"shasum": ""
},
"require": {
- "consolidation/output-formatters": "^3.1.12",
- "php": ">=5.4.0",
+ "consolidation/output-formatters": "^3.4",
+ "php": ">=5.4.5",
"psr/log": "^1",
"symfony/console": "^2.8|^3|^4",
"symfony/event-dispatcher": "^2.5|^3|^4",
"symfony/finder": "^2.5|^3|^4"
},
"require-dev": {
- "g1a/composer-test-scenarios": "^2",
+ "g1a/composer-test-scenarios": "^3",
+ "php-coveralls/php-coveralls": "^1",
"phpunit/phpunit": "^6",
- "satooshi/php-coveralls": "^2",
"squizlabs/php_codesniffer": "^2.7"
},
"type": "library",
"extra": {
+ "scenarios": {
+ "symfony4": {
+ "require": {
+ "symfony/console": "^4.0"
+ },
+ "config": {
+ "platform": {
+ "php": "7.1.3"
+ }
+ }
+ },
+ "symfony2": {
+ "require": {
+ "symfony/console": "^2.8"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "remove": [
+ "php-coveralls/php-coveralls"
+ ],
+ "config": {
+ "platform": {
+ "php": "5.4.8"
+ }
+ },
+ "scenario-options": {
+ "create-lockfile": "false"
+ }
+ },
+ "phpunit4": {
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "remove": [
+ "php-coveralls/php-coveralls"
+ ],
+ "config": {
+ "platform": {
+ "php": "5.4.8"
+ }
+ }
+ }
+ },
"branch-alias": {
"dev-master": "2.x-dev"
}
@@ -4439,20 +4588,20 @@
}
],
"description": "Initialize Symfony Console commands from annotated command class methods.",
- "time": "2018-09-19T17:47:18+00:00"
+ "time": "2019-03-08T16:55:03+00:00"
},
{
"name": "consolidation/config",
- "version": "1.1.0",
+ "version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/consolidation/config.git",
- "reference": "c9fc25e9088a708637e18a256321addc0670e578"
+ "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/consolidation/config/zipball/c9fc25e9088a708637e18a256321addc0670e578",
- "reference": "c9fc25e9088a708637e18a256321addc0670e578",
+ "url": "https://api.github.com/repos/consolidation/config/zipball/cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1",
+ "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1",
"shasum": ""
},
"require": {
@@ -4461,9 +4610,9 @@
"php": ">=5.4.0"
},
"require-dev": {
- "g1a/composer-test-scenarios": "^1",
+ "g1a/composer-test-scenarios": "^3",
+ "php-coveralls/php-coveralls": "^1",
"phpunit/phpunit": "^5",
- "satooshi/php-coveralls": "^1.0",
"squizlabs/php_codesniffer": "2.*",
"symfony/console": "^2.5|^3|^4",
"symfony/yaml": "^2.8.11|^3|^4"
@@ -4473,6 +4622,33 @@
},
"type": "library",
"extra": {
+ "scenarios": {
+ "symfony4": {
+ "require-dev": {
+ "symfony/console": "^4.0"
+ },
+ "config": {
+ "platform": {
+ "php": "7.1.3"
+ }
+ }
+ },
+ "symfony2": {
+ "require-dev": {
+ "symfony/console": "^2.8",
+ "symfony/event-dispatcher": "^2.8",
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "remove": [
+ "php-coveralls/php-coveralls"
+ ],
+ "config": {
+ "platform": {
+ "php": "5.4.8"
+ }
+ }
+ }
+ },
"branch-alias": {
"dev-master": "1.x-dev"
}
@@ -4493,35 +4669,76 @@
}
],
"description": "Provide configuration services for a commandline tool.",
- "time": "2018-08-07T22:57:00+00:00"
+ "time": "2019-03-03T19:37:04+00:00"
},
{
"name": "consolidation/log",
- "version": "1.0.6",
+ "version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/consolidation/log.git",
- "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395"
+ "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/consolidation/log/zipball/dfd8189a771fe047bf3cd669111b2de5f1c79395",
- "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395",
+ "url": "https://api.github.com/repos/consolidation/log/zipball/b2e887325ee90abc96b0a8b7b474cd9e7c896e3a",
+ "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a",
"shasum": ""
},
"require": {
- "php": ">=5.5.0",
- "psr/log": "~1.0",
+ "php": ">=5.4.5",
+ "psr/log": "^1.0",
"symfony/console": "^2.8|^3|^4"
},
"require-dev": {
- "g1a/composer-test-scenarios": "^1",
- "phpunit/phpunit": "4.*",
- "satooshi/php-coveralls": "^2",
- "squizlabs/php_codesniffer": "2.*"
+ "g1a/composer-test-scenarios": "^3",
+ "php-coveralls/php-coveralls": "^1",
+ "phpunit/phpunit": "^6",
+ "squizlabs/php_codesniffer": "^2"
},
"type": "library",
"extra": {
+ "scenarios": {
+ "symfony4": {
+ "require": {
+ "symfony/console": "^4.0"
+ },
+ "config": {
+ "platform": {
+ "php": "7.1.3"
+ }
+ }
+ },
+ "symfony2": {
+ "require": {
+ "symfony/console": "^2.8"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "remove": [
+ "php-coveralls/php-coveralls"
+ ],
+ "config": {
+ "platform": {
+ "php": "5.4.8"
+ }
+ }
+ },
+ "phpunit4": {
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "remove": [
+ "php-coveralls/php-coveralls"
+ ],
+ "config": {
+ "platform": {
+ "php": "5.4.8"
+ }
+ }
+ }
+ },
"branch-alias": {
"dev-master": "1.x-dev"
}
@@ -4542,33 +4759,33 @@
}
],
"description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.",
- "time": "2018-05-25T18:14:39+00:00"
+ "time": "2019-01-01T17:30:51+00:00"
},
{
"name": "consolidation/output-formatters",
- "version": "3.2.1",
+ "version": "3.4.1",
"source": {
"type": "git",
"url": "https://github.com/consolidation/output-formatters.git",
- "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5"
+ "reference": "0881112642ad9059071f13f397f571035b527cb9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5",
- "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5",
+ "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/0881112642ad9059071f13f397f571035b527cb9",
+ "reference": "0881112642ad9059071f13f397f571035b527cb9",
"shasum": ""
},
"require": {
+ "dflydev/dot-access-data": "^1.1.0",
"php": ">=5.4.0",
"symfony/console": "^2.8|^3|^4",
"symfony/finder": "^2.5|^3|^4"
},
"require-dev": {
- "g1a/composer-test-scenarios": "^2",
+ "g1a/composer-test-scenarios": "^3",
+ "php-coveralls/php-coveralls": "^1",
"phpunit/phpunit": "^5.7.27",
- "satooshi/php-coveralls": "^2",
"squizlabs/php_codesniffer": "^2.7",
- "symfony/console": "3.2.3",
"symfony/var-dumper": "^2.8|^3|^4",
"victorjonsson/markdowndocs": "^1.3"
},
@@ -4577,6 +4794,52 @@
},
"type": "library",
"extra": {
+ "scenarios": {
+ "symfony4": {
+ "require": {
+ "symfony/console": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6"
+ },
+ "config": {
+ "platform": {
+ "php": "7.1.3"
+ }
+ }
+ },
+ "symfony3": {
+ "require": {
+ "symfony/console": "^3.4",
+ "symfony/finder": "^3.4",
+ "symfony/var-dumper": "^3.4"
+ },
+ "config": {
+ "platform": {
+ "php": "5.6.32"
+ }
+ }
+ },
+ "symfony2": {
+ "require": {
+ "symfony/console": "^2.8"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "remove": [
+ "php-coveralls/php-coveralls"
+ ],
+ "config": {
+ "platform": {
+ "php": "5.4.8"
+ }
+ },
+ "scenario-options": {
+ "create-lockfile": "false"
+ }
+ }
+ },
"branch-alias": {
"dev-master": "3.x-dev"
}
@@ -4597,29 +4860,28 @@
}
],
"description": "Format text by applying transformations provided by plug-in formatters.",
- "time": "2018-05-25T18:02:34+00:00"
+ "time": "2019-03-14T03:45:44+00:00"
},
{
"name": "consolidation/robo",
- "version": "1.3.1",
+ "version": "1.4.9",
"source": {
"type": "git",
"url": "https://github.com/consolidation/Robo.git",
- "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d"
+ "reference": "5c6b3840a45afda1cbffbb3bb1f94dd5f9f83345"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/consolidation/Robo/zipball/31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d",
- "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d",
+ "url": "https://api.github.com/repos/consolidation/Robo/zipball/5c6b3840a45afda1cbffbb3bb1f94dd5f9f83345",
+ "reference": "5c6b3840a45afda1cbffbb3bb1f94dd5f9f83345",
"shasum": ""
},
"require": {
- "consolidation/annotated-command": "^2.8.2",
- "consolidation/config": "^1.0.10",
+ "consolidation/annotated-command": "^2.10.2",
+ "consolidation/config": "^1.2",
"consolidation/log": "~1",
"consolidation/output-formatters": "^3.1.13",
"consolidation/self-update": "^1",
- "g1a/composer-test-scenarios": "^2",
"grasmash/yaml-expander": "^1.3",
"league/container": "^2.2",
"php": ">=5.5.0",
@@ -4636,14 +4898,15 @@
"codeception/aspect-mock": "^1|^2.1.1",
"codeception/base": "^2.3.7",
"codeception/verify": "^0.3.2",
+ "g1a/composer-test-scenarios": "^3",
"goaop/framework": "~2.1.2",
"goaop/parser-reflection": "^1.1.0",
"natxet/cssmin": "3.0.4",
"nikic/php-parser": "^3.1.5",
"patchwork/jsqueeze": "~2",
- "pear/archive_tar": "^1.4.2",
+ "pear/archive_tar": "^1.4.4",
+ "php-coveralls/php-coveralls": "^1",
"phpunit/php-code-coverage": "~2|~4",
- "satooshi/php-coveralls": "^2",
"squizlabs/php_codesniffer": "^2.8"
},
"suggest": {
@@ -4657,9 +4920,36 @@
],
"type": "library",
"extra": {
+ "scenarios": {
+ "symfony4": {
+ "require": {
+ "symfony/console": "^4"
+ },
+ "config": {
+ "platform": {
+ "php": "7.1.3"
+ }
+ }
+ },
+ "symfony2": {
+ "require": {
+ "symfony/console": "^2.8"
+ },
+ "remove": [
+ "goaop/framework"
+ ],
+ "config": {
+ "platform": {
+ "php": "5.5.9"
+ }
+ },
+ "scenario-options": {
+ "create-lockfile": "false"
+ }
+ }
+ },
"branch-alias": {
- "dev-master": "1.x-dev",
- "dev-state": "1.x-dev"
+ "dev-master": "2.x-dev"
}
},
"autoload": {
@@ -4678,20 +4968,20 @@
}
],
"description": "Modern task runner",
- "time": "2018-08-17T18:44:18+00:00"
+ "time": "2019-03-19T18:07:19+00:00"
},
{
"name": "consolidation/self-update",
- "version": "1.1.3",
+ "version": "1.1.5",
"source": {
"type": "git",
"url": "https://github.com/consolidation/self-update.git",
- "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318"
+ "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/consolidation/self-update/zipball/de33822f907e0beb0ffad24cf4b1b4fae5ada318",
- "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318",
+ "url": "https://api.github.com/repos/consolidation/self-update/zipball/a1c273b14ce334789825a09d06d4c87c0a02ad54",
+ "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54",
"shasum": ""
},
"require": {
@@ -4728,7 +5018,7 @@
}
],
"description": "Provides a self:update command for Symfony Console applications.",
- "time": "2018-08-24T17:01:46+00:00"
+ "time": "2018-10-28T01:52:03+00:00"
},
{
"name": "dflydev/dot-access-data",
@@ -5317,39 +5607,6 @@
],
"time": "2018-07-12T10:23:15+00:00"
},
- {
- "name": "g1a/composer-test-scenarios",
- "version": "2.2.0",
- "source": {
- "type": "git",
- "url": "https://github.com/g1a/composer-test-scenarios.git",
- "reference": "a166fd15191aceab89f30c097e694b7cf3db4880"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/g1a/composer-test-scenarios/zipball/a166fd15191aceab89f30c097e694b7cf3db4880",
- "reference": "a166fd15191aceab89f30c097e694b7cf3db4880",
- "shasum": ""
- },
- "bin": [
- "scripts/create-scenario",
- "scripts/dependency-licenses",
- "scripts/install-scenario"
- ],
- "type": "library",
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Greg Anderson",
- "email": "greg.1.anderson@greenknowe.org"
- }
- ],
- "description": "Useful scripts for testing multiple sets of Composer dependencies.",
- "time": "2018-08-08T23:37:23+00:00"
- },
{
"name": "grasmash/expander",
"version": "1.0.0",
@@ -5563,32 +5820,33 @@
},
{
"name": "guzzlehttp/psr7",
- "version": "1.4.2",
+ "version": "1.5.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
+ "reference": "9f83dded91781a01c63574e387eaa769be769115"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
- "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
+ "reference": "9f83dded91781a01c63574e387eaa769be769115",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
- "psr/http-message": "~1.0"
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.0"
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "1.5-dev"
}
},
"autoload": {
@@ -5618,13 +5876,14 @@
"keywords": [
"http",
"message",
+ "psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
- "time": "2017-03-20T17:10:46+00:00"
+ "time": "2018-12-04T20:46:45+00:00"
},
{
"name": "ircmaxell/password-compat",
@@ -5670,16 +5929,16 @@
},
{
"name": "jms/metadata",
- "version": "1.6.0",
+ "version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/metadata.git",
- "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab"
+ "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/6a06970a10e0a532fb52d3959547123b84a3b3ab",
- "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab",
+ "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/e5854ab1aa643623dc64adde718a8eec32b957a8",
+ "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8",
"shasum": ""
},
"require": {
@@ -5702,9 +5961,13 @@
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "Apache-2.0"
+ "MIT"
],
"authors": [
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ },
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com"
@@ -5717,7 +5980,7 @@
"xml",
"yaml"
],
- "time": "2016-12-05T10:18:33+00:00"
+ "time": "2018-10-26T12:40:10+00:00"
},
{
"name": "jms/parser-lib",
@@ -7094,8 +7357,49 @@
"mock",
"xunit"
],
+ "abandoned": true,
"time": "2017-08-03T14:08:16+00:00"
},
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+ "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~3.7.0",
+ "satooshi/php-coveralls": ">=1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "time": "2016-02-11T07:05:27+00:00"
+ },
{
"name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.1",
@@ -7798,16 +8102,16 @@
},
{
"name": "symfony/browser-kit",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
- "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0"
+ "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f6668d1a6182d5a8dec65a1c863a4c1d963816c0",
- "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0",
+ "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c0fadd368c1031109e996316e53ffeb886d37ea1",
+ "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1",
"shasum": ""
},
"require": {
@@ -7851,20 +8155,20 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
- "time": "2018-07-26T09:06:28+00:00"
+ "time": "2019-02-23T15:06:07+00:00"
},
{
"name": "symfony/config",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
- "reference": "e5389132dc6320682de3643091121c048ff796b3"
+ "reference": "177a276c01575253c95cefe0866e3d1b57637fe0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/e5389132dc6320682de3643091121c048ff796b3",
- "reference": "e5389132dc6320682de3643091121c048ff796b3",
+ "url": "https://api.github.com/repos/symfony/config/zipball/177a276c01575253c95cefe0866e3d1b57637fe0",
+ "reference": "177a276c01575253c95cefe0866e3d1b57637fe0",
"shasum": ""
},
"require": {
@@ -7915,60 +8219,7 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
- "time": "2018-09-08T13:15:14+00:00"
- },
- {
- "name": "symfony/css-selector",
- "version": "v3.4.17",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/css-selector.git",
- "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/3503415d4aafabc31cd08c3a4ebac7f43fde8feb",
- "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb",
- "shasum": ""
- },
- "require": {
- "php": "^5.5.9|>=7.0.8"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.4-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\CssSelector\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Jean-François Simon",
- "email": "jeanfrancois.simon@sensiolabs.com"
- },
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony CssSelector Component",
- "homepage": "https://symfony.com",
- "time": "2018-10-02T16:33:53+00:00"
+ "time": "2019-02-23T15:06:07+00:00"
},
{
"name": "symfony/dependency-injection",
@@ -8042,16 +8293,16 @@
},
{
"name": "symfony/dom-crawler",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
- "reference": "c705bee03ade5b47c087807dd9ffaaec8dda2722"
+ "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c705bee03ade5b47c087807dd9ffaaec8dda2722",
- "reference": "c705bee03ade5b47c087807dd9ffaaec8dda2722",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d40023c057393fb25f7ca80af2a56ed948c45a09",
+ "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09",
"shasum": ""
},
"require": {
@@ -8095,20 +8346,20 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
- "time": "2018-10-02T12:28:39+00:00"
+ "time": "2019-02-23T15:06:07+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "3a4498236ade473c52b92d509303e5fd1b211ab1"
+ "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a4498236ade473c52b92d509303e5fd1b211ab1",
- "reference": "3a4498236ade473c52b92d509303e5fd1b211ab1",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a",
+ "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a",
"shasum": ""
},
"require": {
@@ -8149,20 +8400,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
- "time": "2018-10-03T08:48:18+00:00"
+ "time": "2019-02-23T15:06:07+00:00"
},
{
"name": "symfony/options-resolver",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
- "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d"
+ "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1cf7d8e704a9cc4164c92e430f2dfa3e6983661d",
- "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/926e3b797e6bb66c0e4d7da7eff3a174f7378bcf",
+ "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf",
"shasum": ""
},
"require": {
@@ -8203,20 +8454,20 @@
"configuration",
"options"
],
- "time": "2018-09-17T17:29:18+00:00"
+ "time": "2019-02-23T15:06:07+00:00"
},
{
"name": "symfony/polyfill-php54",
- "version": "v1.9.0",
+ "version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php54.git",
- "reference": "412977e090c6a8472dc39d50d1beb7d59495a965"
+ "reference": "2964b17ddc32dba7bcba009d5501c84d3fba1452"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/412977e090c6a8472dc39d50d1beb7d59495a965",
- "reference": "412977e090c6a8472dc39d50d1beb7d59495a965",
+ "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/2964b17ddc32dba7bcba009d5501c84d3fba1452",
+ "reference": "2964b17ddc32dba7bcba009d5501c84d3fba1452",
"shasum": ""
},
"require": {
@@ -8225,7 +8476,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9-dev"
+ "dev-master": "1.11-dev"
}
},
"autoload": {
@@ -8261,20 +8512,20 @@
"portable",
"shim"
],
- "time": "2018-08-06T14:22:27+00:00"
+ "time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/polyfill-php55",
- "version": "v1.9.0",
+ "version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php55.git",
- "reference": "578b8528da843de0fc65ec395900fa3181f2ead7"
+ "reference": "96fa25cef405ea452919559a0025d5dc16e30e4c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/578b8528da843de0fc65ec395900fa3181f2ead7",
- "reference": "578b8528da843de0fc65ec395900fa3181f2ead7",
+ "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/96fa25cef405ea452919559a0025d5dc16e30e4c",
+ "reference": "96fa25cef405ea452919559a0025d5dc16e30e4c",
"shasum": ""
},
"require": {
@@ -8284,7 +8535,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9-dev"
+ "dev-master": "1.11-dev"
}
},
"autoload": {
@@ -8317,20 +8568,20 @@
"portable",
"shim"
],
- "time": "2018-08-06T14:22:27+00:00"
+ "time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/polyfill-php70",
- "version": "v1.9.0",
+ "version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php70.git",
- "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934"
+ "reference": "bc4858fb611bda58719124ca079baff854149c89"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934",
- "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934",
+ "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/bc4858fb611bda58719124ca079baff854149c89",
+ "reference": "bc4858fb611bda58719124ca079baff854149c89",
"shasum": ""
},
"require": {
@@ -8340,7 +8591,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9-dev"
+ "dev-master": "1.11-dev"
}
},
"autoload": {
@@ -8376,20 +8627,20 @@
"portable",
"shim"
],
- "time": "2018-08-06T14:22:27+00:00"
+ "time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/polyfill-php72",
- "version": "v1.9.0",
+ "version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
- "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae"
+ "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/95c50420b0baed23852452a7f0c7b527303ed5ae",
- "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c",
+ "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c",
"shasum": ""
},
"require": {
@@ -8398,7 +8649,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.9-dev"
+ "dev-master": "1.11-dev"
}
},
"autoload": {
@@ -8431,20 +8682,20 @@
"portable",
"shim"
],
- "time": "2018-08-06T14:22:27+00:00"
+ "time": "2019-02-06T07:57:58+00:00"
},
{
"name": "symfony/stopwatch",
- "version": "v3.4.17",
+ "version": "v3.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
- "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4"
+ "reference": "2a651c2645c10bbedd21170771f122d935e0dd58"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/05e52a39de52ba690aebaed462b2bc8a9649f0a4",
- "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58",
+ "reference": "2a651c2645c10bbedd21170771f122d935e0dd58",
"shasum": ""
},
"require": {
@@ -8480,7 +8731,7 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com",
- "time": "2018-10-02T12:28:39+00:00"
+ "time": "2019-01-16T09:39:14+00:00"
},
{
"name": "symfony/yaml",
@@ -8619,20 +8870,21 @@
},
{
"name": "vlucas/phpdotenv",
- "version": "v2.5.1",
+ "version": "v2.6.1",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
- "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e"
+ "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e",
- "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
+ "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5",
"shasum": ""
},
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "^1.9"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.0"
@@ -8640,7 +8892,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-master": "2.6-dev"
}
},
"autoload": {
@@ -8665,24 +8917,25 @@
"env",
"environment"
],
- "time": "2018-07-29T20:33:41+00:00"
+ "time": "2019-01-29T11:11:52+00:00"
},
{
"name": "webmozart/assert",
- "version": "1.3.0",
+ "version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
- "reference": "0df1908962e7a3071564e857d86874dad1ef204a"
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
- "reference": "0df1908962e7a3071564e857d86874dad1ef204a",
+ "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0"
+ "php": "^5.3.3 || ^7.0",
+ "symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
@@ -8715,7 +8968,7 @@
"check",
"validate"
],
- "time": "2018-01-29T19:49:41+00:00"
+ "time": "2018-12-25T11:19:39+00:00"
}
],
"aliases": [],
diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
index 9682c4b2ee603..39a64c94067aa 100644
--- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
@@ -472,6 +472,10 @@ public function testUpdateDownloadableProductSamplesWithNewFile()
'title' => 'sample2_updated',
'sort_order' => 2,
'sample_type' => 'file',
+ 'sample_file_content' => [
+ 'name' => 'sample2.jpg',
+ 'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+ ],
];
$response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"] =
diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php
index 6dbf2b1aa6a12..9edd087020a72 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php
@@ -195,6 +195,13 @@ class ConditionsElement extends SimpleElement
*/
protected $exception;
+ /**
+ * Condition option text selector.
+ *
+ * @var string
+ */
+ private $conditionOptionTextSelector = '//option[normalize-space(text())="%s"]';
+
/**
* @inheritdoc
*/
@@ -282,10 +289,16 @@ protected function addCondition($type, ElementInterface $context)
$count = 0;
do {
- $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click();
-
try {
- $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select')->setValue($type);
+ $specificType = $newCondition->find(
+ sprintf($this->conditionOptionTextSelector, $type),
+ Locator::SELECTOR_XPATH
+ )->isPresent();
+ $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click();
+ $condition = $specificType
+ ? $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition')
+ : $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select');
+ $condition->setValue($type);
$isSetType = true;
} catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {
$isSetType = false;
diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php
new file mode 100644
index 0000000000000..15a799eac5188
--- /dev/null
+++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php
@@ -0,0 +1,20 @@
+ a';
+ protected $rowTemplate = 'td[div[*[contains(.,normalize-space("%s"))]]]';
/**
- * Secondary part of row locator template for getRow() method with strict option.
+ * Secondary part of row locator template for getRow() method with strict option
+ *
+ * @var string
+ */
+ protected $rowTemplateStrict = 'td[div[*[text()[normalize-space()="%s"]]]]';
+
+ /**
+ * Locator value for opening needed row.
*
* @var string
*/
- protected $rowTemplateStrict = '//*[text()[normalize-space()="%s"]]';
+ protected $editLink = '[href*="editStore"]';
/**
* Filters array mapping.
@@ -38,13 +45,13 @@ class StoreGrid extends Grid
*/
protected $filters = [
'store_title' => [
- 'selector' => '#storeGrid_filter_store_title',
+ 'selector' => '[name="store_title"]',
],
'group_title' => [
- 'selector' => '#storeGrid_filter_group_title',
+ 'selector' => '[name="group_title"]',
],
'website_title' => [
- 'selector' => '#storeGrid_filter_website_title',
+ 'selector' => '[name="name"]',
],
];
@@ -53,7 +60,7 @@ class StoreGrid extends Grid
*
* @var string
*/
- protected $titleFormat = '//td[a[.="%s"]]';
+ protected $titleFormat = '//a[.="%s"]';
/**
* Store name link selector.
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/StoreIndex.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/StoreIndex.xml
index 0491106412fe5..bc2b91fe65e6b 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/StoreIndex.xml
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/StoreIndex.xml
@@ -9,6 +9,6 @@
-
+
diff --git a/dev/tests/functional/tests/app/Magento/Captcha/Test/Constraint/AssertCaptchaFieldOnContactUsForm.php b/dev/tests/functional/tests/app/Magento/Captcha/Test/Constraint/AssertCaptchaFieldOnContactUsForm.php
index 6be21d2e71f9f..3653b0b2b5857 100644
--- a/dev/tests/functional/tests/app/Magento/Captcha/Test/Constraint/AssertCaptchaFieldOnContactUsForm.php
+++ b/dev/tests/functional/tests/app/Magento/Captcha/Test/Constraint/AssertCaptchaFieldOnContactUsForm.php
@@ -6,7 +6,7 @@
namespace Magento\Captcha\Test\Constraint;
-use Magento\Contact\Test\Page\ContactIndex;
+use Magento\Captcha\Test\Page\ContactIndexCaptcha as ContactIndex;
use Magento\Mtf\Constraint\AbstractConstraint;
/**
diff --git a/dev/tests/functional/tests/app/Magento/Captcha/Test/Page/ContactIndex.xml b/dev/tests/functional/tests/app/Magento/Captcha/Test/Page/ContactIndex.xml
index 060fc5f346fda..742eabb61f371 100644
--- a/dev/tests/functional/tests/app/Magento/Captcha/Test/Page/ContactIndex.xml
+++ b/dev/tests/functional/tests/app/Magento/Captcha/Test/Page/ContactIndex.xml
@@ -6,7 +6,7 @@
*/
-->
-
+
diff --git a/dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnContactUsTest.php b/dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnContactUsTest.php
index d8c9bf1f719de..0de71c3a416c8 100644
--- a/dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnContactUsTest.php
+++ b/dev/tests/functional/tests/app/Magento/Captcha/Test/TestCase/CaptchaOnContactUsTest.php
@@ -8,7 +8,7 @@
use Magento\Captcha\Test\Constraint\AssertCaptchaFieldOnContactUsForm;
use Magento\Contact\Test\Fixture\Comment;
-use Magento\Contact\Test\Page\ContactIndex;
+use Magento\Captcha\Test\Page\ContactIndexCaptcha as ContactIndex;
use Magento\Mtf\TestCase\Injectable;
use Magento\Mtf\TestStep\TestStepFactory;
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php
index ccb0d43337562..9f35059556fa8 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php
@@ -128,7 +128,8 @@ class CustomOptions extends Form
*
* @var string
*/
- private $validationErrorMessage = '//div[@class="mage-error"][contains(text(), "required field")]';
+ private $validationErrorMessage = '//div[@class="mage-error"][contains(text(), "required field")' .
+ ' and not(contains(@style,"display"))]';
/**
* Get product options
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml
index 3c88d9193db28..f41731287a5ec 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml
@@ -14,7 +14,6 @@
empty_UK_address_without_email
Flat Rate
Fixed
- Yes
- catalogProductSimple::default
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml
index 9e20bbdaac1d9..6716e4aacab81 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml
@@ -15,13 +15,11 @@
- 1
-
- - 1
-
- 565.00
Flat Rate
+ false
Fixed
checkmo
checkmo
@@ -43,6 +41,7 @@
- 565.00
Flat Rate
+ false
Fixed
checkmo
checkmo
@@ -61,7 +60,7 @@
UK_address_without_email
Flat Rate
Fixed
- Yes
+ false
US_address_1_without_email
checkmo
checkmo
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php
index aa7eba634145f..f13fb93e89b71 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php
@@ -126,11 +126,15 @@ public function run()
if ($this->billingCheckboxState) {
$this->assertBillingAddressCheckbox->processAssert($this->checkoutOnepage, $this->billingCheckboxState);
}
- if ($this->billingCheckboxState === 'Yes' && !$this->editBillingInformation) {
- return [
- 'billingAddress' => $this->shippingAddress
- ];
+
+ if (!$this->editBillingInformation) {
+ $billingAddress = $this->billingCheckboxState === 'Yes'
+ ? $this->shippingAddress
+ : $this->getDefaultBillingAddress();
+
+ return ['billingAddress' => $billingAddress];
}
+
if ($this->billingAddress) {
$selectedPaymentMethod = $this->checkoutOnepage->getPaymentBlock()->getSelectedPaymentMethodBlock();
if ($this->shippingAddress) {
@@ -156,4 +160,25 @@ public function run()
'billingAddress' => $billingAddress
];
}
+
+ /**
+ * Get default billing address
+ *
+ * @return Address|null
+ */
+ private function getDefaultBillingAddress()
+ {
+ $addresses = $this->customer->hasData('address')
+ ? $this->customer->getDataFieldConfig('address')['source']->getAddresses()
+ : [];
+ $defaultAddress = null;
+ foreach ($addresses as $address) {
+ if ($address->getDefaultBilling() === 'Yes') {
+ $defaultAddress = $address;
+ break;
+ }
+ }
+
+ return $defaultAddress;
+ }
}
diff --git a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml
index d304d305a7265..a266b09278ddb 100644
--- a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml
+++ b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml
@@ -7,6 +7,6 @@
-->
-
+
diff --git a/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php b/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php
index 66587879848a3..0d89a1d4eba6e 100644
--- a/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php
+++ b/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php
@@ -123,9 +123,9 @@ protected function prepareConfigPath(array $input)
*/
protected function applyConfigSettings(array $data, $section)
{
- $url = $this->getUrl($section);
$curl = new BackendDecorator(new CurlTransport(), $this->_configuration);
$curl->addOption(CURLOPT_HEADER, 1);
+ $url = $this->getUrl($section);
$curl->write($url, $data);
$response = $curl->read();
$curl->close();
diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Store/Curl.php b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Store/Curl.php
index 2fb05e59c379a..091cea329b78e 100644
--- a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Store/Curl.php
+++ b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Store/Curl.php
@@ -90,22 +90,29 @@ protected function prepareData(FixtureInterface $fixture)
*/
protected function getStoreId($name)
{
- //Set pager limit to 2000 in order to find created store view by name
- $url = $_ENV['app_backend_url'] . 'admin/system_store/index/sort/store_title/dir/asc/limit/2000';
+ $url = $_ENV['app_backend_url'] . 'mui/index/render/';
+ $data = [
+ 'namespace' => 'store_listing',
+ 'filters' => [
+ 'placeholder' => true,
+ 'store_title' => $name,
+ ],
+ 'paging' => [
+ 'pageSize' => 1,
+ ]
+ ];
$curl = new BackendDecorator(new CurlTransport(), $this->_configuration);
- $curl->addOption(CURLOPT_HEADER, 1);
- $curl->write($url, [], CurlInterface::GET);
+
+ $curl->write($url, $data, CurlInterface::POST);
$response = $curl->read();
+ $curl->close();
- $expectedUrl = '/admin/system_store/editStore/store_id/';
- $expectedUrl = preg_quote($expectedUrl);
- $expectedUrl = str_replace('/', '\/', $expectedUrl);
- preg_match('/' . $expectedUrl . '([0-9]*)\/(.)*>' . $name . '<\/a>/', $response, $matches);
+ preg_match('/store_listing_data_source.+items.+"store_id":"(\d+)"/', $response, $match);
- if (empty($matches)) {
+ if (empty($match)) {
throw new \Exception('Cannot find store id');
}
- return empty($matches[1]) ? null : $matches[1];
+ return intval($match[1]);
}
}
diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php
index d593165405102..511f0ed4f4a44 100644
--- a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php
+++ b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php
@@ -49,23 +49,30 @@ public function persist(FixtureInterface $fixture = null)
*/
protected function getStoreGroupIdByGroupName($storeName)
{
- //Set pager limit to 2000 in order to find created store group by name
- $url = $_ENV['app_backend_url'] . 'admin/system_store/index/sort/group_title/dir/asc/limit/2000';
+ $url = $_ENV['app_backend_url'] . 'mui/index/render/';
+ $data = [
+ 'namespace' => 'store_listing',
+ 'filters' => [
+ 'placeholder' => true,
+ 'group_title' => $storeName,
+ ],
+ 'paging' => [
+ 'pageSize' => 1,
+ ]
+ ];
$curl = new BackendDecorator(new CurlTransport(), $this->_configuration);
- $curl->addOption(CURLOPT_HEADER, 1);
- $curl->write($url, [], CurlInterface::GET);
+
+ $curl->write($url, $data, CurlInterface::POST);
$response = $curl->read();
+ $curl->close();
- $expectedUrl = '/admin/system_store/editGroup/group_id/';
- $expectedUrl = preg_quote($expectedUrl);
- $expectedUrl = str_replace('/', '\/', $expectedUrl);
- preg_match('/' . $expectedUrl . '([0-9]*)\/(.)*>' . $storeName . '<\/a>/', $response, $matches);
+ preg_match('/store_listing_data_source.+items.+"group_id":"(\d+)"/', $response, $match);
- if (empty($matches)) {
+ if (empty($match)) {
throw new \Exception('Cannot find store group id');
}
- return (int)$matches[1];
+ return (int)$match[1];
}
/**
diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php
index ecc59ebb55ca5..0e83e36c0f109 100644
--- a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php
+++ b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php
@@ -103,23 +103,30 @@ public function persist(FixtureInterface $fixture = null)
*/
protected function getWebSiteIdByWebsiteName($websiteName)
{
- // Set pager limit to 2000 in order to find created website by name
- $url = $_ENV['app_backend_url'] . 'admin/system_store/index/sort/group_title/dir/asc/limit/2000';
+ $url = $_ENV['app_backend_url'] . 'mui/index/render/';
+ $data = [
+ 'namespace' => 'store_listing',
+ 'filters' => [
+ 'placeholder' => true,
+ 'name' => $websiteName,
+ ],
+ 'paging' => [
+ 'pageSize' => 1,
+ ]
+ ];
$curl = new BackendDecorator(new CurlTransport(), $this->_configuration);
- $curl->addOption(CURLOPT_HEADER, 1);
- $curl->write($url, [], CurlInterface::GET);
+
+ $curl->write($url, $data, CurlInterface::POST);
$response = $curl->read();
+ $curl->close();
- $expectedUrl = '/admin/system_store/editWebsite/website_id/';
- $expectedUrl = preg_quote($expectedUrl);
- $expectedUrl = str_replace('/', '\/', $expectedUrl);
- preg_match('/' . $expectedUrl . '([0-9]*)\/(.)*>' . $websiteName . '<\/a>/', $response, $matches);
+ preg_match('/store_listing_data_source.+items.+"website_id":"(\d+)"/', $response, $match);
- if (empty($matches)) {
+ if (empty($match)) {
throw new \Exception('Cannot find website id.');
}
- return (int)$matches[1];
+ return (int)$match[1];
}
/**
diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php
index 6839d0ae9a7ff..fe2c1c7d01740 100644
--- a/dev/tests/integration/etc/di/preferences/ce.php
+++ b/dev/tests/integration/etc/di/preferences/ce.php
@@ -29,4 +29,5 @@
\Magento\TestFramework\Lock\Backend\DummyLocker::class,
\Magento\Framework\ShellInterface::class => \Magento\TestFramework\App\Shell::class,
\Magento\Framework\App\Shell::class => \Magento\TestFramework\App\Shell::class,
+ \Magento\Framework\Session\SessionStartChecker::class => \Magento\TestFramework\Session\SessionStartChecker::class,
];
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php
index 3132aed4d21e3..ae59bc004db0e 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php
@@ -51,7 +51,7 @@ public function startTestSuite()
{
if (null === $this->reader) {
$this->reader = Bootstrap::getObjectManager()->get(\Magento\Framework\App\DeploymentConfig\Reader::class);
- $this->config = $this->reader->load();
+ $this->config = $this->filterIgnoredConfigValues($this->reader->load());
}
}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Session/SessionStartChecker.php b/dev/tests/integration/framework/Magento/TestFramework/Session/SessionStartChecker.php
new file mode 100644
index 0000000000000..136b0565a729a
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Session/SessionStartChecker.php
@@ -0,0 +1,25 @@
+_auth->getAuthStorage()->destroy(['send_expire_cookie' => false]);
@@ -86,21 +101,33 @@ public function assertSessionMessages(
parent::assertSessionMessages($constraint, $messageType, $messageManagerClass);
}
+ /**
+ * Test ACL configuration for action working.
+ */
public function testAclHasAccess()
{
if ($this->uri === null) {
$this->markTestIncomplete('AclHasAccess test is not complete');
}
+ if ($this->httpMethod) {
+ $this->getRequest()->setMethod($this->httpMethod);
+ }
$this->dispatch($this->uri);
$this->assertNotSame(403, $this->getResponse()->getHttpResponseCode());
$this->assertNotSame(404, $this->getResponse()->getHttpResponseCode());
}
+ /**
+ * Test ACL actually denying access.
+ */
public function testAclNoAccess()
{
if ($this->resource === null || $this->uri === null) {
$this->markTestIncomplete('Acl test is not complete');
}
+ if ($this->httpMethod) {
+ $this->getRequest()->setMethod($this->httpMethod);
+ }
$this->_objectManager->get(\Magento\Framework\Acl\Builder::class)
->getAcl()
->deny(null, $this->resource);
diff --git a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php
index 9920f90193f69..feb9eca0793a2 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php
@@ -9,9 +9,13 @@
*/
namespace Magento\TestFramework\TestCase;
+use Magento\Framework\Data\Form\FormKey;
+use Magento\Framework\Message\MessageInterface;
use Magento\Framework\Stdlib\CookieManagerInterface;
use Magento\Framework\View\Element\Message\InterpretationStrategyInterface;
use Magento\Theme\Controller\Result\MessagePlugin;
+use Magento\Framework\App\Request\Http as HttpRequest;
+use Magento\Framework\App\Response\Http as HttpResponse;
/**
* @SuppressWarnings(PHPMD.NumberOfChildren)
@@ -68,6 +72,9 @@ protected function setUp()
$this->_objectManager->removeSharedInstance(\Magento\Framework\App\RequestInterface::class);
}
+ /**
+ * @inheritDoc
+ */
protected function tearDown()
{
$this->_request = null;
@@ -96,14 +103,23 @@ protected function assertPostConditions()
*/
public function dispatch($uri)
{
- $this->getRequest()->setRequestUri($uri);
+ /** @var HttpRequest $request */
+ $request = $this->getRequest();
+ $request->setRequestUri($uri);
+ if ($request->isPost()
+ && !array_key_exists('form_key', $request->getPost())
+ ) {
+ /** @var FormKey $formKey */
+ $formKey = $this->_objectManager->get(FormKey::class);
+ $request->setPostValue('form_key', $formKey->getFormKey());
+ }
$this->_getBootstrap()->runApp();
}
/**
* Request getter
*
- * @return \Magento\Framework\App\RequestInterface
+ * @return \Magento\Framework\App\RequestInterface|HttpRequest
*/
public function getRequest()
{
@@ -116,7 +132,7 @@ public function getRequest()
/**
* Response getter
*
- * @return \Magento\Framework\App\ResponseInterface
+ * @return \Magento\Framework\App\ResponseInterface|HttpResponse
*/
public function getResponse()
{
@@ -201,13 +217,21 @@ public function assertSessionMessages(
$messageManagerClass = \Magento\Framework\Message\Manager::class
) {
$this->_assertSessionErrors = false;
-
+ /** @var MessageInterface[]|string[] $messageObjects */
$messages = $this->getMessages($messageType, $messageManagerClass);
+ /** @var string[] $messages */
+ $messagesFiltered = array_map(
+ function ($message) {
+ /** @var MessageInterface|string $message */
+ return ($message instanceof MessageInterface) ? $message->toString() : $message;
+ },
+ $messages
+ );
$this->assertThat(
- $messages,
+ $messagesFiltered,
$constraint,
- 'Session messages do not meet expectations ' . var_export($messages, true)
+ 'Session messages do not meet expectations ' . var_export($messagesFiltered, true)
);
}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php
index 17863cd709580..3c8930fb78097 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php
@@ -15,6 +15,9 @@ class GraphTest extends \PHPUnit\Framework\TestCase
*/
protected $_block;
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
parent::setUp();
@@ -25,8 +28,13 @@ protected function setUp()
$this->_block->setDataHelper($objectManager->get(\Magento\Backend\Helper\Dashboard\Order::class));
}
+ /**
+ * Tests getChartUrl.
+ *
+ * @return void
+ */
public function testGetChartUrl()
{
- $this->assertStringStartsWith('http://chart.apis.google.com/chart', $this->_block->getChartUrl());
+ $this->assertStringStartsWith('https://image-charts.com/chart', $this->_block->getChartUrl());
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php
index 8aeee9cf12494..e11c5ce5d9cf3 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php
@@ -87,41 +87,6 @@ public function testMassactionDefaultValues()
$this->assertFalse($blockEmpty->isAvailable());
}
- public function testGetJavaScript()
- {
- $this->loadLayout();
-
- $javascript = $this->_block->getJavaScript();
-
- $expectedItemFirst = '#"option_id1":{"label":"Option One",' .
- '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' .
- '"complete":"Test","id":"option_id1"}#';
- $this->assertRegExp($expectedItemFirst, $javascript);
-
- $expectedItemSecond = '#"option_id2":{"label":"Option Two",' .
- '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' .
- '"confirm":"Are you sure\?","id":"option_id2"}#';
- $this->assertRegExp($expectedItemSecond, $javascript);
- }
-
- public function testGetJavaScriptWithAddedItem()
- {
- $this->loadLayout();
-
- $input = [
- 'id' => 'option_id3',
- 'label' => 'Option Three',
- 'url' => '*/*/option3',
- 'block_name' => 'admin.test.grid.massaction.option3',
- ];
- $expected = '#"option_id3":{"id":"option_id3","label":"Option Three",' .
- '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' .
- '"block_name":"admin.test.grid.massaction.option3"}#';
-
- $this->_block->addItem($input['id'], $input);
- $this->assertRegExp($expected, $this->_block->getJavaScript());
- }
-
/**
* @param string $mageMode
* @param int $expectedCount
@@ -213,21 +178,4 @@ public function getItemsDataProvider()
]
];
}
-
- public function testGridContainsMassactionColumn()
- {
- $this->loadLayout();
- $this->_layout->getBlock('admin.test.grid')->toHtml();
-
- $gridMassactionColumn = $this->_layout->getBlock('admin.test.grid')
- ->getColumnSet()
- ->getChildBlock('massaction');
-
- $this->assertNotNull($gridMassactionColumn, 'Massaction column does not exist in the grid column set');
- $this->assertInstanceOf(
- \Magento\Backend\Block\Widget\Grid\Column::class,
- $gridMassactionColumn,
- 'Massaction column is not an instance of \Magento\Backend\Block\Widget\Column'
- );
- }
}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
index 07af21505f180..4373523350c49 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
@@ -19,8 +19,15 @@ public function testAjaxBlockAction()
$this->assertContains('dashboard-diagram', $actual);
}
+ /**
+ * Tests tunnelAction.
+ *
+ * @return void
+ * @throws \Exception
+ */
public function testTunnelAction()
{
+ // phpcs:disable Magento2.Functions.DiscouragedFunction
$testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World';
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $testUrl);
@@ -34,6 +41,7 @@ public function testTunnelAction()
curl_close($handle);
throw $e;
}
+ // phpcs:enable
$gaData = [
'cht' => 'lc',
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php
index 219fde6e37075..d5a48b960811e 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Backend\Controller\Adminhtml;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
/**
* @magentoAppArea adminhtml
* @magentoDbIsolation enabled
@@ -45,6 +47,7 @@ public function testLoggedIndexAction()
public function testGlobalSearchAction()
{
$this->getRequest()->setParam('isAjax', 'true');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue('query', 'dummy');
$this->dispatch('backend/admin/index/globalSearch');
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php
index 1185ae9727e98..0d48fc8b0f59c 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Backend\Controller\Adminhtml;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
/**
* @magentoAppArea adminhtml
*/
@@ -20,6 +22,7 @@ public function testSaveActionCmsPage()
$page = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Cms\Model\Page::class);
$page->load('page_design_blank', 'identifier');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'description' => 'Some URL rewrite description',
diff --git a/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php
new file mode 100644
index 0000000000000..4bce8d95dafa6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php
@@ -0,0 +1,16 @@
+get(ConfigInterface::class);
+$config->saveConfig('general/country/allow', 'FR');
+$objectManager->get(ReinitableConfigInterface::class)->reinit();
diff --git a/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php
new file mode 100644
index 0000000000000..711d985786329
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php
@@ -0,0 +1,16 @@
+get(ConfigInterface::class);
+$config->deleteConfig('general/country/allow');
+$objectManager->get(ReinitableConfigInterface::class)->reinit();
diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php
new file mode 100644
index 0000000000000..91cea7dc96602
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php
@@ -0,0 +1,40 @@
+getStore();
+$quote->setReservedOrderId('multishipping_quote_id_braintree')
+ ->setStoreId($store->getId())
+ ->setCustomerEmail('customer001@test.com');
+
+/** @var CartRepositoryInterface $quoteRepository */
+$quoteRepository = $objectManager->get(CartRepositoryInterface::class);
+$quote->collectTotals();
+$quoteRepository->save($quote);
+
+$items = $quote->getAllItems();
+$addressList = $quote->getAllShippingAddresses();
+
+foreach ($addressList as $key => $address) {
+ $item = $items[$key];
+ // set correct quantity per shipping address
+ $item->setQty(1);
+ $address->setTotalQty(1);
+ $address->addItem($item);
+}
+
+// assign virtual product to the billing address
+$billingAddress = $quote->getBillingAddress();
+$virtualItem = $items[sizeof($items) - 1];
+$billingAddress->setTotalQty(1);
+$billingAddress->addItem($virtualItem);
+
+// need to recollect totals
+$quote->setTotalsCollectedFlag(false);
+$quote->collectTotals();
+$quoteRepository->save($quote);
diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php
new file mode 100644
index 0000000000000..3e1db90f1f2c8
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php
@@ -0,0 +1,28 @@
+create(Payment::class);
+$payment->setMethod(ConfigProvider::CODE);
+$quote->setPayment($payment);
diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php
new file mode 100644
index 0000000000000..e4bba222078b0
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php
@@ -0,0 +1,28 @@
+create(Payment::class);
+$payment->setMethod(ConfigProvider::PAYPAL_CODE);
+$quote->setPayment($payment);
diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php
new file mode 100644
index 0000000000000..1c56e611dd6db
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php
@@ -0,0 +1,26 @@
+get(StoreManagerInterface::class);
+
+/** @var Quote $quote */
+$quote = $objectManager->create(Quote::class);
+
+require __DIR__ . '/../../../Magento/Multishipping/Fixtures/shipping_address_list.php';
+require __DIR__ . '/../../../Magento/Multishipping/Fixtures/billing_address.php';
+require __DIR__ . '/payment_braintree.php';
+require __DIR__ . '/../../../Magento/Multishipping/Fixtures/items.php';
+require __DIR__ . '/assign_items_per_address.php';
diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php
new file mode 100644
index 0000000000000..4bd8e926abb76
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php
@@ -0,0 +1,26 @@
+get(StoreManagerInterface::class);
+
+/** @var Quote $quote */
+$quote = $objectManager->create(Quote::class);
+
+require __DIR__ . '/../../../Magento/Multishipping/Fixtures/shipping_address_list.php';
+require __DIR__ . '/../../../Magento/Multishipping/Fixtures/billing_address.php';
+require __DIR__ . '/payment_braintree_paypal.php';
+require __DIR__ . '/../../../Magento/Multishipping/Fixtures/items.php';
+require __DIR__ . '/assign_items_per_address.php';
diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php
new file mode 100644
index 0000000000000..91bc0388d8551
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php
@@ -0,0 +1,254 @@
+objectManager = Bootstrap::getObjectManager();
+
+ $orderSender = $this->getMockBuilder(OrderSender::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $adapterFactory = $this->getMockBuilder(BraintreeAdapterFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->adapter = $this->getMockBuilder(BraintreeAdapter::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $adapterFactory->method('create')
+ ->willReturn($this->adapter);
+
+ $this->objectManager->addSharedInstance($adapterFactory, BraintreeAdapterFactory::class);
+ $this->objectManager->addSharedInstance($this->getPaymentNonceMock(), GetPaymentNonceCommand::class);
+
+ $this->model = $this->objectManager->create(
+ Multishipping::class,
+ ['orderSender' => $orderSender]
+ );
+ }
+
+ /**
+ * Checks a case when multiple orders are created successfully using Braintree payment method.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Braintree/Fixtures/quote_with_split_items_braintree.php
+ * @magentoConfigFixture current_store payment/braintree/active 1
+ * @return void
+ */
+ public function testCreateOrdersWithBraintree()
+ {
+ $this->adapter->method('sale')
+ ->willReturn(
+ $this->getTransactionStub()
+ );
+ $this->createOrders();
+ }
+
+ /**
+ * Checks a case when multiple orders are created successfully using Braintree PayPal payment method.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php
+ * @magentoConfigFixture current_store payment/braintree_paypal/active 1
+ * @return void
+ */
+ public function testCreateOrdersWithBraintreePaypal()
+ {
+ $this->adapter->method('sale')
+ ->willReturn(
+ $this->getTransactionPaypalStub()
+ );
+ $this->createOrders();
+ }
+
+ /**
+ * Creates orders for multishipping checkout flow.
+ *
+ * @return void
+ */
+ private function createOrders()
+ {
+ $expectedPlacedOrdersNumber = 3;
+ $quote = $this->getQuote('multishipping_quote_id_braintree');
+
+ /** @var CheckoutSession $session */
+ $session = $this->objectManager->get(CheckoutSession::class);
+ $session->replaceQuote($quote);
+
+ $this->model->createOrders();
+
+ $orderList = $this->getOrderList((int)$quote->getId());
+ self::assertCount(
+ $expectedPlacedOrdersNumber,
+ $orderList,
+ 'Total successfully placed orders number mismatch'
+ );
+ }
+
+ /**
+ * Creates stub for Braintree capture Transaction.
+ *
+ * @return Successful
+ */
+ private function getTransactionStub(): Successful
+ {
+ $transaction = $this->getMockBuilder(Transaction::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $transaction->status = 'submitted_for_settlement';
+ $transaction->creditCard = [
+ 'last4' => '1111',
+ 'cardType' => 'Visa',
+ 'expirationMonth' => '12',
+ 'expirationYear' => '2021'
+ ];
+
+ $creditCardDetails = new \stdClass();
+ $creditCardDetails->token = '4fdg';
+ $creditCardDetails->expirationMonth = '12';
+ $creditCardDetails->expirationYear = '2021';
+ $creditCardDetails->cardType = 'Visa';
+ $creditCardDetails->last4 = '1111';
+ $creditCardDetails->expirationDate = '12/2021';
+ $transaction->creditCardDetails = $creditCardDetails;
+
+ $response = new Successful();
+ $response->success = true;
+ $response->transaction = $transaction;
+
+ return $response;
+ }
+
+ /**
+ * Creates stub for BraintreePaypal capture Transaction.
+ *
+ * @return Successful
+ */
+ private function getTransactionPaypalStub(): Successful
+ {
+ $transaction = $this->getMockBuilder(Transaction::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $transaction->status = 'submitted_for_settlement';
+ $transaction->paypal = [
+ 'token' => 'fchxqx',
+ 'payerEmail' => 'payer@example.com',
+ 'paymentId' => 'PAY-33ac47a28e7f54791f6cda45',
+ ];
+ $paypalDetails = new \stdClass();
+ $paypalDetails->token = 'fchxqx';
+ $paypalDetails->payerEmail = 'payer@example.com';
+ $paypalDetails->paymentId = '33ac47a28e7f54791f6cda45';
+ $transaction->paypalDetails = $paypalDetails;
+
+ $response = new Successful();
+ $response->success = true;
+ $response->transaction = $transaction;
+
+ return $response;
+ }
+
+ /**
+ * Retrieves quote by reserved order id.
+ *
+ * @param string $reservedOrderId
+ * @return Quote
+ */
+ private function getQuote(string $reservedOrderId): Quote
+ {
+ /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+ $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
+ $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
+ ->create();
+
+ /** @var CartRepositoryInterface $quoteRepository */
+ $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
+ $items = $quoteRepository->getList($searchCriteria)->getItems();
+
+ return array_pop($items);
+ }
+
+ /**
+ * Get list of orders by quote id.
+ *
+ * @param int $quoteId
+ * @return array
+ */
+ private function getOrderList(int $quoteId): array
+ {
+ /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+ $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
+ $searchCriteria = $searchCriteriaBuilder->addFilter('quote_id', $quoteId)
+ ->create();
+
+ /** @var OrderRepositoryInterface $orderRepository */
+ $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class);
+ return $orderRepository->getList($searchCriteria)->getItems();
+ }
+
+ /**
+ * Returns GetPaymentNonceCommand command mock.
+ *
+ * @return MockObject
+ */
+ private function getPaymentNonceMock(): MockObject
+ {
+ $commandResult = $this->createMock(CommandResultInterface::class);
+ $commandResult->method('get')
+ ->willReturn(['paymentMethodNonce' => 'testNonce']);
+ $paymentNonce = $this->createMock(GetPaymentNonceCommand::class);
+ $paymentNonce->method('execute')
+ ->willReturn($commandResult);
+
+ return $paymentNonce;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php
index 6794a686146f9..c1750332fa568 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php
@@ -8,7 +8,7 @@
/**
* @magentoDbIsolation disabled
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @group indexer_dimension
* @magentoAppArea frontend
*/
@@ -24,6 +24,9 @@ class DynamicBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract
*/
public function testPriceForDynamicBundle(array $strategyModifiers, array $expectedResults)
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->prepareFixture($strategyModifiers, 'bundle_product');
$bundleProduct = $this->productRepository->get('bundle_product', false, null, true);
@@ -63,6 +66,9 @@ public function testPriceForDynamicBundle(array $strategyModifiers, array $expec
*/
public function testPriceForDynamicBundleInWebsiteScope(array $strategyModifiers, array $expectedResults)
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->prepareFixture($strategyModifiers, 'bundle_product');
$bundleProduct = $this->productRepository->get('bundle_product', false, null, true);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php
index ffc24b2f45d5c..9e7c8d9836c74 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php
@@ -10,7 +10,7 @@
/**
* @magentoDbIsolation disabled
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @group indexer_dimension
* @magentoAppArea frontend
*/
@@ -26,6 +26,9 @@ class FixedBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract
*/
public function testPriceForFixedBundle(array $strategyModifiers, array $expectedResults)
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->prepareFixture($strategyModifiers, 'bundle_product');
$bundleProduct = $this->productRepository->get('bundle_product', false, null, true);
@@ -65,6 +68,9 @@ public function testPriceForFixedBundle(array $strategyModifiers, array $expecte
*/
public function testPriceForFixedBundleInWebsiteScope(array $strategyModifiers, array $expectedResults)
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->prepareFixture($strategyModifiers, 'bundle_product');
$bundleProduct = $this->productRepository->get('bundle_product', false, null, true);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php
index 2a68ff48e5f9a..4a5757aae3134 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php
@@ -6,7 +6,7 @@
namespace Magento\Bundle\Model\Product;
/**
- * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php
+ * Class to test bundle prices
*/
class PriceTest extends \PHPUnit\Framework\TestCase
{
@@ -22,6 +22,9 @@ protected function setUp()
);
}
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php
+ */
public function testGetTierPrice()
{
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
@@ -37,4 +40,50 @@ public function testGetTierPrice()
$this->assertEquals(20.0, $this->_model->getTierPrice(4, $product));
$this->assertEquals(30.0, $this->_model->getTierPrice(5, $product));
}
+
+ /**
+ * Test calculation final price for bundle product with tire price in simple product
+ *
+ * @param float $bundleQty
+ * @param float $selectionQty
+ * @param float $finalPrice
+ * @magentoDataFixture Magento/Bundle/_files/product_with_simple_tier_pricing.php
+ * @dataProvider getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider
+ */
+ public function testGetSelectionFinalTotalPriceWithSimpleTierPrice(
+ float $bundleQty,
+ float $selectionQty,
+ float $finalPrice
+ ) {
+ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+ $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+ $bundleProduct = $productRepository->get('bundle-product');
+ $simpleProduct = $productRepository->get('simple');
+ $simpleProduct->setCustomerGroupId(\Magento\Customer\Model\Group::CUST_GROUP_ALL);
+
+ $this->assertEquals(
+ $finalPrice,
+ $this->_model->getSelectionFinalTotalPrice(
+ $bundleProduct,
+ $simpleProduct,
+ $bundleQty,
+ $selectionQty,
+ false
+ ),
+ 'Tier price calculation for Simple product is wrong'
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider(): array
+ {
+ return [
+ [1, 1, 10],
+ [2, 1, 8],
+ [5, 1, 5],
+ ];
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php
index 3b3b1ed5cbd07..a1516c6a74808 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php
@@ -7,7 +7,7 @@
/**
* @magentoDbIsolation disabled
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @group indexer_dimension
* @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php
*/
@@ -27,6 +27,9 @@ protected function setUp()
public function testGetTierPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing.php
new file mode 100644
index 0000000000000..30f0978480701
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing.php
@@ -0,0 +1,43 @@
+create(\Magento\Catalog\Model\ProductFactory::class);
+/** @var $bundleProduct \Magento\Catalog\Model\Product */
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId('bundle')
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([1])
+ ->setPriceType(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC)
+ ->setPriceView(1)
+ ->setName('Bundle Product')
+ ->setSku('bundle-product')
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setStockData([
+ 'use_config_manage_stock' => 1,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1,
+ ])
+ ->setBundleOptionsData(
+ [
+ [
+ 'title' => 'Bundle Product Items',
+ 'default_title' => 'Bundle Product Items',
+ 'type' => 'checkbox',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+ ]
+ )
+ ->setBundleSelectionsData(
+ [[['product_id' => $product->getId(), 'selection_qty' => 1, 'delete' => '']]]
+ );
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing_rollback.php
new file mode 100644
index 0000000000000..aa661c7412d42
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing_rollback.php
@@ -0,0 +1,24 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+try {
+ $product = $productRepository->get('bundle-product');
+ $productRepository->delete($product);
+} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php b/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php
index 32fab1f456b3e..4c469421402c2 100644
--- a/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php
@@ -7,9 +7,14 @@
use Magento\Framework\Message\MessageInterface;
+/**
+ * @magentoAppArea adminhtml
+ */
class CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest extends \Magento\TestFramework\TestCase\AbstractController
{
/**
+ * Tests backend login action with invalid captcha.
+ *
* @magentoAdminConfigFixture admin/captcha/forms backend_login
* @magentoAdminConfigFixture admin/captcha/enable 1
* @magentoAdminConfigFixture admin/captcha/mode always
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php
index c97dc821913f9..eb08509a9e36d 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php
@@ -12,6 +12,7 @@
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\View\LayoutInterface;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Catalog\Block\Product\View\GalleryOptions;
class GalleryTest extends \PHPUnit\Framework\TestCase
{
@@ -41,10 +42,12 @@ public function testHtml()
/** @var Gallery $block */
$block = $layout->createBlock(Gallery::class);
$block->setData('product', $product);
+ $galleryoptions = $this->objectManager->get(GalleryOptions::class);
+ $block->setData('gallery_options', $galleryoptions);
$block->setTemplate("Magento_Catalog::product/view/gallery.phtml");
$showCaption = $block->getVar('gallery/caption');
- self::assertContains('"showCaption": ' . $showCaption, $block->toHtml());
+ self::assertContains('"showCaption":' . $showCaption, $block->toHtml());
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php
index a6cffda80e705..bdf2486ac2e38 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php
@@ -8,6 +8,7 @@
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Store\Model\Store;
use Magento\Catalog\Model\ResourceModel\Product;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoAppArea adminhtml
@@ -51,6 +52,7 @@ public function testSaveAction($inputData, $defaultAttributes, $attributesSaved
$store->load('fixturestore', 'code');
$storeId = $store->getId();
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($inputData);
$this->getRequest()->setParam('store', $storeId);
$this->getRequest()->setParam('id', 2);
@@ -99,6 +101,7 @@ public function testSaveAction($inputData, $defaultAttributes, $attributesSaved
*/
public function testSaveActionFromProductCreationPage($postData)
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/category/save');
@@ -357,6 +360,7 @@ public function saveActionDataProvider()
*/
public function testSaveActionCategoryWithDangerRequest()
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'general' => [
@@ -407,7 +411,8 @@ public function testMoveAction($parentId, $childId, $childUrlKey, $grandChildId,
}
$this->getRequest()
->setPostValue('id', $grandChildId)
- ->setPostValue('pid', $parentId);
+ ->setPostValue('pid', $parentId)
+ ->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/category/move');
$jsonResponse = json_decode($this->getResponse()->getBody());
$this->assertNotNull($jsonResponse);
@@ -444,6 +449,7 @@ public function testSaveCategoryWithProductPosition(array $postData)
$this->getRequest()->setParam('store', $storeId);
$this->getRequest()->setParam('id', 96377);
$this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/category/save');
$newCategoryProductsCount = $this->getCategoryProductsCount();
$this->assertEquals(
@@ -543,10 +549,8 @@ private function getCategoryProductsCount(): int
{
$oldCategoryProducts = $this->productResource->getConnection()->select()->from(
$this->productResource->getTable('catalog_category_product'),
- 'product_id'
- );
- return count(
- $this->productResource->getConnection()->fetchAll($oldCategoryProducts)
+ new \Zend_Db_Expr('COUNT(product_id)')
);
+ return $this->productResource->getConnection()->fetchOne($oldCategoryProducts);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php
index cea49d940cb62..3d7575729cd92 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product\Action;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
/**
* @magentoAppArea adminhtml
*/
@@ -23,6 +25,7 @@ public function testSaveActionRedirectsSuccessfully()
/** @var $session \Magento\Backend\Model\Session */
$session = $objectManager->get(\Magento\Backend\Model\Session::class);
$session->setProductIds([1]);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product_action_attribute/save/store/0');
@@ -69,6 +72,7 @@ public function testSaveActionChangeVisibility($attributes)
$session = $objectManager->get(\Magento\Backend\Model\Session::class);
$session->setProductIds([$product->getId()]);
$this->getRequest()->setParam('attributes', $attributes);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product_action_attribute/save/store/0');
/** @var \Magento\Catalog\Model\Category $category */
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php
index 45c1583d76400..169b6fa52e307 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php
@@ -6,10 +6,13 @@
namespace Magento\Catalog\Controller\Adminhtml\Product;
use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoAppArea adminhtml
* @magentoDbIsolation enabled
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendController
{
@@ -18,10 +21,14 @@ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendContr
*/
public function testWrongFrontendInput()
{
- $postData = $this->_getAttributeData() + [
+ $postData = array_merge(
+ $this->_getAttributeData(),
+ [
'attribute_id' => 100500,
'frontend_input' => 'some_input',
- ];
+ ]
+ );
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
@@ -47,6 +54,7 @@ public function testWithPopup()
'popup' => 'true',
'new_attribute_set_name' => 'new_attribute_set',
];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
@@ -68,6 +76,7 @@ public function testWithExceptionWhenSaveAttribute()
{
$postData = $this->_getAttributeData() + ['attribute_id' => 0, 'frontend_input' => 'boolean'];
$this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
$this->assertContains(
@@ -86,6 +95,7 @@ public function testWrongAttributeId()
{
$postData = $this->_getAttributeData() + ['attribute_id' => 100500];
$this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
$this->assertContains(
@@ -110,6 +120,7 @@ public function testAttributeWithoutId()
'set' => 4,
'frontend_input' => 'boolean',
];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
@@ -132,6 +143,7 @@ public function testWrongAttributeCode()
{
$postData = $this->_getAttributeData() + ['attribute_code' => '_()&&&?'];
$this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
$this->assertContains(
@@ -156,6 +168,7 @@ public function testWrongAttributeCode()
public function testAttributeWithoutEntityTypeId()
{
$postData = $this->_getAttributeData() + ['attribute_id' => '2', 'new_attribute_set_name' => ' '];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
@@ -171,6 +184,7 @@ public function testAttributeWithoutEntityTypeId()
public function testSaveActionApplyToDataSystemAttribute()
{
$postData = $this->_getAttributeData() + ['attribute_id' => '2'];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
$model = $this->_objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class);
@@ -184,6 +198,7 @@ public function testSaveActionApplyToDataSystemAttribute()
public function testSaveActionApplyToDataUserDefinedAttribute()
{
$postData = $this->_getAttributeData() + ['attribute_id' => '1'];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
/** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $model */
@@ -199,6 +214,7 @@ public function testSaveActionApplyToData()
{
$postData = $this->_getAttributeData() + ['attribute_id' => '3'];
unset($postData['apply_to']);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
$model = $this->_objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class);
@@ -218,6 +234,7 @@ public function testSaveActionCleanAttributeLabelCache()
$this->assertEquals('predefined string translation', $this->_translate('string to translate'));
$string->saveTranslate('string to translate', 'new string translation');
$postData = $this->_getAttributeData() + ['attribute_id' => 1];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product_attribute/save');
$this->assertEquals('new string translation', $this->_translate('string to translate'));
@@ -293,6 +310,7 @@ public function testLargeOptionsDataSet()
$optionsData[] = http_build_query($optionRowData);
}
$attributeData['serialized_options'] = json_encode($optionsData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($attributeData);
$this->dispatch('backend/catalog/product_attribute/save');
$entityTypeId = $this->_objectManager->create(
@@ -364,6 +382,7 @@ protected function _getAttributeData()
'default_value_textarea' => '0',
'is_required' => '1',
'frontend_class' => '',
+ 'frontend_input' => 'select',
'is_searchable' => '0',
'is_visible_in_advanced_search' => '0',
'is_comparable' => '0',
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php
index 7c5d4ea48a238..7e034b8b3cb7e 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php
@@ -6,6 +6,7 @@
namespace Magento\Catalog\Controller\Adminhtml\Product\Set;
use Magento\Framework\Message\MessageInterface;
+use Magento\Framework\App\Request\Http as HttpRequest;
class DeleteTest extends \Magento\TestFramework\TestCase\AbstractBackendController
{
@@ -15,7 +16,7 @@ class DeleteTest extends \Magento\TestFramework\TestCase\AbstractBackendControll
public function testDeleteById()
{
$attributeSet = $this->getAttributeSetByName('empty_attribute_set');
- $this->getRequest()->setParam('id', $attributeSet->getId());
+ $this->getRequest()->setParam('id', $attributeSet->getId())->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product_set/delete/');
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php
index 5b711b2ea7418..8ccd426424a29 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php
@@ -9,6 +9,7 @@
use Magento\Eav\Api\Data\AttributeSetInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Framework\App\Request\Http as HttpRequest;
class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController
{
@@ -20,6 +21,7 @@ public function testAlreadyExistsExceptionProcessingWhenGroupCodeIsDuplicated()
$attributeSet = $this->getAttributeSetByName('attribute_set_test');
$this->assertNotEmpty($attributeSet, 'Attribute set with name "attribute_set_test" is missed');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue('data', json_encode([
'attribute_set_name' => 'attribute_set_test',
'groups' => [
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php
index 4761f13175d81..44577b2a228a0 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php
@@ -11,6 +11,7 @@
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Message\MessageInterface;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoAppArea adminhtml
@@ -19,6 +20,7 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendControl
{
public function testSaveActionWithDangerRequest()
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(['product' => ['entity_id' => 15]]);
$this->dispatch('backend/catalog/product/save');
$this->assertSessionMessages(
@@ -36,6 +38,7 @@ public function testSaveActionAndNew()
$this->getRequest()->setPostValue(['back' => 'new']);
$repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class);
$product = $repository->get('simple');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId());
$this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/new/'));
$this->assertSessionMessages(
@@ -52,6 +55,7 @@ public function testSaveActionAndDuplicate()
$this->getRequest()->setPostValue(['back' => 'duplicate']);
$repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class);
$product = $repository->get('simple');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId());
$this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/edit/'));
$this->assertRedirect(
@@ -161,11 +165,13 @@ public function testEditAction()
public function testSaveActionWithAlreadyExistingUrlKey(array $postData)
{
$this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product/save');
/** @var Manager $messageManager */
$messageManager = $this->_objectManager->get(Manager::class);
$messages = $messageManager->getMessages();
$errors = $messages->getItemsByType('error');
+ $this->assertNotEmpty($errors);
$message = array_shift($errors);
$this->assertSame('URL key for specified store already exists.', $message->getText());
$this->assertRedirect($this->stringContains('/backend/catalog/product/new'));
@@ -233,7 +239,6 @@ public function saveActionWithAlreadyExistingUrlKeyDataProvider()
'thumbnail' => '/m/a//magento_image.jpg.tmp',
'swatch_image' => '/m/a//magento_image.jpg.tmp',
],
- 'form_key' => Bootstrap::getObjectManager()->get(FormKey::class)->getFormKey(),
]
]
];
@@ -251,6 +256,7 @@ public function saveActionWithAlreadyExistingUrlKeyDataProvider()
public function testSaveActionTierPrice(array $postData, array $tierPrice)
{
$postData['product'] = $this->getProductData($tierPrice);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/catalog/product/save/id/' . $postData['id']);
$this->assertSessionMessages(
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php
index 7e27fd7ede8b4..f9b1d10cbb8ae 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php
@@ -7,7 +7,9 @@
// @codingStandardsIgnoreFile
namespace Magento\Catalog\Controller\Product;
+
use Magento\Framework\Message\MessageInterface;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoDataFixture Magento/Catalog/controllers/_files/products.php
@@ -23,6 +25,9 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController
*/
protected $productRepository;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
parent::setUp();
@@ -40,6 +45,7 @@ public function testAddAction()
/** @var \Magento\Framework\Data\Form\FormKey $formKey */
$formKey = $objectManager->get(\Magento\Framework\Data\Form\FormKey::class);
$product = $this->productRepository->get('simple_product_1');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch(
sprintf(
'catalog/product_compare/add/product/%s/form_key/%s?nocookie=1',
@@ -49,7 +55,12 @@ public function testAddAction()
);
$this->assertSessionMessages(
- $this->equalTo(['You added product Simple Product 1 Name to the comparison list .']),
+ $this->equalTo(
+ [
+ 'You added product Simple Product 1 Name to the '.
+ 'comparison list .'
+ ]
+ ),
MessageInterface::TYPE_SUCCESS
);
@@ -73,6 +84,7 @@ public function testRemoveAction()
{
$this->_requireVisitorWithTwoProducts();
$product = $this->productRepository->get('simple_product_2');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId());
$this->assertSessionMessages(
@@ -89,6 +101,7 @@ public function testRemoveActionWithSession()
{
$this->_requireCustomerWithTwoProducts();
$product = $this->productRepository->get('simple_product_1');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId());
$secondProduct = $this->productRepository->get('simple_product_2');
@@ -132,6 +145,7 @@ public function testClearAction()
{
$this->_requireVisitorWithTwoProducts();
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('catalog/product_compare/clear');
$this->assertSessionMessages(
@@ -151,6 +165,7 @@ public function testRemoveActionProductNameXss()
{
$this->_prepareCompareListWithProductNameXss();
$product = $this->productRepository->get('product-with-xss');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId() . '?nocookie=1');
$this->assertSessionMessages(
@@ -307,7 +322,8 @@ protected function _assertCompareListEquals(array $expectedProductIds)
// important
$compareItems->setVisitorId(
\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\Customer\Model\Visitor::class)->getId()
+ \Magento\Customer\Model\Visitor::class
+ )->getId()
);
$actualProductIds = [];
foreach ($compareItems as $compareItem) {
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php
index 40607cd85b3b4..e70fa8f52d269 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php
@@ -42,11 +42,14 @@ protected function setUp()
/**
* @magentoDbIsolation disabled
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @magentoDataFixture Magento/Catalog/_files/category_product.php
*/
public function testTierPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$tierPriceValue = 9.00;
$tierPrice = $this->objectManager->create(ProductTierPriceInterfaceFactory::class)
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php
index 12b7da2bd6e35..e0f26a7aab01b 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php
@@ -17,7 +17,7 @@
/**
* @magentoDbIsolation disabled
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @group indexer_dimension
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
*/
@@ -37,6 +37,9 @@ protected function setUp()
public function testGetPriceFromIndexer()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
/** @var PriceTableResolver $tableResolver */
$tableResolver = Bootstrap::getObjectManager()->create(PriceTableResolver::class);
@@ -66,11 +69,17 @@ public function testGetPriceFromIndexer()
public function testGetPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEquals('test', $this->_model->getPrice(new DataObject(['price' => 'test'])));
}
public function testGetFinalPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$repository = Bootstrap::getObjectManager()->create(
ProductRepository::class
);
@@ -95,6 +104,9 @@ public function testGetFinalPrice()
public function testGetFormatedPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$repository = Bootstrap::getObjectManager()->create(
ProductRepository::class
);
@@ -105,12 +117,18 @@ public function testGetFormatedPrice()
public function testCalculatePrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEquals(10, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01'));
$this->assertEquals(8, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01'));
}
public function testCalculateSpecialPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEquals(
10,
$this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01')
@@ -123,6 +141,9 @@ public function testCalculateSpecialPrice()
public function testIsTierPriceFixed()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertTrue($this->_model->isTierPriceFixed());
}
@@ -134,6 +155,9 @@ public function testIsTierPriceFixed()
*/
private function prepareBuyRequest(Product $product)
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$options = [];
/** @var $option \Magento\Catalog\Model\Product\Option */
foreach ($product->getOptions() as $option) {
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php
index 9e3db8c155e28..cb776fb08723f 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php
@@ -15,7 +15,7 @@
* - pricing behaviour is tested
* @group indexer_dimension
* @magentoDbIsolation disabled
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @see \Magento\Catalog\Model\ProductTest
* @see \Magento\Catalog\Model\ProductExternalTest
*/
@@ -39,6 +39,9 @@ protected function setUp()
public function testGetPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEmpty($this->_model->getPrice());
$this->_model->setPrice(10.0);
$this->assertEquals(10.0, $this->_model->getPrice());
@@ -46,6 +49,9 @@ public function testGetPrice()
public function testGetPriceModel()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$default = $this->_model->getPriceModel();
$this->assertInstanceOf(\Magento\Catalog\Model\Product\Type\Price::class, $default);
$this->assertSame($default, $this->_model->getPriceModel());
@@ -56,6 +62,9 @@ public function testGetPriceModel()
*/
public function testGetTierPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEquals([], $this->_model->getTierPrice());
}
@@ -64,6 +73,9 @@ public function testGetTierPrice()
*/
public function testGetTierPriceCount()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEquals(0, $this->_model->getTierPriceCount());
}
@@ -72,11 +84,17 @@ public function testGetTierPriceCount()
*/
public function testGetFormatedPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEquals('$0.00 ', $this->_model->getFormatedPrice());
}
public function testSetGetFinalPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->assertEquals(0, $this->_model->getFinalPrice());
$this->_model->setPrice(10);
$this->_model->setFinalPrice(10);
@@ -88,6 +106,9 @@ public function testSetGetFinalPrice()
*/
public function testGetMinPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$product = $this->productRepository->get('simple');
$collection = Bootstrap::getObjectManager()->create(Collection::class);
$collection->addIdFilter($product->getId());
@@ -103,6 +124,9 @@ public function testGetMinPrice()
*/
public function testGetMinPriceForComposite()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$confProduct = $this->productRepository->get('configurable');
$collection = Bootstrap::getObjectManager()->create(Collection::class);
$collection->addIdFilter($confProduct->getId());
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
index 7954e2c36227f..476f01eb277df 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
@@ -12,6 +12,11 @@
class ProductTest extends TestCase
{
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
/**
* @var Product
*/
@@ -29,7 +34,8 @@ protected function setUp()
{
$this->objectManager = Bootstrap::getObjectManager();
- $this->model = $this->objectManager->get(Product::class);
+ $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+ $this->model = $this->objectManager->create(Product::class);
}
/**
@@ -42,11 +48,29 @@ public function testGetAttributeRawValue()
$sku = 'simple';
$attribute = 'name';
- /** @var ProductRepositoryInterface $productRepository */
- $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
- $product = $productRepository->get($sku);
-
+ $product = $this->productRepository->get($sku);
$actual = $this->model->getAttributeRawValue($product->getId(), $attribute, null);
self::assertEquals($product->getName(), $actual);
}
+
+ /**
+ * @magentoAppArea adminhtml
+ * @magentoDataFixture Magento/Catalog/_files/product_special_price.php
+ * @magentoAppIsolation enabled
+ * @magentoConfigFixture default_store catalog/price/scope 1
+ */
+ public function testUpdateStoreSpecificSpecialPrice()
+ {
+ /** @var \Magento\Catalog\Model\Product $product */
+ $product = $this->productRepository->get('simple', true, 1);
+ $this->assertEquals(5.99, $product->getSpecialPrice());
+
+ $product->setSpecialPrice('');
+ $this->model->save($product);
+ $product = $this->productRepository->get('simple', false, 1, true);
+ $this->assertEmpty($product->getSpecialPrice());
+
+ $product = $this->productRepository->get('simple', false, 0, true);
+ $this->assertEquals(5.99, $product->getSpecialPrice());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php
new file mode 100644
index 0000000000000..23fd8d7fe324e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php
@@ -0,0 +1,63 @@
+ 1,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1
+];
+
+/** @var ObjectManagerInterface $objectManager */
+$objectManager = Bootstrap::getObjectManager();
+
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->create(ProductRepositoryInterface::class);
+
+/** @var ProductInterface $product */
+$product = $objectManager->create(ProductInterface::class);
+$product->setTypeId(Type::TYPE_SIMPLE)
+ ->setAttributeSetId(4)
+ ->setWebsiteIds([1])
+ ->setName('Чудовий продукт без Url Key')
+ ->setSku('ukrainian-without-url-key')
+ ->setPrice(10)
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setCategoryIds([2])
+ ->setStockData($stockDataConfig);
+try {
+ $productRepository->save($product);
+} catch (\Exception $e) {
+ // problems during save
+};
+
+/** @var ProductInterface $product */
+$product = $objectManager->create(ProductInterface::class);
+$product->setTypeId(Type::TYPE_SIMPLE)
+ ->setAttributeSetId(4)
+ ->setWebsiteIds([1])
+ ->setName('Надзвичайний продукт з Url Key')
+ ->setSku('ukrainian-with-url-key')
+ ->setPrice(10)
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setCategoryIds([2])
+ ->setStockData($stockDataConfig)
+ ->setUrlKey('надзвичайний продукт на кожен день');
+try {
+ $productRepository->save($product);
+} catch (\Exception $e) {
+ // problems during save
+};
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php
new file mode 100644
index 0000000000000..d4592430c0e94
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php
@@ -0,0 +1,37 @@
+getInstance()->reinitialize();
+
+/** @var Registry $registry */
+$registry = Bootstrap::getObjectManager()->get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = Bootstrap::getObjectManager()
+ ->get(ProductRepositoryInterface::class);
+
+$productSkus = [
+ 'ukrainian-with-url-key',
+ 'ukrainian-without-url-key',
+];
+try {
+ foreach ($productSkus as $sku) {
+ $product = $productRepository->get($sku, false, null, true);
+ $productRepository->delete($product);
+ }
+} catch (NoSuchEntityException $e) {
+ // nothing to delete
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php
index 15e274541bac4..a5ca4573d98b0 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php
@@ -15,8 +15,8 @@
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
->setWebsiteIds([1])
->setStockData(['qty' => 100, 'is_in_stock' => 1])
- ->setNewsFromDate(date('Y-m-d', strtotime('-2 day')))
- ->setNewsToDate(date('Y-m-d', strtotime('+2 day')))
+ ->setNewsFromDate(date('Y-m-d H:i:s', strtotime('-2 day')))
+ ->setNewsToDate(date('Y-m-d H:i:s', strtotime('+2 day')))
->setDescription('description')
->setShortDescription('short desc')
->save();
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
index 6e361dfb05de0..cb96910ec86e1 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -28,11 +28,13 @@
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
use Magento\Store\Model\Store;
use Psr\Log\LoggerInterface;
+use Magento\Framework\Exception\NoSuchEntityException;
/**
* Class ProductTest
* @magentoAppIsolation enabled
* @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
* @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php
* @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_catalog_product_reindex_schedule.php
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -71,6 +73,11 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase
*/
private $logger;
+ /**
+ * @var array
+ */
+ private $importedProducts;
+
/**
* @inheritdoc
*/
@@ -84,10 +91,27 @@ protected function setUp()
\Magento\CatalogImportExport\Model\Import\Product::class,
['logger' => $this->logger]
);
+ $this->importedProducts = [];
parent::setUp();
}
+ protected function tearDown()
+ {
+ /* We rollback here the products created during the Import because they were
+ created during test execution and we do not have the rollback for them */
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+ foreach ($this->importedProducts as $productSku) {
+ try {
+ $product = $productRepository->get($productSku, false, null, true);
+ $productRepository->delete($product);
+ } catch (NoSuchEntityException $e) {
+ // nothing to delete
+ }
+ }
+ }
+
/**
* Options for assertion
*
@@ -271,6 +295,8 @@ public function testStockState()
* @param string $importFile
* @param string $sku
* @param int $expectedOptionsQty
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
* @magentoAppIsolation enabled
*/
public function testSaveCustomOptions($importFile, $sku, $expectedOptionsQty)
@@ -1244,6 +1270,8 @@ public function testProductPositionInCategory()
* @magentoAppIsolation enabled
* @magentoDataFixture Magento/Catalog/_files/category_product.php
* @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function testNewProductPositionInCategory()
{
@@ -1369,6 +1397,7 @@ protected function loadCategoryByName($categoryName)
* @dataProvider validateUrlKeysDataProvider
* @param $importFile string
* @param $expectedErrors array
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function testValidateUrlKeys($importFile, $expectedErrors)
{
@@ -1597,12 +1626,13 @@ public function testImportWithoutUrlKeys()
* @magentoDbIsolation disabled
* @magentoAppIsolation enabled
* @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function testImportWithUrlKeysWithSpaces()
{
$products = [
- 'simple1' => 'url-key-with-spaces1',
- 'simple2' => 'url-key-with-spaces2',
+ 'simple1' => 'url key with spaces1',
+ 'simple2' => 'url key with spaces2',
];
$filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class);
$directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
@@ -1629,6 +1659,52 @@ public function testImportWithUrlKeysWithSpaces()
}
}
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_with_non_latin_url_key.php
+ * @magentoDbIsolation disabled
+ * @magentoAppIsolation enabled
+ * @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function testImportWithNonLatinUrlKeys()
+ {
+ $productsCreatedByFixture = [
+ 'ukrainian-with-url-key' => 'nove-im-ja-pislja-importu-scho-stane-url-key',
+ 'ukrainian-without-url-key' => 'новий url key після імпорту',
+ ];
+ $productsImportedByCsv = [
+ 'imported-ukrainian-with-url-key' => 'імпортований продукт',
+ 'imported-ukrainian-without-url-key' => 'importovanij-produkt-bez-url-key',
+ ];
+ $productSkuMap = array_merge($productsCreatedByFixture, $productsImportedByCsv);
+ $this->importedProducts = array_keys($productsImportedByCsv);
+
+ $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class);
+ $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
+ $source = $this->objectManager->create(
+ \Magento\ImportExport\Model\Import\Source\Csv::class,
+ [
+ 'file' => __DIR__ . '/_files/products_to_import_with_non_latin_url_keys.csv',
+ 'directory' => $directory,
+ ]
+ );
+
+ $errors = $this->_model->setParameters(
+ ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE, 'entity' => 'catalog_product']
+ )
+ ->setSource($source)
+ ->validateData();
+
+ $this->assertEquals($errors->getErrorsCount(), 0);
+ $this->_model->importData();
+
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+ foreach ($productSkuMap as $productSku => $productUrlKey) {
+ $this->assertEquals($productUrlKey, $productRepository->get($productSku)->getUrlKey());
+ }
+ }
+
/**
* Make sure the absence of a url_key column in the csv file won't erase the url key of the existing products.
* To reach the goal we need to not send the name column, as the url key is generated from it.
@@ -1844,6 +1920,7 @@ public function testProductWithWrappedAdditionalAttributes()
*
* @param string $fileName
* @param int $expectedErrors
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
private function importDataForMediaTest(string $fileName, int $expectedErrors = 0)
{
@@ -2266,6 +2343,7 @@ public function testImportWithBackordersDisabled()
* Import file by providing import filename in parameters
*
* @param string $fileName
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
private function importFile(string $fileName)
{
@@ -2297,6 +2375,7 @@ private function importFile(string $fileName)
* Import file with non-existing images and skip-errors strategy.
*
* @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function testImportWithSkipErrorsAndNonExistingImage()
{
@@ -2382,6 +2461,7 @@ public function testImportProductWithUpdateUrlKey()
* @magentoDataFixture mediaImportImageFixture
* @magentoAppIsolation enabled
* @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function testSaveProductOnImportNonExistingImage()
{
@@ -2414,6 +2494,8 @@ public function testSaveProductOnImportNonExistingImage()
* @magentoDbIsolation disabled
* @magentoAppIsolation enabled
* @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function testImportProductWithContinueOnError()
{
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv
new file mode 100644
index 0000000000000..8b324a5330779
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv
@@ -0,0 +1,5 @@
+sku,product_type,store_view_code,name,price,attribute_set_code,url_key
+imported-ukrainian-with-url-key,simple,,"Імпортований продукт з Url Key",50,Default,"імпортований продукт"
+imported-ukrainian-without-url-key,simple,,"Імпортований продукт без Url Key",55,Default,
+ukrainian-without-url-key,simple,,"Чудовий продукт без Url Key",55,Default,"новий url key після імпорту"
+ukrainian-with-url-key,simple,,"Нове ім'я після імпорту що стане url key",55,Default,
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php
index 08e9ebbd1f9f0..5774f1cf76ae9 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php
@@ -73,21 +73,6 @@ public function testCreateCollection()
$this->performAssertions(2);
}
- /**
- * Test product list widget can process condition with multiple product sku.
- *
- * @magentoDbIsolation disabled
- * @magentoDataFixture Magento/Catalog/_files/multiple_products.php
- */
- public function testCreateCollectionWithMultipleSkuCondition()
- {
- $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,' .
- '`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule|' .
- '|Condition||Product`,`attribute`:`sku`,`operator`:`==`,`value`:`simple1, simple2`^]^]';
- $this->block->setData('conditions_encoded', $encodedConditions);
- $this->performAssertions(2);
- }
-
/**
* Test product list widget can process condition with dropdown type of attribute
*
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php
new file mode 100644
index 0000000000000..26d9651263cef
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php
@@ -0,0 +1,126 @@
+management = $objectManager->get(GuestShippingInformationManagementInterface::class);
+ $this->cartRepo = $objectManager->get(CartRepositoryInterface::class);
+ $this->customerRepo = $objectManager->get(CustomerRepositoryInterface::class);
+ $this->shippingFactory = $objectManager->get(ShippingInformationInterfaceFactory::class);
+ $this->searchCriteria = $objectManager->get(SearchCriteriaBuilder::class);
+ $this->maskFactory = $objectManager->get(QuoteIdMaskFactory::class);
+ }
+
+ /**
+ * Test using another address for quote.
+ *
+ * @param bool $swapShipping Whether to swap shipping or billing addresses.
+ * @return void
+ *
+ * @magentoDataFixture Magento/Sales/_files/quote.php
+ * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php
+ * @dataProvider differentAddressesDataProvider
+ * @expectedException \Magento\Framework\Exception\InputException
+ * @expectedExceptionMessage Unable to save shipping information. Please check input data.
+ */
+ public function testDifferentAddresses(bool $swapShipping)
+ {
+ $carts = $this->cartRepo->getList(
+ $this->searchCriteria->addFilter('reserved_order_id', 'test01')->create()
+ )->getItems();
+ $cart = array_pop($carts);
+ $otherCustomer = $this->customerRepo->get('customer_with_addresses@test.com');
+ $otherAddresses = $otherCustomer->getAddresses();
+ $otherAddress = array_pop($otherAddresses);
+
+ //Setting invalid IDs.
+ /** @var ShippingAssignmentInterface $shippingAssignment */
+ $shippingAssignment = $cart->getExtensionAttributes()->getShippingAssignments()[0];
+ $shippingAddress = $shippingAssignment->getShipping()->getAddress();
+ $billingAddress = $cart->getBillingAddress();
+ if ($swapShipping) {
+ $address = $shippingAddress;
+ } else {
+ $address = $billingAddress;
+ }
+ $address->setCustomerAddressId($otherAddress->getId());
+ $address->setCustomerId($otherCustomer->getId());
+ $address->setId(null);
+ /** @var ShippingInformationInterface $shippingInformation */
+ $shippingInformation = $this->shippingFactory->create();
+ $shippingInformation->setBillingAddress($billingAddress);
+ $shippingInformation->setShippingAddress($shippingAddress);
+ $shippingInformation->setShippingMethodCode('flatrate');
+ /** @var QuoteIdMask $idMask */
+ $idMask = $this->maskFactory->create();
+ $idMask->load($cart->getId(), 'quote_id');
+ $this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation);
+ }
+
+ /**
+ * @return array
+ */
+ public function differentAddressesDataProvider(): array
+ {
+ return [
+ 'Shipping address swap' => [true],
+ 'Billing address swap' => [false],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php
new file mode 100644
index 0000000000000..ff795a73fec35
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php
@@ -0,0 +1,105 @@
+management = $objectManager->get(ShippingInformationManagementInterface::class);
+ $this->cartRepo = $objectManager->get(CartRepositoryInterface::class);
+ $this->customerRepo = $objectManager->get(CustomerRepositoryInterface::class);
+ $this->shippingFactory = $objectManager->get(ShippingInformationInterfaceFactory::class);
+ }
+
+ /**
+ * Test using another address for quote.
+ *
+ * @param bool $swapShipping Whether to swap shipping or billing addresses.
+ * @return void
+ *
+ * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php
+ * @dataProvider differentAddressesDataProvider
+ * @expectedException \Magento\Framework\Exception\InputException
+ * @expectedExceptionMessage Unable to save shipping information. Please check input data.
+ */
+ public function testDifferentAddresses(bool $swapShipping)
+ {
+ $cart = $this->cartRepo->getForCustomer(1);
+ $otherCustomer = $this->customerRepo->get('customer_with_addresses@test.com');
+ $otherAddresses = $otherCustomer->getAddresses();
+ $otherAddress = array_pop($otherAddresses);
+
+ //Setting invalid IDs.
+ /** @var ShippingAssignmentInterface $shippingAssignment */
+ $shippingAssignment = $cart->getExtensionAttributes()->getShippingAssignments()[0];
+ $shippingAddress = $shippingAssignment->getShipping()->getAddress();
+ $billingAddress = $cart->getBillingAddress();
+ if ($swapShipping) {
+ $address = $shippingAddress;
+ } else {
+ $address = $billingAddress;
+ }
+ $address->setCustomerAddressId($otherAddress->getId());
+ $address->setCustomerId($otherCustomer->getId());
+ $address->setId(null);
+ /** @var ShippingInformationInterface $shippingInformation */
+ $shippingInformation = $this->shippingFactory->create();
+ $shippingInformation->setBillingAddress($billingAddress);
+ $shippingInformation->setShippingAddress($shippingAddress);
+ $shippingInformation->setShippingMethodCode('flatrate');
+ $this->management->saveAddressInformation($cart->getId(), $shippingInformation);
+ }
+
+ /**
+ * @return array
+ */
+ public function differentAddressesDataProvider(): array
+ {
+ return [
+ 'Shipping address swap' => [true],
+ 'Billing address swap' => [false],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php
index 661593b65adca..ec450d3f2fdda 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php
@@ -28,13 +28,14 @@ public function testExecute()
'remove' => 0,
'coupon_code' => 'test'
];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($inputData);
$this->dispatch(
'checkout/cart/couponPost/'
);
$this->assertSessionMessages(
- $this->equalTo(['The coupon code "test" is not valid.']),
+ $this->equalTo(['The coupon code "test" is not valid.']),
\Magento\Framework\Message\MessageInterface::TYPE_ERROR
);
}
@@ -65,7 +66,7 @@ public function testAddingValidCoupon()
);
$this->assertSessionMessages(
- $this->equalTo(['You used coupon code "' . $couponCode . '".']),
+ $this->equalTo(['You used coupon code "' . $couponCode . '".']),
\Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
);
}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php
index 068a9c3529c15..d2e93f7c94ff4 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php
@@ -22,6 +22,7 @@
use Magento\Customer\Model\Session as CustomerSession;
use Magento\Sales\Model\ResourceModel\Order\Collection as OrderCollection;
use Magento\Sales\Model\ResourceModel\Order\Item\Collection as OrderItemCollection;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -286,7 +287,8 @@ private function getQuote($reservedOrderId)
* Gets \Magento\Quote\Model\Quote\Item from \Magento\Quote\Model\Quote by product id
*
* @param \Magento\Quote\Model\Quote $quote
- * @param $productId
+ * @param string|int $productId
+ *
* @return \Magento\Quote\Model\Quote\Item|null
*/
private function _getQuoteItemIdByProductId($quote, $productId)
@@ -321,6 +323,7 @@ public function testAddToCartSimpleProduct($area, $expectedPrice)
'isAjax' => 1
];
\Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea($area);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$quote = $this->_objectManager->create(\Magento\Checkout\Model\Cart::class);
@@ -367,6 +370,7 @@ public function testMessageAtAddToCartWithRedirect()
];
\Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend');
$this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('checkout/cart/add');
@@ -402,6 +406,7 @@ public function testMessageAtAddToCartWithoutRedirect()
];
\Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend');
$this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('checkout/cart/add');
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php
index 43108dbca1f5e..85dede0d84c2d 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php
@@ -21,6 +21,7 @@ class ResetQuoteAddressesTest extends \PHPUnit\Framework\TestCase
{
/**
* @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php
+ * @magentoAppArea frontend
*
* @return void
*/
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php
index 79d4a05aba3c4..994d4d1412b05 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php
@@ -58,6 +58,7 @@ public function testExecute()
$this->mediaDirectory->getRelativePath($fullDirectoryPath . $directoryName)
);
$this->model->getRequest()->setParams(['node' => $this->imagesHelper->idEncode($directoryName)]);
+ $this->model->getRequest()->setMethod('POST');
$this->model->execute();
$this->assertFalse(
$this->mediaDirectory->isExist(
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php
index 629d997a64f87..6b1f8fc717c2d 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php
@@ -77,6 +77,7 @@ public function testExecute()
$this->mediaDirectory->create($this->mediaDirectory->getRelativePath($fullDirectoryPath));
$this->model->getRequest()->setParams(['type' => 'image/png']);
+ $this->model->getRequest()->setMethod('POST');
$this->model->getStorage()->getSession()->setCurrentPath($fullDirectoryPath);
$this->model->execute();
$this->assertTrue(
@@ -101,6 +102,7 @@ public function testExecuteWithLinkedMedia()
$fullDirectoryPath = $this->filesystem->getDirectoryRead(DirectoryList::PUB)
->getAbsolutePath() . DIRECTORY_SEPARATOR . $directoryName;
$wysiwygDir = $this->mediaDirectory->getAbsolutePath() . '/wysiwyg';
+ $this->model->getRequest()->setMethod('POST');
$this->model->getRequest()->setParams(['type' => 'image/png']);
$this->model->getStorage()->getSession()->setCurrentPath($wysiwygDir);
$this->model->execute();
diff --git a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php
index a1998c6c89536..782d8dadcc1e8 100644
--- a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php
+++ b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php
@@ -6,6 +6,8 @@
namespace Magento\Config\Console\Command;
use Magento\Config\Model\Config\Backend\Admin\Custom;
+use Magento\Config\Model\Config\Structure\Converter;
+use Magento\Config\Model\Config\Structure\Data as StructureData;
use Magento\Directory\Model\Currency;
use Magento\Framework\App\Config\ConfigPathResolver;
use Magento\Framework\App\Config\ScopeConfigInterface;
@@ -90,6 +92,8 @@ protected function setUp()
{
Bootstrap::getInstance()->reinitialize();
$this->objectManager = Bootstrap::getObjectManager();
+ $this->extendSystemStructure();
+
$this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class);
$this->reader = $this->objectManager->get(FileReader::class);
$this->filesystem = $this->objectManager->get(Filesystem::class);
@@ -122,6 +126,21 @@ protected function tearDown()
$this->appConfig->reinit();
}
+ /**
+ * Add test system structure to main system structure
+ *
+ * @return void
+ */
+ private function extendSystemStructure()
+ {
+ $document = new \DOMDocument();
+ $document->load(__DIR__ . '/../../_files/system.xml');
+ $converter = $this->objectManager->get(Converter::class);
+ $systemConfig = $converter->convert($document);
+ $structureData = $this->objectManager->get(StructureData::class);
+ $structureData->merge($systemConfig);
+ }
+
/**
* @return array
*/
@@ -190,6 +209,8 @@ public function runLockDataProvider()
['general/region/display_all', '1'],
['general/region/state_required', 'BR,FR', ScopeInterface::SCOPE_WEBSITE, 'base'],
['admin/security/use_form_key', '0'],
+ ['general/group/subgroup/field', 'default_value'],
+ ['general/group/subgroup/field', 'website_value', ScopeInterface::SCOPE_WEBSITE, 'base'],
];
}
diff --git a/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php
index 9ad99745d572f..5170a4b8a4dd6 100644
--- a/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php
+++ b/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php
@@ -9,6 +9,7 @@
namespace Magento\Config\Controller\Adminhtml\System;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoAppArea adminhtml
@@ -22,6 +23,8 @@ public function testEditAction()
}
/**
+ * Test redirect after changing base URL.
+ *
* @magentoAppIsolation enabled
* @magentoDbIsolation enabled
*/
@@ -31,20 +34,22 @@ public function testChangeBaseUrl()
$newHost = 'm2test123.loc';
$request = $this->getRequest();
$request->setPostValue(
- ['groups' =>
- ['unsecure' =>
- ['fields' =>
- ['base_url' =>
- ['value' => 'http://' . $newHost . '/']
+ [
+ 'groups' =>
+ ['unsecure' =>
+ ['fields' =>
+ ['base_url' =>
+ ['value' => 'http://' . $newHost . '/']
+ ]
]
- ]
- ],
- 'config_state' =>
- ['web_unsecure' => 1]
+ ],
+ 'config_state' => ['web_unsecure' => 1]
]
)->setParam(
'section',
'web'
+ )->setMethod(
+ HttpRequest::METHOD_POST
);
$this->dispatch('backend/admin/system_config/save');
@@ -62,14 +67,16 @@ public function testChangeBaseUrl()
}
/**
- * Reset test framework default base url
+ * Reset test framework default base url.
+ *
+ * @param string $defaultHost
*/
protected function resetBaseUrl($defaultHost)
{
$baseUrlData = [
'section' => 'web',
- 'website' => NULL,
- 'store' => NULL,
+ 'website' => null,
+ 'store' => null,
'groups' => [
'unsecure' => [
'fields' => [
diff --git a/dev/tests/integration/testsuite/Magento/Config/_files/system.xml b/dev/tests/integration/testsuite/Magento/Config/_files/system.xml
new file mode 100644
index 0000000000000..f0063a3c0bf7f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Config/_files/system.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
index 4254a6ce9c71d..b71507ae43f9f 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
@@ -9,6 +9,7 @@
use Magento\Framework\Registry;
use Magento\TestFramework\ObjectManager;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoAppArea adminhtml
@@ -23,6 +24,7 @@ public function testSaveActionAssociatedProductIds()
{
$associatedProductIds = ['3', '14', '15', '92'];
$associatedProductIdsJSON = json_encode($associatedProductIds);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'id' => 1,
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php
index f9776b2264ff3..0d93d3ad4f4ae 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php
@@ -9,6 +9,8 @@
*/
namespace Magento\ConfigurableProduct\Controller;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
class CartTest extends \Magento\TestFramework\TestCase\AbstractController
{
/**
@@ -85,13 +87,14 @@ public function testExecuteForConfigurableLastOption()
'remove' => 0,
'coupon_code' => 'test'
];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($inputData);
$this->dispatch(
'checkout/cart/couponPost/'
);
$this->assertSessionMessages(
- $this->equalTo(['The coupon code "test" is not valid.']),
+ $this->equalTo(['The coupon code "test" is not valid.']),
\Magento\Framework\Message\MessageInterface::TYPE_ERROR
);
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php
index f08f0a4543ea3..140df500472b3 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php
@@ -16,7 +16,7 @@
/**
* @magentoDbIsolation disabled
* @group indexer_dimension
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
*/
class SpecialPriceIndexerWithDimensionTest extends \PHPUnit\Framework\TestCase
{
@@ -51,6 +51,9 @@ protected function setUp()
*/
public function testFullReindexIfChildHasSpecialPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$specialPrice = 2;
/** @var Product $childProduct */
$childProduct = $this->productRepository->get('simple_10', true);
@@ -88,6 +91,9 @@ public function testFullReindexIfChildHasSpecialPrice()
*/
public function testOnSaveIndexationIfChildHasSpecialPrice()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$specialPrice = 2;
/** @var Product $childProduct */
$childProduct = $this->productRepository->get('simple_10', true);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php
index bddbb38e9f019..214dcafc9f686 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php
@@ -16,7 +16,7 @@
/**
* @magentoDbIsolation disabled
- * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
+ * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @group indexer_dimension
* Test price rendering according to is_product_list flag for Configurable product
*/
@@ -82,6 +82,9 @@ protected function setUp()
*/
public function testRenderingByDefault()
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$html = $this->finalPriceBox->toHtml();
self::assertContains('5.99', $html);
$this->assertGreaterThanOrEqual(
@@ -117,6 +120,9 @@ public function testRenderingByDefault()
*/
public function testRenderingAccordingToIsProductListFlag($flag, $count)
{
+ $this->markTestSkipped(
+ 'Skipped because of MAGETWO-99136'
+ );
$this->finalPriceBox->setData('is_product_list', $flag);
$html = $this->finalPriceBox->toHtml();
self::assertContains('5.99', $html);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php
index 234f0aae6a3ea..63858e91b64f2 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php
@@ -5,7 +5,19 @@
*/
namespace Magento\ConfigurableProduct\Ui\DataProvider\Product\Form\Modifier\Data;
-class AssociatedProductsTest extends \PHPUnit\Framework\TestCase
+use Magento\Framework\View\Element\UiComponentFactory;
+use Magento\Ui\Component\Filters\FilterModifier;
+use Magento\Framework\View\Element\UiComponent\ContextInterface;
+use Magento\ConfigurableProduct\Ui\DataProvider\Product\Form\Modifier\ConfigurablePanel;
+use Magento\Framework\App\RequestInterface;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * AssociatedProductsTest
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class AssociatedProductsTest extends TestCase
{
/**
* @var \Magento\Framework\ObjectManagerInterface $objectManager
@@ -17,6 +29,9 @@ class AssociatedProductsTest extends \PHPUnit\Framework\TestCase
*/
private $registry;
+ /**
+ * @inheritdoc
+ */
public function setUp()
{
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
@@ -64,6 +79,53 @@ public function testGetProductMatrix($interfaceLocale)
}
}
+ /**
+ * Tests configurable product won't appear in product listing.
+ *
+ * Tests configurable product won't appear in configurable associated product listing if its options attribute
+ * is not filterable in grid.
+ *
+ * @return void
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+ * @magentoAppArea adminhtml
+ */
+ public function testAddManuallyConfigurationsWithNotFilterableInGridAttribute()
+ {
+ /** @var RequestInterface $request */
+ $request = $this->objectManager->get(RequestInterface::class);
+ $request->setParams([
+ FilterModifier::FILTER_MODIFIER => [
+ 'test_configurable' => [
+ 'condition_type' => 'notnull',
+ ],
+ ],
+ 'attributes_codes' => [
+ 'test_configurable'
+ ],
+ ]);
+ $context = $this->objectManager->create(ContextInterface::class, ['request' => $request]);
+ /** @var UiComponentFactory $uiComponentFactory */
+ $uiComponentFactory = $this->objectManager->get(UiComponentFactory::class);
+ $uiComponent = $uiComponentFactory->create(
+ ConfigurablePanel::ASSOCIATED_PRODUCT_LISTING,
+ null,
+ ['context' => $context]
+ );
+
+ foreach ($uiComponent->getChildComponents() as $childUiComponent) {
+ $childUiComponent->prepare();
+ }
+
+ $dataSource = $uiComponent->getDataSourceData();
+ $skus = array_column($dataSource['data']['items'], 'sku');
+
+ $this->assertNotContains(
+ 'configurable',
+ $skus,
+ 'Only products with specified attribute should be in product list'
+ );
+ }
+
/**
* @return array
*/
diff --git a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php
index c9f2ffad67644..fefd1a7b250c3 100644
--- a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php
+++ b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currency;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
class SaveRatesTest extends \Magento\TestFramework\TestCase\AbstractBackendController
{
@@ -43,6 +45,7 @@ public function testSaveAction()
$rate = 1.0000;
$request = $this->getRequest();
+ $request->setMethod(HttpRequest::METHOD_POST);
$request->setPostValue(
'rate',
[
@@ -75,6 +78,7 @@ public function testSaveWithWarningAction()
$rate = '0';
$request = $this->getRequest();
+ $request->setMethod(HttpRequest::METHOD_POST);
$request->setPostValue(
'rate',
[
diff --git a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php
index 5217c3576a51d..2929f137be89f 100644
--- a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php
+++ b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php
@@ -5,10 +5,16 @@
*/
namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController
{
/**
- * Test save action
+ * Test save action.
+ *
+ * @param string $currencyCode
+ * @param string $inputCurrencySymbol
+ * @param string $outputCurrencySymbol
*
* @magentoConfigFixture currency/options/allow USD
* @magentoDbIsolation enabled
@@ -31,6 +37,7 @@ public function testSaveAction($currencyCode, $inputCurrencySymbol, $outputCurre
$currencyCode => $inputCurrencySymbol,
]
);
+ $request->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/admin/system_currencysymbol/save');
$this->assertRedirect();
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
index 4fed6c84ab09d..c169272b133bc 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
@@ -27,6 +27,7 @@
use Magento\Framework\Stdlib\CookieManagerInterface;
use Magento\Theme\Controller\Result\MessagePlugin;
use Zend\Stdlib\Parameters;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -111,10 +112,8 @@ public function testForgotPasswordEmailMessageWithSpecialCharacters()
{
$email = 'customer@example.com';
- $this->getRequest()
- ->setPostValue([
- 'email' => $email,
- ]);
+ $this->getRequest()->setPostValue(['email' => $email]);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('customer/account/forgotPasswordPost');
$this->assertRedirect($this->stringContains('customer/account/'));
@@ -232,6 +231,7 @@ public function testConfirmActionAlreadyActive()
public function testNoFormKeyCreatePostAction()
{
$this->fillRequestWithAccountData();
+ $this->getRequest()->setPostValue('form_key', null);
$this->dispatch('customer/account/createPost');
$this->assertNull($this->getCustomerByEmail('test1@email.com'));
@@ -279,8 +279,7 @@ public function testWithConfirmCreatePostAction()
*/
public function testExistingEmailCreatePostAction()
{
- $this->fillRequestWithAccountDataAndFormKey();
- $this->getRequest()->setParam('email', 'customer@example.com');
+ $this->fillRequestWithAccountDataAndFormKey('customer@example.com');
$this->dispatch('customer/account/createPost');
$this->assertRedirect($this->stringContains('customer/account/create/'));
$this->assertSessionMessages(
@@ -339,10 +338,8 @@ public function testForgotPasswordPostAction()
{
$email = 'customer@example.com';
- $this->getRequest()
- ->setPostValue([
- 'email' => $email,
- ]);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setPostValue(['email' => $email]);
$this->dispatch('customer/account/forgotPasswordPost');
$this->assertRedirect($this->stringContains('customer/account/'));
@@ -362,6 +359,7 @@ public function testForgotPasswordPostAction()
*/
public function testForgotPasswordPostWithBadEmailAction()
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()
->setPostValue([
'email' => 'bad@email',
@@ -383,6 +381,7 @@ public function testResetPasswordPostNoTokenAction()
$this->getRequest()
->setParam('id', 1)
->setParam('token', '8ed8677e6c79e68b94e61658bd756ea5')
+ ->setMethod('POST')
->setPostValue([
'password' => 'new-password',
'password_confirmation' => 'new-password',
@@ -515,18 +514,19 @@ public function testChangePasswordEditPostAction()
$this->login(1);
$this->getRequest()
->setMethod('POST')
- ->setPostValue([
- 'form_key' => $this->_objectManager->get(
- FormKey::class)->getFormKey(),
- 'firstname' => 'John',
- 'lastname' => 'Doe',
- 'email' => 'johndoe@email.com',
- 'change_password' => 1,
- 'change_email' => 1,
- 'current_password' => 'password',
- 'password' => 'new-Password1',
- 'password_confirmation' => 'new-Password1',
- ]);
+ ->setPostValue(
+ [
+ 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(),
+ 'firstname' => 'John',
+ 'lastname' => 'Doe',
+ 'email' => 'johndoe@email.com',
+ 'change_password' => 1,
+ 'change_email' => 1,
+ 'current_password' => 'password',
+ 'password' => 'new-Password1',
+ 'password_confirmation' => 'new-Password1',
+ ]
+ );
$this->dispatch('customer/account/editPost');
@@ -550,14 +550,16 @@ public function testMissingDataEditPostAction()
$this->login(1);
$this->getRequest()
->setMethod('POST')
- ->setPostValue([
- 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(),
- 'firstname' => 'John',
- 'lastname' => 'Doe',
- 'change_email' => 1,
- 'current_password' => 'password',
- 'email' => 'bad-email',
- ]);
+ ->setPostValue(
+ [
+ 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(),
+ 'firstname' => 'John',
+ 'lastname' => 'Doe',
+ 'change_email' => 1,
+ 'current_password' => 'password',
+ 'email' => 'bad-email',
+ ]
+ );
$this->dispatch('customer/account/editPost');
@@ -576,17 +578,18 @@ public function testWrongPasswordEditPostAction()
$this->login(1);
$this->getRequest()
->setMethod('POST')
- ->setPostValue([
- 'form_key' => $this->_objectManager->get(
- FormKey::class)->getFormKey(),
- 'firstname' => 'John',
- 'lastname' => 'Doe',
- 'email' => 'johndoe@email.com',
- 'change_password' => 1,
- 'current_password' => 'wrong-password',
- 'password' => 'new-password',
- 'password_confirmation' => 'new-password',
- ]);
+ ->setPostValue(
+ [
+ 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(),
+ 'firstname' => 'John',
+ 'lastname' => 'Doe',
+ 'email' => 'johndoe@email.com',
+ 'change_password' => 1,
+ 'current_password' => 'wrong-password',
+ 'password' => 'new-password',
+ 'password_confirmation' => 'new-password',
+ ]
+ );
$this->dispatch('customer/account/editPost');
@@ -607,8 +610,7 @@ public function testWrongConfirmationEditPostAction()
$this->getRequest()
->setMethod('POST')
->setPostValue([
- 'form_key' => $this->_objectManager->get(
- FormKey::class)->getFormKey(),
+ 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(),
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'johndoe@email.com',
@@ -640,19 +642,18 @@ public function testWrongConfirmationEditPostAction()
public function testLoginPostRedirect($redirectDashboard, string $redirectUrl)
{
if (isset($redirectDashboard)) {
- $this->_objectManager->get(ScopeConfigInterface::class)->setValue('customer/startup/redirect_dashboard', $redirectDashboard);
+ $this->_objectManager->get(ScopeConfigInterface::class)->setValue(
+ 'customer/startup/redirect_dashboard',
+ $redirectDashboard
+ );
}
-
$this->_objectManager->get(Redirect::class)->setRedirectCookie('test');
-
$configValue = $this->_objectManager->create(Value::class);
$configValue->load('web/unsecure/base_url', 'path');
$baseUrl = $configValue->getValue() ?: 'http://localhost/';
-
$request = $this->prepareRequest();
$app = $this->_objectManager->create(Http::class, ['_request' => $request]);
$response = $app->launch();
-
$this->assertResponseRedirect($response, $baseUrl . $redirectUrl);
$this->assertTrue($this->_objectManager->get(Session::class)->isLoggedIn());
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php
index ddf23e1b6ea98..484725346af64 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php
@@ -9,6 +9,7 @@
use Magento\Customer\Model\CustomerRegistry;
use Magento\Framework\Data\Form\FormKey;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Framework\App\Request\Http as HttpRequest;
class AddressTest extends \Magento\TestFramework\TestCase\AbstractController
{
@@ -18,6 +19,9 @@ class AddressTest extends \Magento\TestFramework\TestCase\AbstractController
/** @var FormKey */
private $formKey;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
parent::setUp();
@@ -165,7 +169,7 @@ public function testFailedFormPostAction()
public function testDeleteAction()
{
$this->getRequest()->setParam('id', 1);
- $this->getRequest()->setParam('form_key', $this->formKey->getFormKey());
+ $this->getRequest()->setParam('form_key', $this->formKey->getFormKey())->setMethod(HttpRequest::METHOD_POST);
// we are overwriting the address coming from the fixture
$this->dispatch('customer/address/delete');
@@ -183,13 +187,13 @@ public function testDeleteAction()
public function testWrongAddressDeleteAction()
{
$this->getRequest()->setParam('id', 555);
- $this->getRequest()->setParam('form_key', $this->formKey->getFormKey());
+ $this->getRequest()->setParam('form_key', $this->formKey->getFormKey())->setMethod(HttpRequest::METHOD_POST);
// we are overwriting the address coming from the fixture
$this->dispatch('customer/address/delete');
$this->assertRedirect($this->stringContains('customer/address/index'));
$this->assertSessionMessages(
- $this->equalTo(['We can\'t delete the address right now.']),
+ $this->equalTo(['We can't delete the address right now.']),
\Magento\Framework\Message\MessageInterface::TYPE_ERROR
);
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php
index 094cc46d42867..1cc421fd2973d 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php
@@ -7,7 +7,7 @@
use Magento\Framework\Message\MessageInterface;
use Magento\TestFramework\Helper\Bootstrap;
-use Magento\Framework\Data\Form\FormKey;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoAppArea adminhtml
@@ -26,6 +26,9 @@ class GroupTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle
/** @var \Magento\Customer\Api\GroupRepositoryInterface */
private $groupRepository;
+ /**
+ * @inheritDoc
+ */
public function setUp()
{
parent::setUp();
@@ -34,12 +37,9 @@ public function setUp()
$this->groupRepository = $objectManager->get(\Magento\Customer\Api\GroupRepositoryInterface::class);
}
- public function tearDown()
- {
- parent::tearDown();
- //$this->session->unsCustomerGroupData();
- }
-
+ /**
+ * Test new group form.
+ */
public function testNewActionNoCustomerGroupDataInSession()
{
$this->dispatch('backend/customer/group/new');
@@ -50,6 +50,9 @@ public function testNewActionNoCustomerGroupDataInSession()
$this->assertContains($expected, $responseBody);
}
+ /**
+ * Test form filling with data in session.
+ */
public function testNewActionWithCustomerGroupDataInSession()
{
/** @var \Magento\Customer\Api\Data\GroupInterfaceFactory $customerGroupFactory */
@@ -77,36 +80,27 @@ public function testNewActionWithCustomerGroupDataInSession()
}
/**
+ * Test calling delete without an ID.
+ *
* @magentoDataFixture Magento/Customer/_files/customer_group.php
*/
public function testDeleteActionNoGroupId()
{
- /** @var FormKey $formKey */
- $formKey = $this->_objectManager->get(FormKey::class);
-
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setParam('form_key', $formKey->getFormKey());
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/delete');
$this->assertRedirect($this->stringStartsWith(self::BASE_CONTROLLER_URL));
}
/**
+ * Test deleting a group.
+ *
* @magentoDataFixture Magento/Customer/_files/customer_group.php
*/
public function testDeleteActionExistingGroup()
{
$groupId = $this->findGroupIdWithCode(self::CUSTOMER_GROUP_CODE);
-
- /** @var FormKey $formKey */
- $formKey = $this->_objectManager->get(FormKey::class);
-
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setParams(
- [
- 'id' => $groupId,
- 'form_key' => $formKey->getFormKey()
- ]
- );
+ $this->getRequest()->setParam('id', $groupId);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/delete');
/**
@@ -120,20 +114,14 @@ public function testDeleteActionExistingGroup()
}
/**
+ * Tet deleting with wrong ID.
+ *
* @magentoDataFixture Magento/Customer/_files/customer_group.php
*/
public function testDeleteActionNonExistingGroupId()
{
- /** @var FormKey $formKey */
- $formKey = $this->_objectManager->get(FormKey::class);
-
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setParams(
- [
- 'id' => 10000,
- 'form_key' => $formKey->getFormKey()
- ]
- );
+ $this->getRequest()->setParam('id', 10000);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/delete');
/**
@@ -147,6 +135,8 @@ public function testDeleteActionNonExistingGroupId()
}
/**
+ * Test saving a valid group.
+ *
* @magentoDataFixture Magento/Customer/_files/customer_group.php
*/
public function testSaveActionExistingGroup()
@@ -155,6 +145,7 @@ public function testSaveActionExistingGroup()
$this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID);
$this->getRequest()->setParam('id', $groupId);
$this->getRequest()->setParam('code', self::CUSTOMER_GROUP_CODE);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/save');
@@ -186,9 +177,13 @@ public function testSaveActionExistingGroup()
);
}
+ /**
+ * Test saving an invalid group.
+ */
public function testSaveActionCreateNewGroupWithoutCode()
{
$this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/save');
@@ -198,19 +193,26 @@ public function testSaveActionCreateNewGroupWithoutCode()
);
}
+ /**
+ * Test saving an empty group.
+ */
public function testSaveActionForwardNewCreateNewGroup()
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/save');
$responseBody = $this->getResponse()->getBody();
$this->assertRegExp('/\s*New Customer Group\s*<\/h1>/', $responseBody);
}
/**
+ * Test saving an existing group.
+ *
* @magentoDataFixture Magento/Customer/_files/customer_group.php
*/
public function testSaveActionForwardNewEditExistingGroup()
{
$groupId = $this->findGroupIdWithCode(self::CUSTOMER_GROUP_CODE);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('id', $groupId);
$this->dispatch('backend/customer/group/save');
@@ -218,10 +220,14 @@ public function testSaveActionForwardNewEditExistingGroup()
$this->assertRegExp('/\s*' . self::CUSTOMER_GROUP_CODE . '\s*<\/h1>/', $responseBody);
}
+ /**
+ * Test using an invalid ID.
+ */
public function testSaveActionNonExistingGroupId()
{
$this->getRequest()->setParam('id', 10000);
$this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/save');
@@ -236,6 +242,8 @@ public function testSaveActionNonExistingGroupId()
}
/**
+ * Test using existing code.
+ *
* @magentoDataFixture Magento/Customer/_files/customer_group.php
*/
public function testSaveActionNewGroupWithExistingGroupCode()
@@ -245,6 +253,7 @@ public function testSaveActionNewGroupWithExistingGroupCode()
$this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID);
$this->getRequest()->setParam('code', self::CUSTOMER_GROUP_CODE);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/save');
@@ -257,6 +266,8 @@ public function testSaveActionNewGroupWithExistingGroupCode()
}
/**
+ * Test saving an invalid group.
+ *
* @magentoDataFixture Magento/Customer/_files/customer_group.php
*/
public function testSaveActionNewGroupWithoutGroupCode()
@@ -265,6 +276,7 @@ public function testSaveActionNewGroupWithoutGroupCode()
$originalCode = $this->groupRepository->getById($groupId)->getCode();
$this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/group/save');
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php
index 434e24b7d2771..3df07fbd4e1c0 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php
@@ -9,6 +9,7 @@
use Magento\Backend\Model\Session;
use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\Customer\Api\Data\CustomerInterface;
use Magento\Framework\Message\MessageInterface;
use Magento\TestFramework\Helper\Bootstrap;
@@ -31,12 +32,18 @@ class MassAssignGroupTest extends AbstractBackendController
*/
protected $customerRepository;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
parent::setUp();
$this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
}
+ /**
+ * @inheritDoc
+ */
protected function tearDown()
{
/**
@@ -73,8 +80,8 @@ public function testMassAssignGroupAction()
'form_key' => $formKey->getFormKey()
];
- $this->getRequest()->setParams($params);
- $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams($params)
+ ->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/massAssignGroup');
$this->assertSessionMessages(
self::equalTo(['A total of 1 record(s) were updated.']),
@@ -111,8 +118,8 @@ public function testLargeGroupMassAssignGroupAction()
'form_key' => $formKey->getFormKey()
];
- $this->getRequest()->setParams($params);
- $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams($params)
+ ->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/massAssignGroup');
$this->assertSessionMessages(
self::equalTo(['A total of 5 record(s) were updated.']),
@@ -141,9 +148,7 @@ public function testMassAssignGroupActionNoCustomerIds()
'namespace' => 'customer_listing',
'form_key' => $formKey->getFormKey()
];
-
- $this->getRequest()->setParams($params);
- $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/massAssignGroup');
$this->assertSessionMessages(
$this->equalTo(['Please select item(s).']),
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php
index 96e993932cb18..dc192c3c8681d 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\Constraint\Constraint;
use Magento\Framework\Message\MessageInterface;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\TestFramework\TestCase\AbstractBackendController;
/**
@@ -32,12 +33,18 @@ class MassDeleteTest extends AbstractBackendController
*/
private $baseControllerUrl = 'http://localhost/index.php/backend/customer/index/index';
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
parent::setUp();
$this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
}
+ /**
+ * @inheritDoc
+ */
protected function tearDown()
{
/**
@@ -110,8 +117,7 @@ private function massDeleteAssertions($ids, Constraint $constraint, $messageType
'form_key' => $formKey->getFormKey()
];
- $this->getRequest()->setParams($requestData);
- $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams($requestData)->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/massDelete');
$this->assertSessionMessages(
$constraint,
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
index b5ca783d68cf2..eaaba639d49a8 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Customer\Controller\Adminhtml\Index;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
/**
* ResetPassword controller test.
*
@@ -32,7 +34,7 @@ public function testResetPasswordSuccess()
$this->passwordResetRequestEventCreate(
\Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
);
- $this->getRequest()->setPostValue(['customer_id' => '1']);
+ $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertSessionMessages(
$this->equalTo(['The customer will receive an email with a link to reset password.']),
@@ -55,7 +57,7 @@ public function testResetPasswordMinTimeError()
$this->passwordResetRequestEventCreate(
\Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
);
- $this->getRequest()->setPostValue(['customer_id' => '1']);
+ $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertSessionMessages(
$this->equalTo(['The customer will receive an email with a link to reset password.']),
@@ -78,7 +80,7 @@ public function testResetPasswordLimitError()
$this->passwordResetRequestEventCreate(
\Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
);
- $this->getRequest()->setPostValue(['customer_id' => '1']);
+ $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertSessionMessages(
$this->equalTo(['The customer will receive an email with a link to reset password.']),
@@ -103,7 +105,7 @@ public function testResetPasswordWithSecurityViolationException()
$this->passwordResetRequestEventCreate(
\Magento\Security\Model\PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST
);
- $this->getRequest()->setPostValue(['customer_id' => '1']);
+ $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertSessionMessages(
$this->equalTo(['The customer will receive an email with a link to reset password.']),
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php
index ccf9c45da8660..4d5c46dff6221 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php
@@ -12,6 +12,7 @@
use Magento\Customer\Controller\RegistryConstants;
use Magento\Customer\Model\EmailNotification;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* @magentoAppArea adminhtml
@@ -85,7 +86,7 @@ protected function tearDown()
*/
public function testSaveActionWithEmptyPostData()
{
- $this->getRequest()->setPostValue([]);
+ $this->getRequest()->setPostValue([])->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/save');
$this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl));
}
@@ -96,43 +97,7 @@ public function testSaveActionWithEmptyPostData()
public function testSaveActionWithInvalidFormData()
{
$post = ['account' => ['middlename' => 'test middlename', 'group_id' => 1]];
- $this->getRequest()->setPostValue($post);
- $this->dispatch('backend/customer/index/save');
- /**
- * Check that errors was generated and set to session
- */
- $this->assertSessionMessages(
- $this->logicalNot($this->isEmpty()),
- \Magento\Framework\Message\MessageInterface::TYPE_ERROR
- );
- /**
- * Check that customer data were set to session
- */
- $this->assertEquals(
- $post,
- $this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerFormData()
- );
- $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new'));
- }
-
- /**
- * @magentoDbIsolation enabled
- */
- public function testSaveActionWithInvalidCustomerAddressData()
- {
- $post = [
- 'customer' => [
- 'middlename' => 'test middlename',
- 'group_id' => 1,
- 'website_id' => 0,
- 'firstname' => 'test firstname',
- 'lastname' => 'test lastname',
- 'email' => 'example@domain.com',
- 'default_billing' => '_item1',
- ],
- 'address' => ['_item1' => []],
- ];
- $this->getRequest()->setPostValue($post);
+ $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/save');
/**
* Check that errors was generated and set to session
@@ -141,13 +106,13 @@ public function testSaveActionWithInvalidCustomerAddressData()
$this->logicalNot($this->isEmpty()),
\Magento\Framework\Message\MessageInterface::TYPE_ERROR
);
+ /** @var \Magento\Backend\Model\Session $session */
+ $session = $this->objectManager->get(\Magento\Backend\Model\Session::class);
/**
* Check that customer data were set to session
*/
- $this->assertEquals(
- $post,
- $this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerFormData()
- );
+ $this->assertNotEmpty($session->getCustomerFormData());
+ $this->assertArraySubset($post, $session->getCustomerFormData());
$this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new'));
}
@@ -181,7 +146,7 @@ public function testSaveActionWithValidCustomerDataAndValidAddressData()
],
],
];
- $this->getRequest()->setPostValue($post);
+ $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('back', '1');
// Emulate setting customer data to session in editAction
@@ -293,7 +258,7 @@ public function testSaveActionExistingCustomerAndExistingAddressData()
],
'subscription' => '',
];
- $this->getRequest()->setPostValue($post);
+ $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('id', 1);
$this->dispatch('backend/customer/index/save');
@@ -359,7 +324,7 @@ public function testSaveActionExistingCustomerUnsubscribeNewsletter()
],
'subscription' => '0'
];
- $this->getRequest()->setPostValue($post);
+ $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('id', 1);
$this->dispatch('backend/customer/index/save');
@@ -397,7 +362,7 @@ public function testSaveActionExistingCustomerChangeEmail()
'change_email_template',
[
'name' => 'CustomerSupport',
- 'email' => 'support@example.com'
+ 'email' => 'support@example.com',
],
$customerId,
$newEmail
@@ -420,7 +385,7 @@ public function testSaveActionExistingCustomerChangeEmail()
'default_billing' => 1,
]
];
- $this->getRequest()->setPostValue($post);
+ $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('id', 1);
$this->dispatch('backend/customer/index/save');
@@ -447,7 +412,7 @@ public function testInlineEditChangeEmail()
'change_email_template',
[
'name' => 'CustomerSupport',
- 'email' => 'support@example.com'
+ 'email' => 'support@example.com',
],
$customerId,
$newEmail
@@ -467,7 +432,7 @@ public function testInlineEditChangeEmail()
]
];
$this->getRequest()->setParam('ajax', true)->setParam('isAjax', true);
- $this->getRequest()->setPostValue($post);
+ $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('id', 1);
$this->dispatch('backend/customer/index/inlineEdit');
@@ -493,7 +458,7 @@ public function testSaveActionCoreException()
'password' => 'password',
],
];
- $this->getRequest()->setPostValue($post);
+ $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/save');
/*
* Check that error message is set
@@ -502,7 +467,7 @@ public function testSaveActionCoreException()
$this->equalTo(['A customer with the same email already exists in an associated website.']),
\Magento\Framework\Message\MessageInterface::TYPE_ERROR
);
- $this->assertEquals(
+ $this->assertArraySubset(
$post,
Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData()
);
@@ -615,8 +580,7 @@ public function testNotExistingCustomerDeleteAction()
{
$this->getRequest()->setParam('id', 2);
$this->getRequest()->setParam('form_key', $this->formKey->getFormKey());
-
- $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/delete');
$this->assertRedirect($this->stringContains('customer/index'));
@@ -693,7 +657,7 @@ public function testValidateCustomerWithAddressSuccess()
/**
* set customer data
*/
- $this->getRequest()->setParams($customerData);
+ $this->getRequest()->setParams($customerData)->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/validate');
$body = $this->getResponse()->getBody();
@@ -747,7 +711,7 @@ public function testValidateCustomerWithAddressFailure()
/**
* set customer data
*/
- $this->getRequest()->setPostValue($customerData);
+ $this->getRequest()->setPostValue($customerData)->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/validate');
$body = $this->getResponse()->getBody();
@@ -764,6 +728,7 @@ public function testValidateCustomerWithAddressFailure()
public function testResetPasswordActionNoCustomerId()
{
// No customer ID in post, will just get redirected to base
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl));
}
@@ -774,6 +739,7 @@ public function testResetPasswordActionNoCustomerId()
public function testResetPasswordActionBadCustomerId()
{
// Bad customer ID in post, will just get redirected to base
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(['customer_id' => '789']);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl));
@@ -785,6 +751,7 @@ public function testResetPasswordActionBadCustomerId()
public function testResetPasswordActionSuccess()
{
$this->getRequest()->setPostValue(['customer_id' => '1']);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertSessionMessages(
$this->equalTo(['The customer will receive an email with a link to reset password.']),
@@ -825,7 +792,7 @@ protected function prepareEmailMock($occurrenceNumber, $templateId, $sender, $cu
'setTemplateIdentifier',
'setTemplateVars',
'setTemplateOptions',
- 'getTransport'
+ 'getTransport',
]
)
->getMock();
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/SendTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/SendTest.php
new file mode 100644
index 0000000000000..415591ac7d990
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/SendTest.php
@@ -0,0 +1,62 @@
+createMock(LoggerInterface::class);
+ $session = Bootstrap::getObjectManager()->create(
+ Session::class,
+ [$logger]
+ );
+ $this->accountManagement = Bootstrap::getObjectManager()->create(AccountManagementInterface::class);
+ $this->formKey = Bootstrap::getObjectManager()->create(FormKey::class);
+ $customer = $this->accountManagement->authenticate('customer@example.com', 'password');
+ $session->setCustomerDataAsLoggedIn($customer);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testExecutePost()
+ {
+ $this->getRequest()
+ ->setMethod('POST')
+ ->setPostValue(
+ [
+ 'form_key' => $this->formKey->getFormKey(),
+ 'emails' => 'example1@gmail.com, example2@gmail.com, example3@gmail.com'
+ ]
+ );
+
+ $this->dispatch('wishlist/index/send');
+ $this->assertRedirect($this->stringContains('wishlist/index/index'));
+ $this->assertSessionMessages(
+ $this->equalTo(['Your wish list has been shared.']),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php
index 2e132d27f5cb1..507150029c0c6 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php
@@ -14,6 +14,7 @@
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\State\ExpiredException;
use Magento\Framework\Reflection\DataObjectProcessor;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
/**
@@ -53,6 +54,9 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase
/** @var \Magento\Framework\Api\ExtensibleDataObjectConverter */
private $extensibleDataObjectConverter;
+ /** @var StoreManagerInterface */
+ private $storeManager;
+
/** @var \Magento\Framework\Api\DataObjectHelper */
protected $dataObjectHelper;
@@ -114,6 +118,9 @@ protected function setUp()
$this->extensibleDataObjectConverter = $this->objectManager
->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class);
+
+ $this->storeManager = $this->objectManager
+ ->create(StoreManagerInterface::class);
}
/**
@@ -1028,4 +1035,42 @@ protected function setResetPasswordData(
$customerModel->setRpTokenCreatedAt(date($date));
$customerModel->save();
}
+
+ /**
+ * Customer has two addresses one of it is allowed in website and second is not
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php
+ * @magentoDataFixture Magento/Store/_files/websites_different_countries.php
+ * @magentoConfigFixture fixture_second_store_store general/country/allow UA
+ * @return void
+ */
+ public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry()
+ {
+ $customerId = 1;
+ $allowedCountryIdForSecondWebsite = 'UA';
+ $store = $this->storeManager->getStore('fixture_second_store');
+ $customerData = $this->customerRepository->getById($customerId);
+ $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite)
+ ->setRegionId(null);
+ $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null);
+ $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class);
+ /** @var \Magento\Framework\Math\Random $mathRandom */
+ $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8);
+ $passwordHash = $encryptor->getHash($password, true);
+ $savedCustomer = $this->accountManagement->createAccountWithPasswordHash(
+ $customerData,
+ $passwordHash
+ );
+ $this->assertCount(
+ 1,
+ $savedCustomer->getAddresses(),
+ 'The wrong address quantity was saved'
+ );
+ $this->assertSame(
+ 'UA',
+ $savedCustomer->getAddresses()[0]->getCountryId(),
+ 'The address with the disallowed country was saved'
+ );
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses.php
new file mode 100644
index 0000000000000..a07010249319c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses.php
@@ -0,0 +1,74 @@
+create(CustomerRepositoryInterface::class);
+/** @var Customer $customer */
+$customer = $objectManager->create(Customer::class);
+/** @var CustomerRegistry $customerRegistry */
+$customerRegistry = $objectManager->get(CustomerRegistry::class);
+$customer->setWebsiteId(1)
+ ->setEmail('customer_with_addresses@test.com')
+ ->setPassword('password')
+ ->setGroupId(1)
+ ->setStoreId(1)
+ ->setIsActive(1)
+ ->setPrefix('Mr.')
+ ->setFirstname('John')
+ ->setMiddlename('A')
+ ->setLastname('Smith')
+ ->setSuffix('Esq.')
+ ->setDefaultBilling(1)
+ ->setDefaultShipping(1)
+ ->setTaxvat('12')
+ ->setGender(0);
+
+$customer->isObjectNew(true);
+$customer->save();
+$customerRegistry->remove($customer->getId());
+
+//Creating address
+/** @var Address $customerAddress */
+$customerAddress = $objectManager->create(Address::class);
+$customerAddress->isObjectNew(true);
+$customerAddress->setData(
+ [
+ 'attribute_set_id' => 2,
+ 'telephone' => 3468676,
+ 'postcode' => 75477,
+ 'country_id' => 'US',
+ 'city' => 'CityM',
+ 'company' => 'CompanyName',
+ 'street' => 'CustomerAddress1',
+ 'lastname' => 'Smith',
+ 'firstname' => 'John',
+ 'parent_id' => $customer->getId(),
+ 'region_id' => 1,
+ ]
+);
+$customerAddress->save();
+/** @var AddressRepositoryInterface $addressRepository */
+$addressRepository = $objectManager->get(AddressRepositoryInterface::class);
+$customerAddress = $addressRepository->getById($customerAddress->getId());
+$customerAddress->setCustomerId($customer->getId());
+$customerAddress->isDefaultBilling(true);
+$customerAddress->setIsDefaultShipping(true);
+$customerAddress = $addressRepository->save($customerAddress);
+$customerRegistry->remove($customerAddress->getCustomerId());
+/** @var AddressRegistry $addressRegistry */
+$addressRegistry = $objectManager->get(AddressRegistry::class);
+$addressRegistry->remove($customerAddress->getId());
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php
new file mode 100644
index 0000000000000..c3acf62cddefa
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php
@@ -0,0 +1,27 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var CustomerRepositoryInterface $customerRepo */
+$customerRepo = $objectManager->get(CustomerRepositoryInterface::class);
+try {
+ $customer = $customerRepo->get('customer_with_addresses@test.com');
+ $customerRepo->delete($customer);
+} catch (NoSuchEntityException $exception) {
+ //Already deleted
+}
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php
new file mode 100644
index 0000000000000..cde7569cc2467
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php
@@ -0,0 +1,29 @@
+get(\Magento\Framework\Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
+try {
+ $customer = $customerRepository->get('customer_two@example.com');
+ $customerRepository->delete($customer);
+} catch (NoSuchEntityException $e) {
+ /** Tests which are wrapped with MySQL transaction clear all data by transaction rollback. */
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php
new file mode 100644
index 0000000000000..dd1f917e34d5e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php
@@ -0,0 +1,252 @@
+dhlCarrier = $objectManager->create(
+ Carrier::class,
+ ['httpClientFactory' => $this->getHttpClientFactory()]
+ );
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ *
+ * @magentoConfigFixture default_store carriers/dhl/id CustomerSiteID
+ * @magentoConfigFixture default_store carriers/dhl/password CustomerPassword
+ *
+ * @param array $trackingNumbers
+ * @param string $responseXml
+ * @param array $expectedTrackingData
+ * @param string $expectedRequestXml
+ * @dataProvider getTrackingDataProvider
+ *
+ * @return void
+ */
+ public function testGetTracking(
+ array $trackingNumbers,
+ string $responseXml,
+ array $expectedTrackingData,
+ string $expectedRequestXml = ''
+ ) {
+ $this->httpResponseMock->method('getBody')
+ ->willReturn($responseXml);
+ $trackingResult = $this->dhlCarrier->getTracking($trackingNumbers);
+ $this->assertTrackingResult($expectedTrackingData, $trackingResult->getAllTrackings());
+ if ($expectedRequestXml !== '') {
+ $method = new \ReflectionMethod($this->httpClientMock, '_prepareBody');
+ $method->setAccessible(true);
+ $requestXml = $method->invoke($this->httpClientMock);
+ $this->assertRequest($expectedRequestXml, $requestXml);
+ }
+ }
+
+ /**
+ * Get tracking data provider.
+ *
+ * @return array
+ */
+ public function getTrackingDataProvider(): array
+ {
+ $expectedMultiAWBRequestXml = file_get_contents(__DIR__ . '/../_files/TrackingRequest_MultipleAWB.xml');
+ $multiAWBResponseXml = file_get_contents(__DIR__ . '/../_files/TrackingResponse_MultipleAWB.xml');
+ $expectedSingleAWBRequestXml = file_get_contents(__DIR__ . '/../_files/TrackingRequest_SingleAWB.xml');
+ $singleAWBResponseXml = file_get_contents(__DIR__ . '/../_files/TrackingResponse_SingleAWB.xml');
+ $singleNoDataResponseXml = file_get_contents(__DIR__ . '/../_files/SingleknownTrackResponse-no-data-found.xml');
+ $failedResponseXml = file_get_contents(__DIR__ . '/../_files/Track-res-XML-Parse-Err.xml');
+ $expectedTrackingDataA = [
+ 'carrier' => 'dhl',
+ 'carrier_title' => 'DHL',
+ 'tracking' => 4781584780,
+ 'service' => 'DOCUMENT',
+ 'progressdetail' => [
+ [
+ 'activity' => 'SD Shipment information received',
+ 'deliverydate' => '2017-12-25',
+ 'deliverytime' => '14:38:00',
+ 'deliverylocation' => 'BEIJING-CHN [PEK]',
+ ],
+ ],
+ 'weight' => '0.5 K',
+ ];
+ $expectedTrackingDataB = [
+ 'carrier' => 'dhl',
+ 'carrier_title' => 'DHL',
+ 'tracking' => 4781585060,
+ 'service' => 'NOT RESTRICTED FOR TRANSPORT,',
+ 'progressdetail' => [
+ [
+ 'activity' => 'SD Shipment information received',
+ 'deliverydate' => '2017-12-24',
+ 'deliverytime' => '13:35:00',
+ 'deliverylocation' => 'HONG KONG-HKG [HKG]',
+ ],
+ ],
+ 'weight' => '2.0 K',
+ ];
+ $expectedTrackingDataC = [
+ 'carrier' => 'dhl',
+ 'carrier_title' => 'DHL',
+ 'tracking' => 5702254250,
+ 'service' => 'CD',
+ 'progressdetail' => [
+ [
+ 'activity' => 'SD Shipment information received',
+ 'deliverydate' => '2017-12-24',
+ 'deliverytime' => '04:12:00',
+ 'deliverylocation' => 'BIRMINGHAM-GBR [BHX]',
+ ],
+ ],
+ 'weight' => '0.12 K',
+ ];
+ $expectedTrackingDataD = [
+ 'carrier' => 'dhl',
+ 'carrier_title' => 'DHL',
+ 'tracking' => 4781585060,
+ 'error_message' => __('Unable to retrieve tracking'),
+ ];
+ $expectedTrackingDataE = [
+ 'carrier' => 'dhl',
+ 'carrier_title' => 'DHL',
+ 'tracking' => 111,
+ 'error_message' => __(
+ 'Error #%1 : %2',
+ '111',
+ ' Error Parsing incoming request XML
+ Error: The content of element type
+ "ShipperReference" must match
+ "(ReferenceID,ReferenceType?)". at line
+ 16, column 22'
+ ),
+ ];
+
+ return [
+ 'multi-AWB' => [
+ ['4781584780', '4781585060', '5702254250'],
+ $multiAWBResponseXml,
+ [$expectedTrackingDataA, $expectedTrackingDataB, $expectedTrackingDataC],
+ $expectedMultiAWBRequestXml,
+ ],
+ 'single-AWB' => [
+ ['4781585060'],
+ $singleAWBResponseXml,
+ [$expectedTrackingDataB],
+ $expectedSingleAWBRequestXml,
+ ],
+ 'single-AWB-no-data' => [
+ ['4781585061'],
+ $singleNoDataResponseXml,
+ [$expectedTrackingDataD],
+ ],
+ 'failed-response' => [
+ ['4781585060-failed'],
+ $failedResponseXml,
+ [$expectedTrackingDataE],
+ ],
+ ];
+ }
+
+ /**
+ * Get mocked Http Client Factory.
+ *
+ * @return MockObject
+ */
+ private function getHttpClientFactory(): MockObject
+ {
+ $this->httpResponseMock = $this->getMockBuilder(\Zend_Http_Response::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->httpClientMock = $this->getMockBuilder(ZendClient::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['request'])
+ ->getMock();
+ $this->httpClientMock->method('request')
+ ->willReturn($this->httpResponseMock);
+ /** @var ZendClientFactory|MockObject $httpClientFactoryMock */
+ $httpClientFactoryMock = $this->getMockBuilder(ZendClientFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $httpClientFactoryMock->method('create')
+ ->willReturn($this->httpClientMock);
+
+ return $httpClientFactoryMock;
+ }
+
+ /**
+ * Assert request.
+ *
+ * @param string $expectedRequestXml
+ * @param string $requestXml
+ *
+ * @return void
+ */
+ private function assertRequest(string $expectedRequestXml, string $requestXml)
+ {
+ $expectedRequestElement = new Element($expectedRequestXml);
+ $requestElement = new Element($requestXml);
+ $requestMessageTime = $requestElement->Request->ServiceHeader->MessageTime->__toString();
+ $this->assertRegexp(
+ "/\d{4}\-\d{2}\-\d{2}T\d{2}\:\d{2}\:\d{2}\+\d{2}\:\d{2}/",
+ $requestMessageTime
+ );
+ $expectedRequestElement->Request->ServiceHeader->MessageTime = $requestMessageTime;
+ $messageReference = $requestElement->Request->ServiceHeader->MessageReference->__toString();
+ $this->assertStringStartsWith('MAGE_TRCK_', $messageReference);
+ $this->assertGreaterThanOrEqual(28, strlen($messageReference));
+ $this->assertLessThanOrEqual(32, strlen($messageReference));
+ $requestElement->Request->ServiceHeader->MessageReference = 'MAGE_TRCK_28TO32_Char_CHECKED';
+ $this->assertXmlStringEqualsXmlString($expectedRequestElement->asXML(), $requestElement->asXML());
+ }
+
+ /**
+ * Assert tracking.
+ *
+ * @param array|null $expectedTrackingData
+ * @param Status[]|null $trackingResults
+ *
+ * @return void
+ */
+ private function assertTrackingResult($expectedTrackingData, $trackingResults)
+ {
+ $ctr = 0;
+ foreach ($trackingResults as $trackingResult) {
+ $this->assertEquals($expectedTrackingData[$ctr++], $trackingResult->getData());
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/SingleknownTrackResponse-no-data-found.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/SingleknownTrackResponse-no-data-found.xml
new file mode 100644
index 0000000000000..80e7b42e4c534
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/SingleknownTrackResponse-no-data-found.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ 2018-02-27T12:59:34+01:00
+ 1234567890123456789012345678
+ CustomerSiteID
+
+
+
+ 4781585060
+
+ No Shipments Found
+
+ 209
+ No Shipments Found for AWBNumber 6017300993
+
+
+
+ String
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/Track-res-XML-Parse-Err.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/Track-res-XML-Parse-Err.xml
new file mode 100644
index 0000000000000..a3b4729fb21ae
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/Track-res-XML-Parse-Err.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+ 2018-02-27T12:55:05+01:00
+
+
+ Failure
+
+ 111
+ Error Parsing incoming request XML
+ Error: The content of element type
+ "ShipperReference" must match
+ "(ReferenceID,ReferenceType?)". at line
+ 16, column 22
+
+
+
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_MultipleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_MultipleAWB.xml
new file mode 100644
index 0000000000000..fefadf2d4ebde
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_MultipleAWB.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ 2002-06-25T11:28:56-08:00
+ MAGE_TRCK_28TO32_Char_CHECKED
+ CustomerSiteID
+ CustomerPassword
+
+
+ EN
+ 4781584780
+ 4781585060
+ 5702254250
+ ALL_CHECK_POINTS
+
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_SingleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_SingleAWB.xml
new file mode 100644
index 0000000000000..e9968e1464906
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_SingleAWB.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ 2002-06-25T11:28:56-08:00
+ MAGE_TRCK_28TO32_Char_CHECKED
+ CustomerSiteID
+ CustomerPassword
+
+
+ EN
+ 4781585060
+ ALL_CHECK_POINTS
+
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_MultipleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_MultipleAWB.xml
new file mode 100644
index 0000000000000..618bbb4de8e78
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_MultipleAWB.xml
@@ -0,0 +1,174 @@
+
+
+
+
+
+ 2018-02-27T12:43:44+01:00
+ 1234567890123456789012345678
+ CustomerSiteID
+
+
+
+ 4781584780
+
+ success
+
+
+
+ PEK
+ BEIJING-CHN
+
+
+ PHL
+ WEST PHILADELPHIA,PA-USA
+
+ THE EXP HIGH SCH ATT TO BNU
+ 123456789
+ HAVEFORD COLLEGE
+ 2017-12-25T14:38:00
+ 1
+ 0.5
+ K
+ D
+ DOCUMENT
+ Y
+
+ BEIJING
+ 100032
+ CN
+
+
+ HAVERFORD
+ PA
+ 19041
+ US
+
+
+ 2469
+
+
+ 2017-12-25
+ 14:38:00
+
+ SD
+ Shipment information received
+
+
+
+ PEK
+ BEIJING-CHN
+
+
+
+
+
+ 4781585060
+
+ success
+
+
+
+ HKG
+ HONG KONG-HKG
+
+
+ HKG
+ HONG KONG-HKG
+
+ NET-A-PORTER
+ 123456789
+ NICOLE LI
+ 2017-12-24T13:35:00
+ 1
+ 2.0
+ K
+ N
+ NOT RESTRICTED FOR TRANSPORT,
+ Y
+
+ HONG KONG
+ HK
+
+
+ HONG KONG
+ CH
+ HK
+
+
+ 1060571
+
+
+ 2017-12-24
+ 13:35:00
+
+ SD
+ Shipment information received
+
+
+
+ HKG
+ HONG KONG-HKG
+
+
+
+
+
+ 5702254250
+
+ success
+
+
+
+ BHX
+ BIRMINGHAM-GBR
+
+
+ AOI
+ ANCONA-ITA
+
+ AMAZON EU SARL
+ 123456789
+ MATTEO LOMBO
+ 2017-12-24T04:12:00
+ 1
+ 0.12
+ K
+ U
+ CD
+ Y
+
+ PETERBOROUGH
+ PE2 9EN
+ GB
+
+
+ ORTONA
+ 66026
+ IT
+
+
+ DGWYDy4xN_1
+
+
+ 2017-12-24
+ 04:12:00
+
+ SD
+ Shipment information received
+
+
+
+ BHX
+ BIRMINGHAM-GBR
+
+
+
+
+ en
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_SingleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_SingleAWB.xml
new file mode 100644
index 0000000000000..fa31b898b7a1f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_SingleAWB.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+ 2018-02-27T12:27:42+01:00
+ 1234567890123456789012345678
+ CustomerSiteID
+
+
+
+ 4781585060
+
+ success
+
+
+
+ HKG
+ HONG KONG-HKG
+
+
+ HKG
+ HONG KONG-HKG
+
+ NET-A-PORTER
+ 123456789
+ NICOLE LI
+ 2017-12-24T13:35:00
+ 1
+ 2.0
+ K
+ N
+ NOT RESTRICTED FOR TRANSPORT,
+ Y
+
+ HONG KONG
+ HK
+
+
+ HONG KONG
+ CH
+ HK
+
+
+ 1060571
+
+
+ 2017-12-24
+ 13:35:00
+
+ SD
+ Shipment information received
+
+
+
+ HKG
+ HONG KONG-HKG
+
+
+
+
+ en
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/CurrencyConfigTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/CurrencyConfigTest.php
index b620d9097b4be..10f2749ddace1 100644
--- a/dev/tests/integration/testsuite/Magento/Directory/Model/CurrencyConfigTest.php
+++ b/dev/tests/integration/testsuite/Magento/Directory/Model/CurrencyConfigTest.php
@@ -56,7 +56,7 @@ protected function setUp()
}
/**
- * Test get currency config for admin and storefront areas.
+ * Test get currency config for admin, crontab and storefront areas.
*
* @dataProvider getConfigCurrenciesDataProvider
* @magentoDataFixture Magento/Store/_files/store.php
@@ -77,7 +77,7 @@ public function testGetConfigCurrencies(string $areaCode, array $expected)
$storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
$storeManager->setCurrentStore($store->getId());
- if ($areaCode === Area::AREA_ADMINHTML) {
+ if (in_array($areaCode, [Area::AREA_ADMINHTML, Area::AREA_CRONTAB])) {
self::assertEquals($expected['allowed'], $this->currency->getConfigAllowCurrencies());
self::assertEquals($expected['base'], $this->currency->getConfigBaseCurrencies());
self::assertEquals($expected['default'], $this->currency->getConfigDefaultCurrencies());
@@ -118,6 +118,14 @@ public function getConfigCurrenciesDataProvider()
'default' => ['BDT', 'USD'],
],
],
+ [
+ 'areaCode' => Area::AREA_CRONTAB,
+ 'expected' => [
+ 'allowed' => ['BDT', 'BNS', 'BTD', 'EUR', 'USD'],
+ 'base' => ['BDT', 'USD'],
+ 'default' => ['BDT', 'USD'],
+ ],
+ ],
[
'areaCode' => Area::AREA_FRONTEND,
'expected' => [
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php
new file mode 100644
index 0000000000000..aaa3aa6c97a7e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php
@@ -0,0 +1,85 @@
+createMock(LoggerInterface::class);
+ $this->getTestFrameworkObjectManager()->addSharedInstance($loggerTestDouble, MagentoMonologLogger::class);
+ }
+
+ /**
+ * @after
+ */
+ public function removeLoggerTestDouble()
+ {
+ $this->getTestFrameworkObjectManager()->removeSharedInstance(MagentoMonologLogger::class);
+ }
+
+ /**
+ * @param \RuntimeException $testException
+ * @return Generator|MockObject
+ */
+ private function createExceptionThrowingGeneratorTestDouble(\RuntimeException $testException)
+ {
+ /** @var Generator|MockObject $generatorStub */
+ $generatorStub = $this->createMock(Generator::class);
+ $generatorStub->method('generateClass')->willThrowException($testException);
+
+ return $generatorStub;
+ }
+
+ public function testLogsExceptionDuringGeneration()
+ {
+ $exceptionMessage = 'Test exception thrown during generation';
+ $testException = new \RuntimeException($exceptionMessage);
+
+ $loggerMock = ObjectManager::getInstance()->get(LoggerInterface::class);
+ $loggerMock->expects($this->once())->method('debug')->with($exceptionMessage, ['exception' => $testException]);
+
+ $autoloader = new Autoloader($this->createExceptionThrowingGeneratorTestDouble($testException));
+ $this->assertNull($autoloader->load(NonExistingClassName::class));
+ }
+
+ public function testFiltersDuplicateExceptionMessages()
+ {
+ $exceptionMessage = 'Test exception thrown during generation';
+ $testException = new \RuntimeException($exceptionMessage);
+
+ $loggerMock = ObjectManager::getInstance()->get(LoggerInterface::class);
+ $loggerMock->expects($this->once())->method('debug')->with($exceptionMessage, ['exception' => $testException]);
+
+ $autoloader = new Autoloader($this->createExceptionThrowingGeneratorTestDouble($testException));
+ $autoloader->load(OneNonExistingClassName::class);
+ $autoloader->load(AnotherNonExistingClassName::class);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php
new file mode 100644
index 0000000000000..e64b3c505acf1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php
@@ -0,0 +1,55 @@
+objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->create(
+ \Magento\Framework\Lock\Backend\FileLock::class,
+ ['path' => '/tmp']
+ );
+ }
+
+ public function testLockAndUnlock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+
+ $this->assertTrue($this->model->lock($name));
+ $this->assertTrue($this->model->isLocked($name));
+ $this->assertFalse($this->model->lock($name, 2));
+
+ $this->assertTrue($this->model->unlock($name));
+ $this->assertFalse($this->model->isLocked($name));
+ }
+
+ public function testUnlockWithoutExistingLock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+ $this->assertFalse($this->model->unlock($name));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php
new file mode 100644
index 0000000000000..8d0caad5d55e4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php
@@ -0,0 +1,90 @@
+markTestSkipped('php extension Zookeeper is not installed.');
+ }
+
+ $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->configReader = $this->objectManager->get(FileReader::class);
+ $this->lockBackendFactory = $this->objectManager->create(LockBackendFactory::class);
+ $this->arrayManager = $this->objectManager->create(ArrayManager::class);
+ $config = $this->configReader->load(ConfigFilePool::APP_ENV);
+
+ if ($this->arrayManager->get('lock/provider', $config) !== 'zookeeper') {
+ $this->markTestSkipped('Zookeeper is not configured during installation.');
+ }
+
+ $this->model = $this->lockBackendFactory->create();
+ $this->assertInstanceOf(ZookeeperLock::class, $this->model);
+ }
+
+ public function testLockAndUnlock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+
+ $this->assertTrue($this->model->lock($name));
+ $this->assertTrue($this->model->isLocked($name));
+ $this->assertFalse($this->model->lock($name, 2));
+
+ $this->assertTrue($this->model->unlock($name));
+ $this->assertFalse($this->model->isLocked($name));
+ }
+
+ public function testUnlockWithoutExistingLock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+ $this->assertFalse($this->model->unlock($name));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php b/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php
index 8011873577dc8..4da0c12c6087a 100644
--- a/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php
+++ b/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php
@@ -7,6 +7,7 @@
namespace Magento\Integration\Controller\Adminhtml;
use Magento\TestFramework\Bootstrap;
+use Magento\Framework\App\Request\Http as HttpRequest;
/**
* \Magento\Integration\Controller\Adminhtml\Integration
@@ -20,6 +21,9 @@ class IntegrationTest extends \Magento\TestFramework\TestCase\AbstractBackendCon
/** @var \Magento\Integration\Model\Integration */
private $_integration;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
parent::setUp();
@@ -29,6 +33,9 @@ protected function setUp()
$this->_integration = $integration->load('Fixture Integration', 'name');
}
+ /**
+ * Test view page.
+ */
public function testIndexAction()
{
$this->dispatch('backend/admin/integration/index');
@@ -44,6 +51,9 @@ public function testIndexAction()
);
}
+ /**
+ * Test creation form.
+ */
public function testNewAction()
{
$this->dispatch('backend/admin/integration/new');
@@ -61,6 +71,9 @@ public function testNewAction()
);
}
+ /**
+ * Test update form.
+ */
public function testEditAction()
{
$integrationId = $this->_integration->getId();
@@ -88,12 +101,16 @@ public function testEditAction()
);
}
+ /**
+ * Test saving.
+ */
public function testSaveActionUpdateIntegration()
{
$integrationId = $this->_integration->getId();
$integrationName = $this->_integration->getName();
$this->getRequest()->setParam('id', $integrationId);
$url = 'http://magento.ll/endpoint_url';
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'name' => $integrationName,
@@ -111,10 +128,14 @@ public function testSaveActionUpdateIntegration()
$this->assertRedirect($this->stringContains('backend/admin/integration/index/'));
}
+ /**
+ * Test saving.
+ */
public function testSaveActionNewIntegration()
{
$url = 'http://magento.ll/endpoint_url';
$integrationName = md5(rand());
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'name' => $integrationName,
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php
new file mode 100644
index 0000000000000..48d3356525f49
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php
@@ -0,0 +1,105 @@
+objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+ $this->layout = $this->objectManager->create(\Magento\Framework\View\LayoutInterface::class);
+ $this->layout->getUpdate()->load('newsletter_subscriber_grid');
+ $this->layout->generateXml();
+ $this->layout->generateElements();
+ }
+
+ /**
+ * Check if mass action block exists.
+ */
+ public function testMassActionBlockExists()
+ {
+ $this->assertNotFalse(
+ $this->getMassActionBlock(),
+ 'Mass action block does not exist in the grid, or it name was changed.'
+ );
+ }
+
+ /**
+ * Check if mass action id field is correct.
+ */
+ public function testMassActionFieldIdIsCorrect()
+ {
+ $this->assertEquals(
+ 'subscriber_id',
+ $this->getMassActionBlock()->getMassactionIdField(),
+ 'Mass action id field is incorrect.'
+ );
+ }
+
+ /**
+ * Check if function returns correct result.
+ *
+ * @magentoDataFixture Magento/Newsletter/_files/subscribers.php
+ */
+ public function testMassActionBlockContainsCorrectIdList()
+ {
+ $this->assertEquals(
+ implode(',', $this->getAllSubscriberIdList()),
+ $this->getMassActionBlock()->getGridIdsJson(),
+ 'Function returns incorrect result.'
+ );
+ }
+
+ /**
+ * Retrieve mass action block.
+ *
+ * @return bool|\Magento\Backend\Block\Widget\Grid\Massaction
+ */
+ private function getMassActionBlock()
+ {
+ return $this->layout->getBlock('adminhtml.newslettrer.subscriber.grid.massaction');
+ }
+
+ /**
+ * Retrieve list of id of all subscribers.
+ *
+ * @return array
+ */
+ private function getAllSubscriberIdList()
+ {
+ /** @var \Magento\Framework\App\ResourceConnection $resourceConnection */
+ $resourceConnection = $this->objectManager->get(\Magento\Framework\App\ResourceConnection::class);
+ $select = $resourceConnection->getConnection()
+ ->select()
+ ->from($resourceConnection->getTableName('newsletter_subscriber'))
+ ->columns(['subscriber_id' => 'subscriber_id']);
+
+ return $resourceConnection->getConnection()->fetchCol($select);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php
index 5c1a11756c1b1..7a0ac030d120b 100644
--- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Newsletter\Controller\Adminhtml;
+use Magento\Framework\App\Request\Http as HttpRequest;
+
/**
* @magentoAppArea adminhtml
*/
@@ -15,6 +17,9 @@ class NewsletterQueueTest extends \Magento\TestFramework\TestCase\AbstractBacken
*/
protected $_model;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
parent::setUp();
@@ -23,6 +28,9 @@ protected function setUp()
);
}
+ /**
+ * @inheritDoc
+ */
protected function tearDown()
{
/**
@@ -47,6 +55,7 @@ public function testSaveActionQueueTemplateAndVerifySuccessMessage()
'subject' => 'test subject',
'text' => 'newsletter text',
];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postForQueue);
// Loading by code, since ID will vary. template_code is not actually used to load anywhere else.
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php
index 50e89d92e434c..4a5b190d789d7 100644
--- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php
@@ -20,6 +20,9 @@ class NewsletterTemplateTest extends \Magento\TestFramework\TestCase\AbstractBac
*/
protected $model;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
parent::setUp();
@@ -39,6 +42,9 @@ protected function setUp()
);
}
+ /**
+ * @inheritDoc
+ */
protected function tearDown()
{
/**
diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php
new file mode 100644
index 0000000000000..e2bb1d7b8f7c6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php
@@ -0,0 +1,135 @@
+ $paypalExpDate,
+ 'AMT' => '0.00',
+ 'RESPMSG' => 'Verified',
+ 'CVV2MATCH' => 'Y',
+ 'PNREF' => 'A10AAD866C87',
+ 'SECURETOKEN' => '3HYEHfG06skydAdBXbpIl8QJZ',
+ 'AVSDATA' => 'YNY',
+ 'RESULT' => '0',
+ 'IAVS' => 'N',
+ 'AVSADDR' => 'Y',
+ 'SECURETOKENID' => 'yqanLisRZbI0HAG8q3SbbKbhiwjNZAGf',
+ ];
+
+ $quote = $this->getQuote($reservedOrderId);
+ $this->getRequest()->setPostValue($postData);
+
+ /** @var Session $checkoutSession */
+ $checkoutSession = $this->_objectManager->get(GenericSession::class);
+ $checkoutSession->setQuoteId($quote->getId());
+ $this->setCurrentDateTime($currentDateTime);
+
+ $this->dispatch('paypal/transparent/response');
+
+ /** @var PaymentMethodManagementInterface $paymentManagment */
+ $paymentManagment = $this->_objectManager->get(PaymentMethodManagementInterface::class);
+ $payment = $paymentManagment->get($quote->getId());
+
+ $this->assertEquals($expectedCcMonth, $payment->getCcExpMonth());
+ $this->assertEquals($expectedCcYear, $payment->getCcExpYear());
+ }
+
+ /**
+ * @return array
+ */
+ public function paymentCcExpirationDateDataProvider(): array
+ {
+ return [
+ 'Expiration year in current century' => [
+ 'currentDateTime' => '2019-07-05 00:00:00',
+ 'paypalExpDate' => '0321',
+ 'expectedCcMonth' => 3,
+ 'expectedCcYear' => 2021
+ ],
+ 'Expiration year in next century' => [
+ 'currentDateTime' => '2099-01-01 00:00:00',
+ 'paypalExpDate' => '1002',
+ 'expectedCcMonth' => 10,
+ 'expectedCcYear' => 2102
+ ]
+ ];
+ }
+
+ /**
+ * Sets current date and time.
+ *
+ * @param string $date
+ */
+ private function setCurrentDateTime(string $dateTime)
+ {
+ $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
+ $dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dateTimeFactory->method('create')
+ ->willReturn($dateTime);
+
+ $this->_objectManager->addSharedInstance($dateTimeFactory, DateTimeFactory::class);
+ }
+
+ /**
+ * Gets quote by reserved order ID.
+ *
+ * @param string $reservedOrderId
+ * @return CartInterface
+ */
+ private function getQuote(string $reservedOrderId): CartInterface
+ {
+ $searchCriteria = $this->_objectManager->get(SearchCriteriaBuilder::class)
+ ->addFilter('reserved_order_id', $reservedOrderId)
+ ->create();
+
+ /** @var CartRepositoryInterface $quoteRepository */
+ $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
+ $items = $quoteRepository->getList($searchCriteria)
+ ->getItems();
+
+ return array_pop($items);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
index eb80da6d21b19..ee05e402bc689 100644
--- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
+++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
@@ -72,7 +72,7 @@ protected function setUp()
$this->api = $this->getMockBuilder(Nvp::class)
->disableOriginalConstructor()
- ->setMethods(['call', 'getExportedShippingAddress', 'getExportedBillingAddress'])
+ ->setMethods(['call', 'getExportedShippingAddress', 'getExportedBillingAddress', 'getShippingRateCode'])
->getMock();
$this->api->expects($this->any())
@@ -303,6 +303,7 @@ public function testReturnFromPaypal()
public function testReturnFromPaypalButton()
{
$quote = $this->getFixtureQuote();
+ $quote->getShippingAddress()->setShippingMethod('');
$this->prepareCheckoutModel($quote);
$quote->getPayment()->setAdditionalInformation(Checkout::PAYMENT_INFO_BUTTON, 1);
@@ -318,6 +319,8 @@ public function testReturnFromPaypalButton()
$this->assertEquals($exportedShippingData['telephone'], $shippingAddress->getTelephone());
$this->assertEquals($exportedShippingData['email'], $shippingAddress->getEmail());
+ $this->assertEquals('flatrate_flatrate', $shippingAddress->getShippingMethod());
+
$this->assertEquals([$exportedShippingData['street']], $billingAddress->getStreet());
$this->assertEquals($exportedShippingData['firstname'], $billingAddress->getFirstname());
$this->assertEquals($exportedShippingData['city'], $billingAddress->getCity());
@@ -512,6 +515,9 @@ private function prepareCheckoutModel(Quote $quote, $prefix = '')
$this->api->method('getExportedShippingAddress')
->will($this->returnValue($exportedShippingAddress));
+ $this->api->method('getShippingRateCode')
+ ->willReturn('flatrate_flatrate Flat Rate - Fixed');
+
$this->paypalInfo->method('importToPayment')
->with($this->api, $quote->getPayment());
}
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
index 3a516befc37ff..ce5180710728f 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
@@ -311,6 +311,26 @@ public function testAssignCustomerWithAddressChange()
}
}
+ /**
+ * Customer has address with country which not allowed in website
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ * @magentoDataFixture Magento/Backend/_files/allowed_countries_fr.php
+ * @return void
+ */
+ public function testAssignCustomerWithAddressChangeWithNotAllowedCountry()
+ {
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
+ $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote);
+ $quote->assignCustomerWithAddressChange($customerData);
+
+ /** Check that addresses are empty */
+ $this->assertNull($quote->getBillingAddress()->getCountryId());
+ $this->assertNull($quote->getShippingAddress()->getCountryId());
+ }
+
/**
* @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
*/
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php
new file mode 100644
index 0000000000000..7bfdb096673cc
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php
@@ -0,0 +1,97 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ }
+
+ /**
+ * Covers case when during quote item collection load product exists in db but not accessible.
+ *
+ * @magentoDataFixture Magento/Sales/_files/quote.php
+ * @return void
+ */
+ public function testLoadCollectionWithNotAccessibleProduct()
+ {
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
+ $quote->load('test01', 'reserved_order_id');
+
+ $this->assertCount(1, $quote->getItemsCollection());
+
+ $product = $this->productRepository->get('simple');
+ /** @var ProductCollection $productCollection */
+ $productCollection = $this->objectManager->create(ProductCollection::class);
+ $this->setPropertyValue($productCollection, '_isCollectionLoaded', true);
+ /** @var ProductCollectionFactory|\PHPUnit_Framework_MockObject_MockObject $productCollectionFactoryMock */
+ $productCollectionFactoryMock = $this->createMock(ProductCollectionFactory::class);
+ $productCollectionFactoryMock->expects($this->any())->method('create')->willReturn($productCollection);
+
+ /** @var QuoteItemCollection $quoteItemCollection */
+ $quoteItemCollection = $this->objectManager->create(
+ QuoteItemCollection::class,
+ [
+ 'productCollectionFactory' => $productCollectionFactoryMock,
+ ]
+ );
+
+ $quoteItemCollection->setQuote($quote);
+ $this->assertCount(1, $quoteItemCollection);
+ $item = $quoteItemCollection->getItemByColumnValue('product_id', $product->getId());
+
+ $this->assertNotNull($item);
+ $this->assertTrue($item->isDeleted());
+ }
+
+ /**
+ * Set object non-public property value.
+ *
+ * @param object $object
+ * @param string $propertyName
+ * @param mixed $value
+ * @return void
+ */
+ private function setPropertyValue($object, string $propertyName, $value)
+ {
+ $reflectionClass = new \ReflectionClass($object);
+ if ($reflectionClass->hasProperty($propertyName)) {
+ $reflectionProperty = $reflectionClass->getProperty($propertyName);
+ $reflectionProperty->setAccessible(true);
+ $reflectionProperty->setValue($object, $value);
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php b/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php
new file mode 100644
index 0000000000000..e0c38775101a3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php
@@ -0,0 +1,109 @@
+urlBuilder = $this->_objectManager->get(\Magento\Rss\Model\UrlBuilder::class);
+ $this->customerRepository = $this->_objectManager->get(
+ \Magento\Customer\Api\CustomerRepositoryInterface::class
+ );
+ $this->wishlist = $this->_objectManager->get(\Magento\Wishlist\Model\Wishlist::class);
+ $this->customerSession = $this->_objectManager->get(\Magento\Customer\Model\Session::class);
+ }
+
+ /**
+ * Check Rss response.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php
+ * @magentoConfigFixture current_store rss/wishlist/active 1
+ * @magentoConfigFixture current_store rss/config/active 1
+ * @return void
+ */
+ public function testRssResponse()
+ {
+ $customerEmail = 'customer@example.com';
+ $customer = $this->customerRepository->get($customerEmail);
+ $customerId = $customer->getId();
+ $this->customerSession->setCustomerId($customerId);
+ $wishlistId = $this->wishlist->loadByCustomerId($customerId)->getId();
+ $this->dispatch($this->getLink($customerId, $customerEmail, $wishlistId));
+ $body = $this->getResponse()->getBody();
+
+ $this->assertContains('John Smith\'s Wishlist', $body);
+ }
+
+ /**
+ * Check Rss with incorrect wishlist id.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php
+ * @magentoConfigFixture current_store rss/wishlist/active 1
+ * @magentoConfigFixture current_store rss/config/active 1
+ * @return void
+ */
+ public function testRssResponseWithIncorrectWishlistId()
+ {
+ $firstCustomerEmail = 'customer@example.com';
+ $secondCustomerEmail = 'customer_two@example.com';
+ $firstCustomer = $this->customerRepository->get($firstCustomerEmail);
+ $secondCustomer = $this->customerRepository->get($secondCustomerEmail);
+
+ $firstCustomerId = $firstCustomer->getId();
+ $secondCustomerId = $secondCustomer->getId();
+ $this->customerSession->setCustomerId($firstCustomerId);
+ $wishlistId = $this->wishlist->loadByCustomerId($secondCustomerId, true)->getId();
+ $this->dispatch($this->getLink($firstCustomerId, $firstCustomerEmail, $wishlistId));
+ $body = $this->getResponse()->getBody();
+
+ $this->assertContains('404 Not Found ', $body);
+ }
+
+ /**
+ * @param mixed $customerId
+ * @param string $customerEmail
+ * @param mixed $wishlistId
+ * @return string
+ */
+ private function getLink($customerId, string $customerEmail, $wishlistId): string
+ {
+ return 'rss/feed/index/type/wishlist/data/'
+ . base64_encode($customerId . ',' . $customerEmail)
+ . '/wishlist_id/' . $wishlistId;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php
new file mode 100644
index 0000000000000..1125fc1730718
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php
@@ -0,0 +1,59 @@
+layout = $this->_objectManager->get(LayoutInterface::class);
+ $this->block = $this->layout->createBlock(Totals::class, 'totals_block');
+ $this->orderFactory = $this->_objectManager->get(OrderFactory::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_free_shipping_by_coupon.php
+ */
+ public function testShowShippingCoupon()
+ {
+ /** @var Order $order */
+ $order = $this->orderFactory->create();
+ $order->loadByIncrementId('100000001');
+
+ $this->block->setOrder($order);
+ $this->block->toHtml();
+
+ $shippingTotal = $this->block->getTotal('shipping');
+ $this->assertNotFalse($shippingTotal, 'Shipping method is absent on the total\'s block.');
+ $this->assertContains(
+ '1234567890',
+ $shippingTotal->getLabel(),
+ 'Coupon code is absent in the shipping method label name.'
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php
index da0f2be856c51..26b5f58760da8 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php
@@ -78,6 +78,7 @@ public function testExecuteWithPaymentOperation()
'email' => $email,
]
];
+ $this->getRequest()->setMethod('POST');
$this->getRequest()->setPostValue(['order' => $data]);
/** @var OrderService|MockObject $orderService */
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
index 7110f39ee532c..f8e468ac42a32 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
@@ -43,6 +43,7 @@ protected function setUp()
public function testLoadBlockAction()
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('block', ',');
$this->getRequest()->setParam('json', 1);
$this->dispatch('backend/sales/order_create/loadBlock');
@@ -60,6 +61,7 @@ public function testLoadBlockActionData()
)->addProducts(
[$product->getId() => ['qty' => 1]]
);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('block', 'data');
$this->getRequest()->setParam('json', 1);
$this->dispatch('backend/sales/order_create/loadBlock');
@@ -135,6 +137,7 @@ public function testLoadBlockShippingMethod()
*/
public function testLoadBlockActions($block, $expected)
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('block', $block);
$this->getRequest()->setParam('json', 1);
$this->dispatch('backend/sales/order_create/loadBlock');
@@ -142,6 +145,9 @@ public function testLoadBlockActions($block, $expected)
$this->assertContains($expected, $html);
}
+ /**
+ * @return array
+ */
public function loadBlockActionsDataProvider()
{
return [
@@ -166,6 +172,7 @@ public function testLoadBlockActionItems()
)->addProducts(
[$product->getId() => ['qty' => 1]]
);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setParam('block', 'items');
$this->getRequest()->setParam('json', 1);
$this->dispatch('backend/sales/order_create/loadBlock');
@@ -308,6 +315,7 @@ public function testDeniedSaveAction()
\Magento\TestFramework\Helper\Bootstrap::getInstance()
->loadArea('adminhtml');
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/sales/order_create/save');
$this->assertEquals('403', $this->getResponse()->getHttpResponseCode());
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon.php
new file mode 100644
index 0000000000000..57ccffadaa4d0
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon.php
@@ -0,0 +1,35 @@
+create(OrderItem::class);
+$orderItem->setProductId($product->getId())
+ ->setQtyOrdered(2)
+ ->setBasePrice($product->getPrice())
+ ->setPrice($product->getPrice())
+ ->setRowTotal($product->getPrice())
+ ->setProductType('simple')
+ ->setName($product->getName())
+ ->setFreeShipping('1');
+
+/** @var Order $order */
+$order->setShippingDescription('Flat Rate - Fixed')
+ ->setShippingAmount(0)
+ ->setCouponCode('1234567890')
+ ->setDiscountDescription('1234567890')
+ ->addItem($orderItem);
+
+/** @var OrderRepositoryInterface $orderRepository */
+$orderRepository = $objectManager->create(OrderRepositoryInterface::class);
+$orderRepository->save($order);
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon_rollback.php
new file mode 100644
index 0000000000000..1fb4b4636ab29
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon_rollback.php
@@ -0,0 +1,8 @@
+get(ScopeConfigInterface::class);
+ $this->defaultTimezone = $scopeConfig->getValue(AdminBackendConfig::XML_PATH_GENERAL_LOCALE_TIMEZONE);
+
+ $this->collection = Bootstrap::getObjectManager()->create(Collection::class);
+ }
+
/**
* @magentoDataFixture Magento/SalesRule/_files/rules.php
* @magentoDataFixture Magento/SalesRule/_files/coupons.php
@@ -21,12 +48,8 @@ class CollectionTest extends \PHPUnit\Framework\TestCase
*/
public function testSetValidationFilter($couponCode, $expectedItems)
{
- $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
- $items = array_values($collection->setValidationFilter(1, 0, $couponCode)->getItems());
-
- $ids = [];
+ /** @var \Magento\SalesRule\Model\Rule[] $items */
+ $items = array_values($this->collection->setValidationFilter(1, 0, $couponCode)->getItems());
$this->assertEquals(
count($expectedItems),
@@ -34,6 +57,7 @@ public function testSetValidationFilter($couponCode, $expectedItems)
'Invalid number of items in the result collection'
);
+ $ids = [];
foreach ($items as $key => $item) {
$this->assertEquals($expectedItems[$key], $item->getName());
$this->assertFalse(
@@ -71,7 +95,7 @@ public function setValidationFilterDataProvider()
*/
public function testSetValidationFilterWithGroup()
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $objectManager = Bootstrap::getObjectManager();
/** @var \Magento\SalesRule\Model\Rule $rule */
$rule = $objectManager->get(\Magento\Framework\Registry::class)
@@ -82,13 +106,8 @@ public function testSetValidationFilterWithGroup()
$quote->load('test_order_item_with_items', 'reserved_order_id');
//gather only the existing rules that obey the validation filter
- /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */
- $ruleCollection = $objectManager->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
-
$appliedRulesArray = array_keys(
- $ruleCollection->setValidationFilter(
+ $this->collection->setValidationFilter(
$quote->getStore()->getWebsiteId(),
0,
'',
@@ -108,7 +127,7 @@ public function testSetValidationFilterWithGroup()
*/
public function testSetValidationFilterAnyCategory()
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $objectManager = Bootstrap::getObjectManager();
/** @var \Magento\SalesRule\Model\Rule $rule */
$rule = $objectManager->get(\Magento\Framework\Registry::class)
@@ -119,13 +138,8 @@ public function testSetValidationFilterAnyCategory()
$quote->load('test_order_item_with_items', 'reserved_order_id');
//gather only the existing rules that obey the validation filter
- /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */
- $ruleCollection = $objectManager->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
-
$appliedRulesArray = array_keys(
- $ruleCollection->setValidationFilter(
+ $this->collection->setValidationFilter(
$quote->getStore()->getWebsiteId(),
0,
'',
@@ -146,20 +160,15 @@ public function testSetValidationFilterAnyCategory()
*/
public function testSetValidationFilterOther()
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $objectManager = Bootstrap::getObjectManager();
/** @var \Magento\Quote\Model\Quote $quote */
$quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
//gather only the existing rules that obey the validation filter
- /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */
- $ruleCollection = $objectManager->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
-
$appliedRulesArray = array_keys(
- $ruleCollection->setValidationFilter(
+ $this->collection->setValidationFilter(
$quote->getStore()->getWebsiteId(),
0,
'',
@@ -181,11 +190,8 @@ public function testSetValidationFilterOther()
public function testMultiRulesWithTimezone()
{
$this->setSpecificTimezone('Europe/Kiev');
- $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
- $collection->addWebsiteGroupDateFilter(1, 0);
- $items = array_values($collection->getItems());
+ $this->collection->addWebsiteGroupDateFilter(1, 0);
+ $items = array_values($this->collection->getItems());
$this->assertNotEmpty($items);
}
@@ -200,11 +206,8 @@ public function testMultiRulesWithTimezone()
public function testMultiRulesWithDifferentTimezone()
{
$this->setSpecificTimezone('Australia/Sydney');
- $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
- $collection->addWebsiteGroupDateFilter(1, 0);
- $items = array_values($collection->getItems());
+ $this->collection->addWebsiteGroupDateFilter(1, 0);
+ $items = array_values($this->collection->getItems());
$this->assertNotEmpty($items);
}
@@ -224,7 +227,7 @@ protected function setSpecificTimezone($timezone)
]
]
];
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Config\Model\Config\Factory::class)
+ Bootstrap::getObjectManager()->get(\Magento\Config\Model\Config\Factory::class)
->create()
->addData($localeData)
->save();
@@ -239,11 +242,9 @@ protected function setSpecificTimezone($timezone)
*/
public function testAddAttributeInConditionFilterPositive()
{
- $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
- $collection->addAttributeInConditionFilter('attribute_for_sales_rule_1');
- $item = $collection->getFirstItem();
+ $this->collection->addAttributeInConditionFilter('attribute_for_sales_rule_1');
+ /** @var \Magento\SalesRule\Model\Rule $item */
+ $item = $this->collection->getFirstItem();
$this->assertEquals('50% Off on some attribute', $item->getName());
}
@@ -256,16 +257,57 @@ public function testAddAttributeInConditionFilterPositive()
*/
public function testAddAttributeInConditionFilterNegative()
{
- $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class
- );
- $collection->addAttributeInConditionFilter('attribute_for_sales_rule_2');
- $this->assertEquals(0, $collection->count());
+ $this->collection->addAttributeInConditionFilter('attribute_for_sales_rule_2');
+ $this->assertEquals(0, $this->collection->count());
+ }
+
+ /**
+ * @magentoAppIsolation disabled
+ * @magentoDataFixture Magento/SalesRule/_files/multi_websites_rules.php
+ * @dataProvider addWebsiteFilterDataProvider
+ * @param string[] $websiteCodes
+ * @param int $count
+ */
+ public function testAddWebsiteFilter(array $websiteCodes, int $count)
+ {
+ $websiteRepository = Bootstrap::getObjectManager()->get(WebsiteRepositoryInterface::class);
+ $websiteIds = [];
+ foreach ($websiteCodes as $websiteCode) {
+ $websiteIds[] = (int) $websiteRepository->get($websiteCode)->getId();
+ }
+
+ $this->collection->addWebsiteFilter($websiteIds);
+ $this->assertEquals($count, $this->collection->getSize());
+ $this->assertCount($count, $this->collection->getItems());
}
- public function tearDown()
+ /**
+ * @return array
+ */
+ public function addWebsiteFilterDataProvider(): array
+ {
+ return [
+ [
+ ['base'],
+ 4,
+ ],
+ [
+ ['test'],
+ 2,
+ ],
+ [
+ ['base', 'test'],
+ 5,
+ ],
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function tearDown()
{
// restore default timezone
- $this->setSpecificTimezone('America/Los_Angeles');
+ $this->setSpecificTimezone($this->defaultTimezone);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php
new file mode 100644
index 0000000000000..a43df3d67c077
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php
@@ -0,0 +1,16 @@
+setWebsiteIds($website->getId())
+ ->save();
+
+/** @var \Magento\SalesRule\Model\Rule $rule3 */
+$rule3->setWebsiteIds(implode(',', [1, $website->getId()]))
+ ->save();
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php
new file mode 100644
index 0000000000000..9e0e01b9fc51c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php
@@ -0,0 +1,8 @@
+create(\Magento\SalesRule\Model\Rule::class);
-$rule->setName(
+/** @var \Magento\SalesRule\Model\Rule $rule1 */
+$rule1 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
+$rule1->setName(
'#1'
)->setIsActive(
1
@@ -27,9 +27,9 @@
)->setSortOrder(1)
->save();
-/** @var \Magento\SalesRule\Model\Rule $rule */
-$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
-$rule->setName(
+/** @var \Magento\SalesRule\Model\Rule $rule2 */
+$rule2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
+$rule2->setName(
'#2'
)->setIsActive(
1
@@ -50,9 +50,9 @@
)->setSortOrder(2)
->save();
-/** @var \Magento\SalesRule\Model\Rule $rule */
-$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
-$rule->setName(
+/** @var \Magento\SalesRule\Model\Rule $rule3 */
+$rule3 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
+$rule3->setName(
'#3'
)->setIsActive(
1
@@ -73,9 +73,9 @@
)->setSortOrder(3)
->save();
-/** @var \Magento\SalesRule\Model\Rule $rule */
-$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
-$rule->setName(
+/** @var \Magento\SalesRule\Model\Rule $rule4 */
+$rule4 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
+$rule4->setName(
'#4'
)->setIsActive(
1
@@ -96,9 +96,9 @@
)->setSortOrder(4)
->save();
-/** @var \Magento\SalesRule\Model\Rule $rule */
-$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
-$rule->setName(
+/** @var \Magento\SalesRule\Model\Rule $rule5 */
+$rule5 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
+$rule5->setName(
'#5'
)->setIsActive(
1
diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php
new file mode 100644
index 0000000000000..d464c51050834
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php
@@ -0,0 +1,168 @@
+accountManagement = $this->_objectManager->create(AccountManagementInterface::class);
+ $this->formKey = $this->_objectManager->create(FormKey::class);
+ $logger = $this->createMock(LoggerInterface::class);
+ $this->session = $this->_objectManager->create(
+ Session::class,
+ [$logger]
+ );
+ $this->captchaHelper = $this->_objectManager->create(CaptchaHelper::class);
+ $customer = $this->accountManagement->authenticate('customer@example.com', 'password');
+ $this->session->setCustomerDataAsLoggedIn($customer);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ */
+ public function testExecute()
+ {
+ $this->getRequest()
+ ->setMethod('POST')
+ ->setPostValue(
+ [
+ 'form_key' => $this->formKey->getFormKey(),
+ 'sender' => [
+ 'name' => 'customer',
+ 'email' => 'customer@example.com',
+ 'message' => 'example message'
+ ],
+ 'id' => 1,
+ 'recipients' => [
+ 'name' => ['John'],
+ 'email' => ['example1@gmail.com']
+ ]
+
+ ]
+ );
+
+ $this->dispatch('sendfriend/product/sendmail');
+ $this->assertSessionMessages(
+ $this->equalTo(['The link to a friend was sent.']),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * @magentoConfigFixture default_store customer/captcha/enable 1
+ * @magentoConfigFixture default_store customer/captcha/failed_attempts_login 0
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoConfigFixture default_store customer/captcha/forms product_sendtofriend_form
+ */
+ public function testWithCaptchaFailed()
+ {
+ $this->getRequest()
+ ->setMethod('POST')
+ ->setPostValue(
+ [
+ 'form_key' => $this->formKey->getFormKey(),
+ 'sender' => [
+ 'name' => 'customer',
+ 'email' => 'customer@example.com',
+ 'message' => 'example message'
+ ],
+ 'id' => 1,
+ 'recipients' => [
+ 'name' => ['John'],
+ 'email' => ['example1@gmail.com']
+ ]
+
+ ]
+ );
+
+ $this->dispatch('sendfriend/product/sendmail');
+ $this->assertSessionMessages(
+ $this->equalTo(['Incorrect CAPTCHA']),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+
+ /**
+ * @magentoConfigFixture default_store customer/captcha/enable 1
+ * @magentoConfigFixture default_store customer/captcha/failed_attempts_login 0
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoConfigFixture default_store customer/captcha/forms product_sendtofriend_form
+ *
+ */
+ public function testWithCaptchaSuccess()
+ {
+ /** @var DefaultModel $captchaModel */
+ $captchaModel = $this->captchaHelper->getCaptcha('product_sendtofriend_form');
+ $captchaModel->generate();
+ $word = $captchaModel->getWord();
+ $this->getRequest()
+ ->setMethod('POST')
+ ->setPostValue(
+ [
+ 'form_key' => $this->formKey->getFormKey(),
+ 'sender' => [
+ 'name' => 'customer',
+ 'email' => 'customer@example.com',
+ 'message' => 'example message'
+ ],
+ 'id' => 1,
+ 'captcha' => [
+ 'product_sendtofriend_form' => $word
+ ],
+ 'recipients' => [
+ 'name' => ['John'],
+ 'email' => ['example1@gmail.com']
+ ]
+ ]
+ );
+
+ $this->dispatch('sendfriend/product/sendmail');
+ $this->assertSessionMessages(
+ $this->equalTo(['The link to a friend was sent.']),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
index bd6c900cf203c..4f9778febfb1c 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
@@ -9,10 +9,19 @@
namespace Magento\Tax\Model\Sales\Total\Quote;
use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Quote\Model\Quote;
use Magento\Tax\Model\Config;
use Magento\Tax\Model\Calculation;
+use Magento\Quote\Model\Quote\Item\Updater;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Framework\Api\Filter;
+use Magento\Framework\Api\Search\FilterGroup;
+use Magento\Framework\Api\SearchCriteriaInterface;
/**
+ * Setup utility for quote
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class SetupUtil
@@ -593,7 +602,7 @@ protected function createCartRule($ruleDataOverride)
*
* @param array $quoteData
* @param \Magento\Customer\Api\Data\CustomerInterface $customer
- * @return \Magento\Quote\Model\Quote
+ * @return Quote
*/
protected function createQuote($quoteData, $customer)
{
@@ -618,8 +627,8 @@ protected function createQuote($quoteData, $customer)
$quoteBillingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class);
$quoteBillingAddress->importCustomerAddressData($addressService->getById($billingAddress->getId()));
- /** @var \Magento\Quote\Model\Quote $quote */
- $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
$quote->setStoreId(1)
->setIsActive(true)
->setIsMultiShipping(false)
@@ -633,7 +642,7 @@ protected function createQuote($quoteData, $customer)
/**
* Add products to quote
*
- * @param \Magento\Quote\Model\Quote $quote
+ * @param Quote $quote
* @param array $itemsData
* @return $this
*/
@@ -656,7 +665,8 @@ protected function addProductToQuote($quote, $itemsData)
* Create a quote based on given data
*
* @param array $quoteData
- * @return \Magento\Quote\Model\Quote
+ *
+ * @return Quote
*/
public function setupQuote($quoteData)
{
@@ -665,7 +675,9 @@ public function setupQuote($quoteData)
$quote = $this->createQuote($quoteData, $customer);
$this->addProductToQuote($quote, $quoteData['items']);
-
+ if (isset($quoteData['update_items'])) {
+ $this->updateItems($quote, $quoteData['update_items']);
+ }
//Set shipping amount
if (isset($quoteData['shipping_method'])) {
$quote->getShippingAddress()->setShippingMethod($quoteData['shipping_method']);
@@ -682,4 +694,33 @@ public function setupQuote($quoteData)
return $quote;
}
+
+ /**
+ * Update quote items
+ *
+ * @param Quote $quote
+ * @param array $items
+ *
+ * @return void
+ */
+ private function updateItems(Quote $quote, array $items)
+ {
+ $updater = $this->objectManager->get(Updater::class);
+ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $filter = $this->objectManager->create(Filter::class);
+ $filter->setField('sku')->setValue(array_keys($items));
+ $filterGroup = $this->objectManager->create(FilterGroup::class);
+ $filterGroup->setFilters([$filter]);
+ $searchCriteria = $this->objectManager->create(SearchCriteriaInterface::class);
+ $searchCriteria->setFilterGroups([$filterGroup]);
+ $products = $productRepository->getList($searchCriteria)->getItems();
+ /** @var ProductInterface $product */
+ foreach ($products as $product) {
+ $quoteItem = $quote->getItemByProduct($product);
+ $updater->update(
+ $quoteItem,
+ $items[$product->getSku()]
+ );
+ }
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
index 0513dd1c7d3c4..ebf2c2eea9553 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
@@ -11,6 +11,7 @@
require_once __DIR__ . '/SetupUtil.php';
require_once __DIR__ . '/../../../../_files/tax_calculation_data_aggregated.php';
+require_once __DIR__ . '/../../../../_files/full_discount_with_tax.php';
/**
* Class TaxTest
@@ -124,6 +125,40 @@ public function testCollect()
);
}
+ /**
+ * Test taxes collection with full discount for quote.
+ *
+ * Test tax calculation and price when the discount may be bigger than total
+ * This method will test the collector through $quote->collectTotals() method
+ *
+ * @see \Magento\SalesRule\Model\Utility::deltaRoundingFix
+ * @magentoDataFixture Magento/Tax/_files/full_discount_with_tax.php
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ */
+ public function testFullDiscountWithDeltaRoundingFix()
+ {
+ global $fullDiscountIncTax;
+ $configData = $fullDiscountIncTax['config_data'];
+ $quoteData = $fullDiscountIncTax['quote_data'];
+ $expectedResults = $fullDiscountIncTax['expected_result'];
+
+ /** @var \Magento\Framework\ObjectManagerInterface $objectManager */
+ $objectManager = Bootstrap::getObjectManager();
+
+ //Setup tax configurations
+ $this->setupUtil = new SetupUtil($objectManager);
+ $this->setupUtil->setupTax($configData);
+
+ $quote = $this->setupUtil->setupQuote($quoteData);
+
+ $quote->collectTotals();
+
+ $quoteAddress = $quote->getShippingAddress();
+
+ $this->verifyResult($quoteAddress, $expectedResults);
+ }
+
/**
* Verify fields in quote item
*
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php
new file mode 100644
index 0000000000000..2b5ef07de341a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php
@@ -0,0 +1,120 @@
+ [
+ 'config_overrides' => [
+ Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0,
+ Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1,
+ Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION',
+ Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS,
+ ],
+ 'tax_rate_overrides' => [
+ SetupUtil::TAX_RATE_TX => 18,
+ SetupUtil::TAX_RATE_SHIPPING => 0,
+ ],
+ 'tax_rule_overrides' => [
+ [
+ 'code' => 'Product Tax Rule',
+ 'product_tax_class_ids' => [
+ SetupUtil::PRODUCT_TAX_CLASS_1
+ ],
+ ],
+ [
+ 'code' => 'Shipping Tax Rule',
+ 'product_tax_class_ids' => [
+ SetupUtil::SHIPPING_TAX_CLASS
+ ],
+ 'tax_rate_ids' => [
+ SetupUtil::TAX_RATE_SHIPPING,
+ ],
+ ],
+ ],
+ ],
+ 'quote_data' => [
+ 'billing_address' => [
+ 'region_id' => SetupUtil::REGION_TX,
+ ],
+ 'shipping_address' => [
+ 'region_id' => SetupUtil::REGION_TX,
+ ],
+ 'items' => [
+ [
+ 'sku' => 'simple1',
+ 'price' => 2542.37,
+ 'qty' => 2,
+ ]
+ ],
+ 'shipping_method' => 'free',
+ 'shopping_cart_rules' => [
+ [
+ 'discount_amount' => 100
+ ],
+ ],
+ ],
+ 'expected_result' => [
+ 'address_data' => [
+ 'subtotal' => 5084.74,
+ 'base_subtotal' => 5084.74,
+ 'subtotal_incl_tax' => 5999.99,
+ 'base_subtotal_incl_tax' => 5999.99,
+ 'tax_amount' => 915.25,
+ 'base_tax_amount' => 915.25,
+ 'shipping_amount' => 0,
+ 'base_shipping_amount' => 0,
+ 'shipping_incl_tax' => 0,
+ 'base_shipping_incl_tax' => 0,
+ 'shipping_tax_amount' => 0,
+ 'base_shipping_tax_amount' => 0,
+ 'discount_amount' => -5999.99,
+ 'base_discount_amount' => -5999.99,
+ 'discount_tax_compensation_amount' => 0,
+ 'base_discount_tax_compensation_amount' => 0,
+ 'shipping_discount_tax_compensation_amount' => 0,
+ 'base_shipping_discount_tax_compensation_amount' => 0,
+ 'grand_total' => 0,
+ 'base_grand_total' => 0,
+ 'applied_taxes' => [
+ SetupUtil::TAX_RATE_TX => [
+ 'percent' => 18,
+ 'amount' => 915.25,
+ 'base_amount' => 915.25,
+ 'rates' => [
+ [
+ 'code' => SetupUtil::TAX_RATE_TX,
+ 'title' => SetupUtil::TAX_RATE_TX,
+ 'percent' => 18,
+ ],
+ ],
+ ]
+ ],
+ ],
+ 'items_data' => [
+ 'simple1' => [
+ 'row_total' => 5084.74,
+ 'base_row_total' => 5084.74,
+ 'tax_percent' => 18,
+ 'price' => 2542.37,
+ 'base_price' => 2542.37,
+ 'price_incl_tax' => 3000,
+ 'base_price_incl_tax' => 3000,
+ 'row_total_incl_tax' => 5999.99,
+ 'base_row_total_incl_tax' => 5999.99,
+ 'tax_amount' => 915.25,
+ 'base_tax_amount' => 915.25,
+ 'discount_amount' => 5999.99,
+ 'base_discount_amount' => 5999.99,
+ 'discount_percent' => 100,
+ 'discount_tax_compensation_amount' => 0,
+ 'base_discount_tax_compensation_amount' => 0,
+ ],
+ ],
+ ]
+];
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_origin_price_with_custom_price.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_origin_price_with_custom_price.php
new file mode 100644
index 0000000000000..081b8e0a24620
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_origin_price_with_custom_price.php
@@ -0,0 +1,92 @@
+ [
+ SetupUtil::CONFIG_OVERRIDES => [
+ Config::CONFIG_XML_PATH_APPLY_ON => 1,
+ ],
+ SetupUtil::TAX_RATE_OVERRIDES => [
+ SetupUtil::TAX_RATE_TX => 8.25,
+ SetupUtil::TAX_STORE_RATE => 8.25,
+ ],
+ SetupUtil::TAX_RULE_OVERRIDES => [
+ ],
+ ],
+ 'quote_data' => [
+ 'billing_address' => [
+ 'region_id' => SetupUtil::REGION_TX,
+ ],
+ 'shipping_address' => [
+ 'region_id' => SetupUtil::REGION_TX,
+ ],
+ 'items' => [
+ [
+ 'sku' => 'simple1',
+ 'price' => 16.24,
+ 'qty' => 1,
+ ],
+ ],
+ 'update_items' => [
+ 'simple1' => [
+ 'custom_price' => 14,
+ 'qty' => 1,
+ ],
+ ],
+ ],
+ 'expected_results' => [
+ 'address_data' => [
+ 'subtotal' => 14,
+ 'base_subtotal' => 14,
+ 'subtotal_incl_tax' => 15.34,
+ 'base_subtotal_incl_tax' => 15.34,
+ 'tax_amount' => 1.34,
+ 'base_tax_amount' => 1.34,
+ 'shipping_amount' => 0,
+ 'base_shipping_amount' => 0,
+ 'shipping_incl_tax' => 0,
+ 'base_shipping_incl_tax' => 0,
+ 'shipping_taxable' => 0,
+ 'base_shipping_taxable' => 0,
+ 'shipping_tax_amount' => 0,
+ 'base_shipping_tax_amount' => 0,
+ 'discount_amount' => 0,
+ 'base_discount_amount' => 0,
+ 'discount_tax_compensation_amount' => 0,
+ 'base_discount_tax_compensation_amount' => 0,
+ 'shipping_discount_tax_compensation_amount' => 0,
+ 'base_shipping_discount_tax_compensation_amount' => 0,
+ 'grand_total' => 15.34,
+ 'base_grand_total' => 15.34,
+ ],
+ 'items_data' => [
+ 'simple1' => [
+ 'row_total' => 14,
+ 'base_row_total' => 14,
+ 'tax_percent' => 8.25,
+ 'price' => 14,
+ 'custom_price' => 14,
+ 'original_custom_price' => 14,
+ 'base_price' => 14,
+ 'price_incl_tax' => 15.34,
+ 'base_price_incl_tax' => 15.34,
+ 'row_total_incl_tax' => 15.34,
+ 'base_row_total_incl_tax' => 15.34,
+ 'tax_amount' => 1.34,
+ 'base_tax_amount' => 1.34,
+ 'discount_amount' => 0,
+ 'base_discount_amount' => 0,
+ 'discount_percent' => 0,
+ 'discount_tax_compensation_amount' => 0,
+ 'base_discount_tax_compensation_amount' => 0,
+ ],
+ ],
+ ],
+];
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_with_custom_price.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_with_custom_price.php
new file mode 100644
index 0000000000000..290c133f455f6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_with_custom_price.php
@@ -0,0 +1,93 @@
+ [
+ SetupUtil::CONFIG_OVERRIDES => [
+ Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX => 1,
+ Config::CONFIG_XML_PATH_APPLY_ON => 0,
+ ],
+ SetupUtil::TAX_RATE_OVERRIDES => [
+ SetupUtil::TAX_RATE_TX => 8.25,
+ SetupUtil::TAX_STORE_RATE => 8.25,
+ ],
+ SetupUtil::TAX_RULE_OVERRIDES => [
+ ],
+ ],
+ 'quote_data' => [
+ 'billing_address' => [
+ 'region_id' => SetupUtil::REGION_TX,
+ ],
+ 'shipping_address' => [
+ 'region_id' => SetupUtil::REGION_TX,
+ ],
+ 'items' => [
+ [
+ 'sku' => 'simple1',
+ 'price' => 16.24,
+ 'qty' => 1,
+ ],
+ ],
+ 'update_items' => [
+ 'simple1' => [
+ 'custom_price' => 14,
+ 'qty' => 1,
+ ],
+ ],
+ ],
+ 'expected_results' => [
+ 'address_data' => [
+ 'subtotal' => 12.93,
+ 'base_subtotal' => 12.93,
+ 'subtotal_incl_tax' => 14,
+ 'base_subtotal_incl_tax' => 14,
+ 'tax_amount' => 1.07,
+ 'base_tax_amount' => 1.07,
+ 'shipping_amount' => 0,
+ 'base_shipping_amount' => 0,
+ 'shipping_incl_tax' => 0,
+ 'base_shipping_incl_tax' => 0,
+ 'shipping_taxable' => 0,
+ 'base_shipping_taxable' => 0,
+ 'shipping_tax_amount' => 0,
+ 'base_shipping_tax_amount' => 0,
+ 'discount_amount' => 0,
+ 'base_discount_amount' => 0,
+ 'discount_tax_compensation_amount' => 0,
+ 'base_discount_tax_compensation_amount' => 0,
+ 'shipping_discount_tax_compensation_amount' => 0,
+ 'base_shipping_discount_tax_compensation_amount' => 0,
+ 'grand_total' => 14,
+ 'base_grand_total' => 14,
+ ],
+ 'items_data' => [
+ 'simple1' => [
+ 'row_total' => 12.93,
+ 'base_row_total' => 12.93,
+ 'tax_percent' => 8.25,
+ 'price' => 12.93,
+ 'custom_price' => 12.93,
+ 'original_custom_price' => 14,
+ 'base_price' => 12.93,
+ 'price_incl_tax' => 14,
+ 'base_price_incl_tax' => 14,
+ 'row_total_incl_tax' => 14,
+ 'base_row_total_incl_tax' => 14,
+ 'tax_amount' => 1.07,
+ 'base_tax_amount' => 1.07,
+ 'discount_amount' => 0,
+ 'base_discount_amount' => 0,
+ 'discount_percent' => 0,
+ 'discount_tax_compensation_amount' => 0,
+ 'base_discount_tax_compensation_amount' => 0,
+ ],
+ ],
+ ],
+];
diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php
index f22b48a259685..d3716ae260e0f 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php
@@ -3,14 +3,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
/**
- * Global array that holds test scenarios data
+ * Global array that holds test scenarios data.
*
* @var array
*/
$taxCalculationData = [];
-
+//phpcs:disable Magento2.Security.IncludeFile
require_once __DIR__ . '/scenarios/excluding_tax_apply_tax_after_discount.php';
require_once __DIR__ . '/scenarios/excluding_tax_apply_tax_after_discount_discount_tax.php';
require_once __DIR__ . '/scenarios/excluding_tax_apply_tax_before_discount.php';
@@ -31,3 +32,5 @@
require_once __DIR__ . '/scenarios/multi_tax_rule_two_row_calculate_subtotal_yes_row.php';
require_once __DIR__ . '/scenarios/multi_tax_rule_two_row_calculate_subtotal_yes_total.php';
require_once __DIR__ . '/scenarios/including_tax_apply_tax_after_discount.php';
+require_once __DIR__ . '/scenarios/including_tax_with_custom_price.php';
+require_once __DIR__ . '/scenarios/excluding_tax_apply_origin_price_with_custom_price.php';
diff --git a/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php b/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php
index 1ea2b28986d8a..1ceb15a63508c 100644
--- a/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php
+++ b/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php
@@ -8,6 +8,7 @@
use Magento\Framework\Data\Form\FormKey;
use Magento\TestFramework\TestCase\AbstractBackendController;
+use Magento\Framework\App\Request\Http;
/**
* Class SaveTest @covers \Magento\Theme\Controller\Adminhtml\Design\Config\Save
@@ -24,6 +25,11 @@ class SaveTest extends AbstractBackendController
*/
protected $uri = 'backend/theme/design_config/save';
+ /**
+ * @var string
+ */
+ protected $httpMethod = Http::METHOD_POST;
+
/**
* Test design configuration save valid values.
*
diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php
index edea65ee810ba..296eb60b9dac3 100644
--- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php
+++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php
@@ -57,6 +57,7 @@ public function testGetShipConfirmUrlLive()
* @magentoConfigFixture current_store carriers/ups/active 1
* @magentoConfigFixture current_store carriers/ups/allowed_methods 1DA,GND
* @magentoConfigFixture current_store carriers/ups/free_method GND
+ * @magentoConfigFixture current_store carriers/ups/type UPS
*/
public function testCollectFreeRates()
{
@@ -76,4 +77,29 @@ public function testCollectFreeRates()
$this->assertEquals(0, $methods['GND']['price']);
$this->assertNotEquals(0, $methods['1DA']['price']);
}
+
+ /**
+ * Check default UPS carrier parameters.
+ *
+ * @return void
+ */
+ public function testValidDefaultParameters()
+ {
+ $protocolType = $this->carrier->getConfigData('type');
+ $this->assertEquals("UPS_XML", $protocolType, "Default type should be UPS_XML");
+
+ $gatewayUrl = $this->carrier->getConfigData('gateway_url');
+ $this->assertEquals(
+ "https://www.ups.com/using/services/rave/qcostcgi.cgi",
+ $gatewayUrl,
+ "Incorrect gateway url"
+ );
+
+ $gatewayXmlUrl = $this->carrier->getConfigData('gateway_xml_url');
+ $this->assertEquals(
+ "https://onlinetools.ups.com/ups.app/xml/Rate",
+ $gatewayXmlUrl,
+ "Incorrect gateway XML url"
+ );
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php
new file mode 100644
index 0000000000000..b6055f14e79d2
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php
@@ -0,0 +1,71 @@
+urlFinder = Bootstrap::getObjectManager()->create(UrlFinderInterface::class);
+ }
+
+ /**
+ * @dataProvider findOneDataProvider
+ * @param string $requestPath
+ * @param string $targetPath
+ * @param int $redirectType
+ */
+ public function testFindOneByData(string $requestPath, string $targetPath, int $redirectType)
+ {
+ $data = [
+ UrlRewrite::REQUEST_PATH => $requestPath,
+ ];
+ $urlRewrite = $this->urlFinder->findOneByData($data);
+ $this->assertEquals($targetPath, $urlRewrite->getTargetPath());
+ $this->assertEquals($redirectType, $urlRewrite->getRedirectType());
+ }
+
+ /**
+ * @return array
+ */
+ public function findOneDataProvider(): array
+ {
+ return [
+ ['string', 'test_page1', 0],
+ ['string/', 'string', 301],
+ ['string_permanent', 'test_page1', 301],
+ ['string_permanent/', 'test_page1', 301],
+ ['string_temporary', 'test_page1', 302],
+ ['string_temporary/', 'test_page1', 302],
+ ['строка', 'test_page1', 0],
+ ['строка/', 'строка', 301],
+ [urlencode('строка'), 'test_page2', 0],
+ [urlencode('строка') . '/', urlencode('строка'), 301],
+ ['другая_строка', 'test_page1', 302],
+ ['другая_строка/', 'test_page1', 302],
+ [urlencode('другая_строка'), 'test_page1', 302],
+ [urlencode('другая_строка') . '/', 'test_page1', 302],
+ ['السلسلة', 'test_page1', 0],
+ [urlencode('السلسلة'), 'test_page1', 0],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php
new file mode 100644
index 0000000000000..9edc6507308ee
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php
@@ -0,0 +1,42 @@
+create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewrite::class);
+foreach ($rewritesData as $rewriteData) {
+ list ($requestPath, $targetPath, $redirectType) = $rewriteData;
+ $rewrite = $objectManager->create(\Magento\UrlRewrite\Model\UrlRewrite::class);
+ $rewrite->setEntityType('custom')
+ ->setRequestPath($requestPath)
+ ->setTargetPath($targetPath)
+ ->setRedirectType($redirectType);
+ $rewriteResource->save($rewrite);
+}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php
new file mode 100644
index 0000000000000..a98f947d614e0
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php
@@ -0,0 +1,20 @@
+get(\Magento\Framework\Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$urlRewriteCollection = $objectManager->create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection::class);
+$collection = $urlRewriteCollection
+ ->addFieldToFilter('target_path', ['test_page1', 'test_page2'])
+ ->load()
+ ->walk('delete');
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php
index cfed67b9ddbdb..e62b45862025f 100644
--- a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php
+++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php
@@ -5,6 +5,7 @@
*/
namespace Magento\User\Controller\Adminhtml;
+use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\TestFramework\Bootstrap;
/**
@@ -34,6 +35,7 @@ public function testIndexAction()
*/
public function testSaveActionNoData()
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/admin/user/save');
$this->assertRedirect($this->stringContains('backend/admin/user/index/'));
}
@@ -54,6 +56,7 @@ public function testSaveActionWrongId()
$userId = $user->getId();
$this->assertNotEmpty($userId, 'Broken fixture');
$user->delete();
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue('user_id', $userId);
$this->dispatch('backend/admin/user/save');
$this->assertSessionMessages(
@@ -71,6 +74,7 @@ public function testSaveActionWrongId()
public function testSaveActionMissingCurrentAdminPassword()
{
$fixture = uniqid();
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'username' => $fixture,
@@ -94,6 +98,7 @@ public function testSaveActionMissingCurrentAdminPassword()
public function testSaveAction()
{
$fixture = uniqid();
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'username' => $fixture,
@@ -121,6 +126,7 @@ public function testSaveAction()
*/
public function testSaveActionDuplicateUser()
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue(
[
'username' => 'adminUser',
@@ -149,6 +155,7 @@ public function testSaveActionDuplicateUser()
*/
public function testSaveActionPasswordChange($postData, $isPasswordCorrect)
{
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->getRequest()->setPostValue($postData);
$this->dispatch('backend/admin/user/save');
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php
new file mode 100644
index 0000000000000..47705262caaf3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php
@@ -0,0 +1,92 @@
+login(1);
+ $this->prepareRequestData();
+ $this->dispatch('wishlist/index/send/');
+
+ $this->assertSessionMessages(
+ $this->equalTo(['Your wish list has been shared.']),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * Test share wishlist with incorrect data
+ *
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist.php
+ */
+ public function testShareWishlistWithoutEmails()
+ {
+ $this->login(1);
+ $this->prepareRequestData(true);
+ $this->dispatch('wishlist/index/send/');
+
+ $this->assertSessionMessages(
+ $this->equalTo(['Please enter an email address.']),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+
+ /**
+ * Login the user
+ *
+ * @param string $customerId Customer to mark as logged in for the session
+ * @return void
+ */
+ protected function login($customerId)
+ {
+ /** @var Session $session */
+ $session = $this->_objectManager->get(Session::class);
+ $session->loginById($customerId);
+ }
+
+ /**
+ * Prepares the request with data
+ *
+ * @param bool $invalidData
+ * @return void
+ */
+ private function prepareRequestData($invalidData = false)
+ {
+ Bootstrap::getInstance()->loadArea(Area::AREA_FRONTEND);
+ $emails = !$invalidData ? 'email-1@example.com,email-2@example.com' : '';
+
+ /** @var FormKey $formKey */
+ $formKey = $this->_objectManager->get(FormKey::class);
+ $post = [
+ 'emails' => $emails,
+ 'message' => '',
+ 'form_key' => $formKey->getFormKey(),
+ ];
+
+ $this->getRequest()->setMethod(Request::METHOD_POST);
+ $this->getRequest()->setPostValue($post);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php
new file mode 100644
index 0000000000000..d12dc4a61f8f1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php
@@ -0,0 +1,25 @@
+create(\Magento\Customer\Api\CustomerRepositoryInterface::class);
+$firstCustomer = $customerRepository->get('customer@example.com');
+
+$wishlistForFirstCustomer = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class);
+$wishlistForFirstCustomer->loadByCustomerId($firstCustomer->getId(), true);
+$item = $wishlistForFirstCustomer->addNewItem($product, new \Magento\Framework\DataObject([]));
+$wishlistForFirstCustomer->save();
+
+$secondCustomer = $customerRepository->get('customer_two@example.com');
+$wishlistForSecondCustomer = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class);
+$wishlistForSecondCustomer->loadByCustomerId($secondCustomer->getId(), true);
+$item = $wishlistForSecondCustomer->addNewItem($product, new \Magento\Framework\DataObject([]));
+$wishlistForSecondCustomer->save();
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php
new file mode 100644
index 0000000000000..31cab3f9cbc74
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php
@@ -0,0 +1,39 @@
+get(\Magento\Framework\Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var \Magento\Wishlist\Model\Wishlist $wishlist */
+$wishlist = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class);
+
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = $objectManager->get(CustomerRepositoryInterface::class);
+try {
+ $firstCustomer = $customerRepository->get('customer@example.com');
+ $wishlist->loadByCustomerId($firstCustomer->getId());
+ $wishlist->delete();
+ $secondCustomer = $customerRepository->get('customer_two@example.com');
+ $wishlist->loadByCustomerId($secondCustomer->getId());
+ $wishlist->delete();
+} catch (NoSuchEntityException $e) {
+ /** Tests which are wrapped with MySQL transaction clear all data by transaction rollback. */
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+require __DIR__ . '/../../../Magento/Customer/_files/two_customers_rollback.php';
+require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php';
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js
index df6996afeb965..6062439db2365 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js
@@ -15,6 +15,18 @@ define([
describe('Magento_Braintree/js/view/payment/method-renderer/cc-form', function () {
var injector = new Squire(),
mocks = {
+ 'Magento_Checkout/js/model/checkout-data-resolver': {
+
+ /** Stub */
+ applyBillingAddress: function () {
+ return true;
+ },
+
+ /** Stub */
+ resolveBillingAddress: function () {
+ return true;
+ }
+ },
'Magento_Checkout/js/model/quote': {
billingAddress: ko.observable(),
shippingAddress: ko.observable(),
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js
index a2373cfb99091..d58c301e5d934 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js
@@ -14,6 +14,18 @@ define([
var injector = new Squire(),
mocks = {
+ 'Magento_Checkout/js/model/checkout-data-resolver': {
+
+ /** Stub */
+ applyBillingAddress: function () {
+ return true;
+ },
+
+ /** Stub */
+ resolveBillingAddress: function () {
+ return true;
+ }
+ },
'Magento_Checkout/js/model/quote': {
billingAddress: ko.observable(),
shippingAddress: ko.observable({
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js
index 12e12eb492c89..cc40386a7779d 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js
@@ -24,6 +24,18 @@ define([
return true;
}).and.callThrough(),
mocks = {
+ 'Magento_Checkout/js/model/checkout-data-resolver': {
+
+ /** Stub */
+ applyBillingAddress: function () {
+ return true;
+ },
+
+ /** Stub */
+ resolveBillingAddress: function () {
+ return true;
+ }
+ },
'Magento_Checkout/js/model/quote': {
billingAddress: ko.observable(),
shippingAddress: ko.observable(),
diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml
index 56a5a9e55c30e..fac0842214d92 100644
--- a/dev/tests/static/framework/Magento/ruleset.xml
+++ b/dev/tests/static/framework/Magento/ruleset.xml
@@ -25,5 +25,6 @@
+
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php
index 6fc84486c626b..2f1ab7a75bc83 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php
@@ -452,7 +452,7 @@ private function convertModuleToPackageName($moduleName)
{
list($vendor, $name) = explode('_', $moduleName, 2);
$package = 'module';
- foreach (preg_split('/([A-Z][a-z\d]+)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE) as $chunk) {
+ foreach (preg_split('/([A-Z\d][a-z]*)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE) as $chunk) {
$package .= $chunk ? "-{$chunk}" : '';
}
return strtolower("{$vendor}/{$package}");
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml
index 4790ce24fd87f..62406b6cbd30a 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml
@@ -69,5 +69,8 @@
-
dev/build/publication/sanity/ce.xml
+ -
+
dev/composer.lock
+
diff --git a/lib/internal/Magento/Framework/App/DocRootLocator.php b/lib/internal/Magento/Framework/App/DocRootLocator.php
index 6fb35c42f1330..d73baf8e4e742 100644
--- a/lib/internal/Magento/Framework/App/DocRootLocator.php
+++ b/lib/internal/Magento/Framework/App/DocRootLocator.php
@@ -3,10 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Framework\App;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\ReadFactory;
/**
@@ -20,18 +22,26 @@ class DocRootLocator
private $request;
/**
+ * @deprecated
* @var ReadFactory
*/
private $readFactory;
+ /**
+ * @var Filesystem
+ */
+ private $filesystem;
+
/**
* @param RequestInterface $request
* @param ReadFactory $readFactory
+ * @param Filesystem|null $filesystem
*/
- public function __construct(RequestInterface $request, ReadFactory $readFactory)
+ public function __construct(RequestInterface $request, ReadFactory $readFactory, Filesystem $filesystem = null)
{
$this->request = $request;
$this->readFactory = $readFactory;
+ $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class);
}
/**
@@ -42,7 +52,8 @@ public function __construct(RequestInterface $request, ReadFactory $readFactory)
public function isPub()
{
$rootBasePath = $this->request->getServer('DOCUMENT_ROOT');
- $readDirectory = $this->readFactory->create(DirectoryList::ROOT);
- return (substr($rootBasePath, -strlen('/pub')) === '/pub') && !$readDirectory->isExist($rootBasePath . 'setup');
+ $readDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT);
+
+ return (substr($rootBasePath, -\strlen('/pub')) === '/pub') && ! $readDirectory->isExist('setup');
}
}
diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php
index 3c6dee49f97b4..23024a44c2def 100644
--- a/lib/internal/Magento/Framework/App/Http.php
+++ b/lib/internal/Magento/Framework/App/Http.php
@@ -6,6 +6,7 @@
namespace Magento\Framework\App;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Debug;
use Magento\Framework\ObjectManager\ConfigLoaderInterface;
use Magento\Framework\App\Request\Http as RequestHttp;
use Magento\Framework\App\Response\Http as ResponseHttp;
@@ -79,7 +80,7 @@ class Http implements \Magento\Framework\AppInterface
* @param ResponseHttp $response
* @param ConfigLoaderInterface $configLoader
* @param State $state
- * @param Filesystem $filesystem,
+ * @param Filesystem $filesystem
* @param \Magento\Framework\Registry $registry
*/
public function __construct(
@@ -149,7 +150,7 @@ public function launch()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function catchException(Bootstrap $bootstrap, \Exception $exception)
{
@@ -198,6 +199,7 @@ private function buildContentFromException(\Exception $exception)
{
/** @var \Exception[] $exceptions */
$exceptions = [];
+
do {
$exceptions[] = $exception;
} while ($exception = $exception->getPrevious());
@@ -214,7 +216,12 @@ private function buildContentFromException(\Exception $exception)
$index,
get_class($exception),
$exception->getMessage(),
- $exception->getTraceAsString()
+ Debug::trace(
+ $exception->getTrace(),
+ true,
+ true,
+ (bool)getenv('MAGE_DEBUG_SHOW_ARGS')
+ )
);
}
@@ -312,7 +319,15 @@ private function handleInitException(\Exception $exception)
*/
private function handleGenericReport(Bootstrap $bootstrap, \Exception $exception)
{
- $reportData = [$exception->getMessage(), $exception->getTraceAsString()];
+ $reportData = [
+ $exception->getMessage(),
+ Debug::trace(
+ $exception->getTrace(),
+ true,
+ true,
+ (bool)getenv('MAGE_DEBUG_SHOW_ARGS')
+ )
+ ];
$params = $bootstrap->getParams();
if (isset($params['REQUEST_URI'])) {
$reportData['url'] = $params['REQUEST_URI'];
diff --git a/lib/internal/Magento/Framework/App/StaticResource.php b/lib/internal/Magento/Framework/App/StaticResource.php
index 87a2c37f94768..b5e4b2828d93b 100644
--- a/lib/internal/Magento/Framework/App/StaticResource.php
+++ b/lib/internal/Magento/Framework/App/StaticResource.php
@@ -10,6 +10,7 @@
use Magento\Framework\Filesystem;
use Magento\Framework\Config\ConfigOptionsListConstants;
use Psr\Log\LoggerInterface;
+use Magento\Framework\Debug;
/**
* Entry point for retrieving static resources like JS, CSS, images by requested public path
@@ -54,12 +55,12 @@ class StaticResource implements \Magento\Framework\AppInterface
private $objectManager;
/**
- * @var \Magento\Framework\ObjectManager\ConfigLoaderInterface
+ * @var ConfigLoaderInterface
*/
private $configLoader;
/**
- * @var \Magento\Framework\Filesystem
+ * @var Filesystem
*/
private $filesystem;
@@ -69,7 +70,7 @@ class StaticResource implements \Magento\Framework\AppInterface
private $deploymentConfig;
/**
- * @var \Psr\Log\LoggerInterface
+ * @var LoggerInterface
*/
private $logger;
@@ -138,7 +139,7 @@ public function launch()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function catchException(Bootstrap $bootstrap, \Exception $exception)
{
@@ -146,7 +147,15 @@ public function catchException(Bootstrap $bootstrap, \Exception $exception)
if ($bootstrap->isDeveloperMode()) {
$this->response->setHttpResponseCode(404);
$this->response->setHeader('Content-Type', 'text/plain');
- $this->response->setBody($exception->getMessage() . "\n" . $exception->getTraceAsString());
+ $this->response->setBody(
+ $exception->getMessage() . "\n" .
+ Debug::trace(
+ $exception->getTrace(),
+ true,
+ true,
+ (bool)getenv('MAGE_DEBUG_SHOW_ARGS')
+ )
+ );
$this->response->sendResponse();
} else {
require $this->getFilesystem()->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/404.php');
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php
index 23afbbc73d2b9..ef4152ba2e49e 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php
@@ -8,6 +8,9 @@
use Magento\Framework\App\DocRootLocator;
+/**
+ * Test for Magento\Framework\App\DocRootLocator class.
+ */
class DocRootLocatorTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -21,11 +24,15 @@ public function testIsPub($path, $isExist, $result)
{
$request = $this->createMock(\Magento\Framework\App\Request\Http::class);
$request->expects($this->once())->method('getServer')->willReturn($path);
+
+ $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class);
+
$reader = $this->createMock(\Magento\Framework\Filesystem\Directory\Read::class);
+ $filesystem = $this->createMock(\Magento\Framework\Filesystem::class);
+ $filesystem->expects($this->once())->method('getDirectoryRead')->willReturn($reader);
$reader->expects($this->any())->method('isExist')->willReturn($isExist);
- $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class);
- $readFactory->expects($this->once())->method('create')->willReturn($reader);
- $model = new DocRootLocator($request, $readFactory);
+
+ $model = new DocRootLocator($request, $readFactory, $filesystem);
$this->assertSame($result, $model->isPub());
}
diff --git a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php
index c214008393609..f8e469fe05265 100644
--- a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php
+++ b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php
@@ -3,37 +3,94 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\Code\Generator;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Code\Generator;
+use Psr\Log\LoggerInterface;
+/**
+ * Class loader and generator.
+ */
class Autoloader
{
/**
- * @var \Magento\Framework\Code\Generator
+ * @var Generator
*/
protected $_generator;
/**
- * @param \Magento\Framework\Code\Generator $generator
+ * Enables guarding against spamming the debug log with duplicate messages, as
+ * the generation exception will be thrown multiple times within a single request.
+ *
+ * @var string
+ */
+ private $lastGenerationErrorMessage;
+
+ /**
+ * @param Generator $generator
*/
- public function __construct(
- \Magento\Framework\Code\Generator $generator
- ) {
+ public function __construct(Generator $generator)
+ {
$this->_generator = $generator;
}
/**
* Load specified class name and generate it if necessary
*
+ * According to PSR-4 section 2.4 an autoloader MUST NOT throw an exception and SHOULD NOT return a value.
+ *
+ * @see https://www.php-fig.org/psr/psr-4/
+ *
* @param string $className
- * @return bool True if class was loaded
+ * @return void
*/
public function load($className)
{
- if (!class_exists($className)) {
- return Generator::GENERATION_ERROR != $this->_generator->generateClass($className);
+ if (! class_exists($className)) {
+ try {
+ $this->_generator->generateClass($className);
+ } catch (\Exception $exception) {
+ $this->tryToLogExceptionMessageIfNotDuplicate($exception);
+ }
+ }
+ }
+
+ /**
+ * Log exception.
+ *
+ * @param \Exception $exception
+ */
+ private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception)
+ {
+ if ($this->lastGenerationErrorMessage !== $exception->getMessage()) {
+ $this->lastGenerationErrorMessage = $exception->getMessage();
+ $this->tryToLogException($exception);
+ }
+ }
+
+ /**
+ * Try to capture the exception message.
+ *
+ * The Autoloader is instantiated before the ObjectManager, so the LoggerInterface can not be injected.
+ * The Logger is instantiated in the try/catch block because ObjectManager might still not be initialized.
+ * In that case the exception message can not be captured.
+ *
+ * The debug level is used for logging in case class generation fails for a common class, but a custom
+ * autoloader is used later in the stack. A more severe log level would fill the logs with messages on production.
+ * The exception message now can be accessed in developer mode if debug logging is enabled.
+ *
+ * @param \Exception $exception
+ * @return void
+ */
+ private function tryToLogException(\Exception $exception)
+ {
+ try {
+ $logger = ObjectManager::getInstance()->get(LoggerInterface::class);
+ $logger->debug($exception->getMessage(), ['exception' => $exception]);
+ } catch (\Exception $ignoreThisException) {
+ // Do not take an action here, since the original exception might have been caused by logger
}
- return true;
}
}
diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
index 3689aeef81e0b..fa468a6df2909 100644
--- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
+++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
@@ -2906,7 +2906,7 @@ public function prepareSqlCondition($fieldName, $condition)
if (isset($condition['to'])) {
$query .= empty($query) ? '' : ' AND ';
$to = $this->_prepareSqlDateCondition($condition, 'to');
- $query = $this->_prepareQuotedSqlCondition($query . $conditionKeyMap['to'], $to, $fieldName);
+ $query = $query . $this->_prepareQuotedSqlCondition($conditionKeyMap['to'], $to, $fieldName);
}
} elseif (array_key_exists($key, $conditionKeyMap)) {
$value = $condition[$key];
diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php
index 4e9132d49a2e2..099753ac1b56f 100644
--- a/lib/internal/Magento/Framework/Data/Collection.php
+++ b/lib/internal/Magento/Framework/Data/Collection.php
@@ -7,7 +7,6 @@
use Magento\Framework\Data\Collection\EntityFactoryInterface;
use Magento\Framework\Option\ArrayInterface;
-use Magento\Framework\Exception\InputException;
/**
* Data collection
@@ -235,20 +234,12 @@ protected function _setIsLoaded($flag = true)
* Get current collection page
*
* @param int $displacement
- * @throws \Magento\Framework\Exception\InputException
* @return int
*/
public function getCurPage($displacement = 0)
{
if ($this->_curPage + $displacement < 1) {
return 1;
- } elseif ($this->_curPage > $this->getLastPageNumber() && $displacement === 0) {
- throw new InputException(
- __(
- 'currentPage value %1 specified is greater than the %2 page(s) available.',
- [$this->_curPage, $this->getLastPageNumber()]
- )
- );
} elseif ($this->_curPage + $displacement > $this->getLastPageNumber()) {
return $this->getLastPageNumber();
} else {
diff --git a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php
index ac9cdd3822ec5..1b7e9ad990ce4 100644
--- a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php
+++ b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php
@@ -170,7 +170,11 @@ public function setId($id)
*/
public function getHtmlId()
{
- return $this->getForm()->getHtmlIdPrefix() . $this->getData('html_id') . $this->getForm()->getHtmlIdSuffix();
+ return $this->_escaper->escapeHtml(
+ $this->getForm()->getHtmlIdPrefix() .
+ $this->getData('html_id') .
+ $this->getForm()->getHtmlIdSuffix()
+ );
}
/**
@@ -184,7 +188,7 @@ public function getName()
if ($suffix = $this->getForm()->getFieldNameSuffix()) {
$name = $this->getForm()->addSuffixToName($name, $suffix);
}
- return $name;
+ return $this->_escaper->escapeHtml($name);
}
/**
diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Date.php b/lib/internal/Magento/Framework/Data/Form/Element/Date.php
index e762a641bfdcc..52d3eae7d66ac 100644
--- a/lib/internal/Magento/Framework/Data/Form/Element/Date.php
+++ b/lib/internal/Magento/Framework/Data/Form/Element/Date.php
@@ -49,6 +49,19 @@ public function __construct(
}
}
+ /**
+ * Check if a string is a date value
+ *
+ * @param string $value
+ * @return bool
+ */
+ private function isDate(string $value): bool
+ {
+ $date = date_parse($value);
+
+ return !empty($date['year']) && !empty($date['month']) && !empty($date['day']);
+ }
+
/**
* If script executes on x64 system, converts large
* numeric values to timestamp limit
@@ -85,9 +98,10 @@ public function setValue($value)
try {
if (preg_match('/^[0-9]+$/', $value)) {
$this->_value = (new \DateTime())->setTimestamp($this->_toTimestamp($value));
+ } else if (is_string($value) && $this->isDate($value)) {
+ $this->_value = new \DateTime($value, new \DateTimeZone($this->localeDate->getConfigTimezone()));
} else {
- $this->_value = new \DateTime($value);
- $this->_value->setTimezone(new \DateTimeZone($this->localeDate->getConfigTimezone()));
+ $this->_value = '';
}
} catch (\Exception $e) {
$this->_value = '';
diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Label.php b/lib/internal/Magento/Framework/Data/Form/Element/Label.php
index 901dcb5289e8d..70b7885e7a0d0 100644
--- a/lib/internal/Magento/Framework/Data/Form/Element/Label.php
+++ b/lib/internal/Magento/Framework/Data/Form/Element/Label.php
@@ -4,13 +4,13 @@
* See COPYING.txt for license details.
*/
-/**
- * Data form abstract class
- *
- * @author Magento Core Team
- */
namespace Magento\Framework\Data\Form\Element;
+use Magento\Framework\Phrase;
+
+/**
+ * Label form element.
+ */
class Label extends \Magento\Framework\Data\Form\Element\AbstractElement
{
/**
@@ -37,8 +37,13 @@ public function __construct(
public function getElementHtml()
{
$html = $this->getBold() ? '' : '
';
- $html .= $this->getEscapedValue() . '
';
+ if (is_string($this->getValue()) || $this->getValue() instanceof Phrase) {
+ $html .= $this->getEscapedValue();
+ }
+
+ $html .= '
';
$html .= $this->getAfterElementHtml();
+
return $html;
}
}
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php
index 0b6ddd6999d3e..a69f5af08a93c 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php
@@ -146,19 +146,6 @@ public function testGetCurPage()
$this->assertEquals(1, $this->_model->getCurPage());
}
- /**
- * Test for getCurPage with exception.
- *
- * @expectedException \Magento\Framework\Exception\InputException
- * @expectedExceptionMessage currentPage value 10 specified is greater than the 1 page(s) available.
- * @return void
- */
- public function testGetCurPageWithException()
- {
- $this->_model->setCurPage(10);
- $this->_model->getCurPage();
- }
-
/**
* Test for method possibleFlowWithItem.
*
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php
index a85c1f4aa450c..a207f45cb805a 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php
@@ -38,6 +38,7 @@ protected function setUp()
$this->_collectionFactoryMock =
$this->createMock(\Magento\Framework\Data\Form\Element\CollectionFactory::class);
$this->_escaperMock = $this->createMock(\Magento\Framework\Escaper::class);
+ $this->_escaperMock->method('escapeHtml')->willReturnArgument(0);
$this->_model = $this->getMockForAbstractClass(
\Magento\Framework\Data\Form\Element\AbstractElement::class,
@@ -423,9 +424,6 @@ public function testGetHtmlContainerIdWithFieldContainerIdPrefix()
*/
public function testAddElementValues(array $initialData, $expectedValue)
{
- $this->_escaperMock->expects($this->any())
- ->method('escapeHtml')
- ->will($this->returnArgument(0));
$this->_model->setValues($initialData['initial_values']);
$this->_model->addElementValues($initialData['add_values'], $initialData['overwrite']);
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php
index a2a40ee03b044..d347fed13ed65 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php
@@ -26,6 +26,7 @@ protected function setUp()
$factoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\Factory::class);
$collectionFactoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\CollectionFactory::class);
$escaperMock = $this->createMock(\Magento\Framework\Escaper::class);
+ $escaperMock->method('escapeHtml')->willReturnArgument(0);
$this->_link = new \Magento\Framework\Data\Form\Element\Link(
$factoryMock,
$collectionFactoryMock,
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php
index 6d1680a9f38a6..ed2b04e47b7a0 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php
@@ -7,6 +7,11 @@
class MultiselectTest extends \PHPUnit\Framework\TestCase
{
+ /**
+ * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $escaperMock;
+
/**
* @var \Magento\Framework\Data\Form\Element\Multiselect
*/
@@ -15,7 +20,12 @@ class MultiselectTest extends \PHPUnit\Framework\TestCase
protected function setUp()
{
$testHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
- $this->_model = $testHelper->getObject(\Magento\Framework\Data\Form\Element\Editablemultiselect::class);
+ $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class);
+ $this->escaperMock->method('escapeHtml')->willReturnArgument(0);
+ $this->_model = $testHelper->getObject(
+ \Magento\Framework\Data\Form\Element\Editablemultiselect::class,
+ ['escaper' => $this->escaperMock]
+ );
$this->_model->setForm(new \Magento\Framework\DataObject());
}
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php
index f77f4a816a1af..d58bc8639e82f 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php
@@ -26,6 +26,7 @@ protected function setUp()
$factoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\Factory::class);
$collectionFactoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\CollectionFactory::class);
$escaperMock = $this->createMock(\Magento\Framework\Escaper::class);
+ $escaperMock->method('escapeHtml')->willReturnArgument(0);
$this->_model = new \Magento\Framework\Data\Form\Element\Note(
$factoryMock,
$collectionFactoryMock,
diff --git a/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php b/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php
index a7a387b5def81..acd0a61633557 100644
--- a/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php
+++ b/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php
@@ -9,7 +9,12 @@
namespace Magento\Framework\Event\Invoker;
use Magento\Framework\Event\Observer;
+use Psr\Log\LoggerInterface;
+use Magento\Framework\App\State;
+/**
+ * Default Invoker.
+ */
class InvokerDefault implements \Magento\Framework\Event\InvokerInterface
{
/**
@@ -22,20 +27,29 @@ class InvokerDefault implements \Magento\Framework\Event\InvokerInterface
/**
* Application state
*
- * @var \Magento\Framework\App\State
+ * @var State
*/
protected $_appState;
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
/**
* @param \Magento\Framework\Event\ObserverFactory $observerFactory
- * @param \Magento\Framework\App\State $appState
+ * @param State $appState
+ * @param LoggerInterface $logger
*/
public function __construct(
\Magento\Framework\Event\ObserverFactory $observerFactory,
- \Magento\Framework\App\State $appState
+ State $appState,
+ LoggerInterface $logger = null
) {
$this->_observerFactory = $observerFactory;
$this->_appState = $appState;
+ $this->logger = $logger ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(LoggerInterface::class);
}
/**
@@ -61,6 +75,8 @@ public function dispatch(array $configuration, Observer $observer)
}
/**
+ * Execute Observer.
+ *
* @param \Magento\Framework\Event\ObserverInterface $object
* @param Observer $observer
* @return $this
@@ -70,7 +86,7 @@ protected function _callObserverMethod($object, $observer)
{
if ($object instanceof \Magento\Framework\Event\ObserverInterface) {
$object->execute($observer);
- } elseif ($this->_appState->getMode() == \Magento\Framework\App\State::MODE_DEVELOPER) {
+ } elseif ($this->_appState->getMode() == State::MODE_DEVELOPER) {
throw new \LogicException(
sprintf(
'Observer "%s" must implement interface "%s"',
@@ -78,6 +94,12 @@ protected function _callObserverMethod($object, $observer)
\Magento\Framework\Event\ObserverInterface::class
)
);
+ } else {
+ $this->logger->warning(sprintf(
+ 'Observer "%s" must implement interface "%s"',
+ get_class($object),
+ \Magento\Framework\Event\ObserverInterface::class
+ ));
}
return $this;
}
diff --git a/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php b/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php
index 37f650dbef6a0..e6ec123823854 100644
--- a/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php
+++ b/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Framework\Event\Test\Unit\Invoker;
+/**
+ * Test for Magento\Framework\Event\Invoker\InvokerDefault.
+ */
class InvokerDefaultTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -32,6 +35,11 @@ class InvokerDefaultTest extends \PHPUnit\Framework\TestCase
*/
protected $_invokerDefault;
+ /**
+ * @var |Psr\Log|LoggerInterface
+ */
+ private $loggerMock;
+
protected function setUp()
{
$this->_observerFactoryMock = $this->createMock(\Magento\Framework\Event\ObserverFactory::class);
@@ -41,10 +49,12 @@ protected function setUp()
['execute']
);
$this->_appStateMock = $this->createMock(\Magento\Framework\App\State::class);
+ $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class);
$this->_invokerDefault = new \Magento\Framework\Event\Invoker\InvokerDefault(
$this->_observerFactoryMock,
- $this->_appStateMock
+ $this->_appStateMock,
+ $this->loggerMock
);
}
@@ -166,13 +176,15 @@ public function testWrongInterfaceCallWithDisabledDeveloperMode($shared)
$this->returnValue($notObserver)
);
$this->_appStateMock->expects(
- $this->once()
+ $this->exactly(1)
)->method(
'getMode'
)->will(
$this->returnValue(\Magento\Framework\App\State::MODE_PRODUCTION)
);
+ $this->loggerMock->expects($this->once())->method('warning');
+
$this->_invokerDefault->dispatch(
[
'shared' => $shared,
diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php
index a2e772853efcb..62a00b3dc0123 100644
--- a/lib/internal/Magento/Framework/Filter/Template.php
+++ b/lib/internal/Magento/Framework/Filter/Template.php
@@ -9,6 +9,9 @@
*/
namespace Magento\Framework\Filter;
+use Magento\Framework\Model\AbstractExtensibleModel;
+use Magento\Framework\Model\AbstractModel;
+
/**
* @api
*/
@@ -52,6 +55,18 @@ class Template implements \Zend_Filter_Interface
*/
protected $string;
+ /**
+ * @var string[]
+ */
+ private $restrictedMethods = [
+ 'addafterfiltercallback',
+ 'getresourcecollection',
+ 'load',
+ 'save',
+ 'getcollection',
+ 'getresource'
+ ];
+
/**
* @param \Magento\Framework\Stdlib\StringUtils $string
* @param array $variables
@@ -297,6 +312,46 @@ protected function getParameters($value)
return $params;
}
+ /**
+ * Validate method call initiated in a template.
+ *
+ * Deny calls for methods that may disrupt template processing.
+ *
+ * @param object $object
+ * @param string $method
+ * @return void
+ * @throws \InvalidArgumentException
+ */
+ private function validateVariableMethodCall($object, string $method)
+ {
+ if ($object === $this) {
+ if (in_array(mb_strtolower($method), $this->restrictedMethods)) {
+ throw new \InvalidArgumentException("Method $method cannot be called from template.");
+ }
+ }
+ }
+
+ /**
+ * Check allowed methods for data objects.
+ *
+ * Deny calls for methods that may disrupt template processing.
+ *
+ * @param object $object
+ * @param string $method
+ * @return bool
+ * @throws \InvalidArgumentException
+ */
+ private function isAllowedDataObjectMethod($object, string $method): bool
+ {
+ if ($object instanceof AbstractExtensibleModel || $object instanceof AbstractModel) {
+ if (in_array(mb_strtolower($method), $this->restrictedMethods)) {
+ throw new \InvalidArgumentException("Method $method cannot be called from template.");
+ }
+ }
+
+ return true;
+ }
+
/**
* Return variable value for var construction
*
@@ -336,21 +391,27 @@ protected function getVariable($value, $default = '{no_value_defined}')
|| substr($stackVars[$i]['name'], 0, 3) == 'get'
) {
$stackVars[$i]['args'] = $this->getStackArgs($stackVars[$i]['args']);
- $stackVars[$i]['variable'] = call_user_func_array(
- [$stackVars[$i - 1]['variable'], $stackVars[$i]['name']],
- $stackVars[$i]['args']
- );
+
+ if ($this->isAllowedDataObjectMethod($stackVars[$i - 1]['variable'], $stackVars[$i]['name'])) {
+ $stackVars[$i]['variable'] = call_user_func_array(
+ [$stackVars[$i - 1]['variable'], $stackVars[$i]['name']],
+ $stackVars[$i]['args']
+ );
+ }
}
}
$last = $i;
- } elseif (isset($stackVars[$i - 1]['variable']) && $stackVars[$i]['type'] == 'method') {
+ } elseif (isset($stackVars[$i - 1]['variable'])
+ && is_object($stackVars[$i - 1]['variable'])
+ && $stackVars[$i]['type'] == 'method'
+ ) {
// Calling object methods
- if (method_exists($stackVars[$i - 1]['variable'], $stackVars[$i]['name'])) {
- $stackVars[$i]['args'] = $this->getStackArgs($stackVars[$i]['args']);
- $stackVars[$i]['variable'] = call_user_func_array(
- [$stackVars[$i - 1]['variable'], $stackVars[$i]['name']],
- $stackVars[$i]['args']
- );
+ $object = $stackVars[$i - 1]['variable'];
+ $method = $stackVars[$i]['name'];
+ if (method_exists($object, $method)) {
+ $args = $this->getStackArgs($stackVars[$i]['args']);
+ $this->validateVariableMethodCall($object, $method);
+ $stackVars[$i]['variable'] = call_user_func_array([$object, $method], $args);
}
$last = $i;
}
diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php
index e7376d9b7d264..a78c14eabc587 100644
--- a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php
+++ b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php
@@ -6,6 +6,8 @@
namespace Magento\Framework\Filter\Test\Unit;
+use Magento\Store\Model\Store;
+
class TemplateTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -13,10 +15,16 @@ class TemplateTest extends \PHPUnit\Framework\TestCase
*/
private $templateFilter;
+ /**
+ * @var Store
+ */
+ private $store;
+
protected function setUp()
{
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->templateFilter = $objectManager->getObject(\Magento\Framework\Filter\Template::class);
+ $this->store = $objectManager->getObject(Store::class);
}
public function testFilter()
@@ -205,4 +213,60 @@ public function varDirectiveDataProvider()
],
];
}
+
+ /**
+ * Test adding callbacks when already filtering.
+ *
+ * @expectedException \InvalidArgumentException
+ */
+ public function testInappropriateCallbacks()
+ {
+ $this->templateFilter->setVariables(['filter' => $this->templateFilter]);
+ $this->templateFilter->filter('Test {{var filter.addAfterFilterCallback(\'mb_strtolower\')}}');
+ }
+
+ /**
+ * Test adding callbacks when already filtering.
+ *
+ * @param string $method
+ * @dataProvider disallowedMethods
+ * @expectedException \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function testDisallowedMethods(string $method)
+ {
+ $this->templateFilter->setVariables(['store' => $this->store]);
+ $this->templateFilter->filter('{{var store.'.$method.'()}}');
+ }
+
+ /**
+ * Data for testDisallowedMethods method.
+ *
+ * @return array
+ */
+ public function disallowedMethods(): array
+ {
+ return [
+ ['getResourceCollection'],
+ ['load'],
+ ['save'],
+ ['getCollection'],
+ ['getResource'],
+ ];
+ }
+
+ /**
+ * Check that if calling a method of an object fails expected result is returned.
+ *
+ * @return void
+ */
+ public function testInvalidMethodCall()
+ {
+ $this->templateFilter->setVariables(['dateTime' => '\DateTime']);
+ $this->assertEquals(
+ '\DateTime',
+ $this->templateFilter->filter('{{var dateTime.createFromFormat(\'d\',\'1548201468\')}}')
+ );
+ }
}
diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php
index ca50cdb2440f4..adcffe01b910e 100644
--- a/lib/internal/Magento/Framework/Locale/Format.php
+++ b/lib/internal/Magento/Framework/Locale/Format.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Framework\Locale;
+/**
+ * Price locale format.
+ */
class Format implements \Magento\Framework\Locale\FormatInterface
{
/**
@@ -38,7 +41,8 @@ public function __construct(
}
/**
- * Returns the first found number from a string
+ * Returns the first found number from a string.
+ *
* Parsing depends on given locale (grouping and decimal)
*
* Examples for input:
@@ -100,7 +104,7 @@ public function getPriceFormat($localeCode = null, $currencyCode = null)
}
$formatter = new \NumberFormatter(
- $localeCode . '@currency=' . $currency->getCode(),
+ $currency->getCode() ? $localeCode . '@currency=' . $currency->getCode() : $localeCode,
\NumberFormatter::CURRENCY
);
$format = $formatter->getPattern();
diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php
index 8372908a380ff..abbfbdc5c6c37 100644
--- a/lib/internal/Magento/Framework/Locale/Resolver.php
+++ b/lib/internal/Magento/Framework/Locale/Resolver.php
@@ -6,7 +6,12 @@
namespace Magento\Framework\Locale;
use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\DeploymentConfig;
+use Magento\Framework\App\ObjectManager;
+/**
+ * Manages locale config information.
+ */
class Resolver implements ResolverInterface
{
/**
@@ -47,26 +52,34 @@ class Resolver implements ResolverInterface
*/
protected $emulatedLocales = [];
+ /**
+ * @var DeploymentConfig
+ */
+ private $deploymentConfig;
+
/**
* @param ScopeConfigInterface $scopeConfig
* @param string $defaultLocalePath
* @param string $scopeType
* @param mixed $locale
+ * @param DeploymentConfig|null $deploymentConfig
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
$defaultLocalePath,
$scopeType,
- $locale = null
+ $locale = null,
+ DeploymentConfig $deploymentConfig = null
) {
$this->scopeConfig = $scopeConfig;
$this->defaultLocalePath = $defaultLocalePath;
$this->scopeType = $scopeType;
+ $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->create(DeploymentConfig::class);
$this->setLocale($locale);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getDefaultLocalePath()
{
@@ -74,7 +87,7 @@ public function getDefaultLocalePath()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setDefaultLocale($locale)
{
@@ -83,12 +96,15 @@ public function setDefaultLocale($locale)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getDefaultLocale()
{
if (!$this->defaultLocale) {
- $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType);
+ $locale = false;
+ if ($this->deploymentConfig->isAvailable() && $this->deploymentConfig->isDbAvailable()) {
+ $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType);
+ }
if (!$locale) {
$locale = self::DEFAULT_LOCALE;
}
@@ -98,7 +114,7 @@ public function getDefaultLocale()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setLocale($locale = null)
{
@@ -111,7 +127,7 @@ public function setLocale($locale = null)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getLocale()
{
@@ -122,7 +138,7 @@ public function getLocale()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function emulate($scopeId)
{
@@ -142,7 +158,7 @@ public function emulate($scopeId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function revert()
{
diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php
index aa7ca377efa03..f6d7326f52764 100644
--- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php
+++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php
@@ -53,6 +53,7 @@ protected function setUp()
/** @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject $currencyFactory */
$currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class)
+ ->disableOriginalConstructor()
->getMock();
$this->formatModel = new \Magento\Framework\Locale\Format(
diff --git a/lib/internal/Magento/Framework/Lock/Backend/FileLock.php b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php
new file mode 100644
index 0000000000000..d168e910a4ab7
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php
@@ -0,0 +1,194 @@
+fileDriver = $fileDriver;
+ $this->path = rtrim($path, '/') . '/';
+
+ try {
+ if (!$this->fileDriver->isExists($this->path)) {
+ $this->fileDriver->createDirectory($this->path);
+ }
+ } catch (FileSystemException $exception) {
+ throw new RuntimeException(
+ new Phrase('Cannot create the directory for locks: %1', [$this->path]),
+ $exception
+ );
+ }
+ }
+
+ /**
+ * Acquires a lock by name
+ *
+ * @param string $name The lock name
+ * @param int $timeout Timeout in seconds. A negative timeout value means infinite timeout
+ * @return bool Returns true if the lock is acquired, otherwise returns false
+ * @throws RuntimeException Throws RuntimeException if cannot acquires the lock because FS problems
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+ try {
+ $lockFile = $this->getLockPath($name);
+ $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+');
+ $skipDeadline = $timeout < 0;
+ $deadline = microtime(true) + $timeout;
+
+ while (!$this->tryToLock($fileResource)) {
+ if (!$skipDeadline && $deadline <= microtime(true)) {
+ $this->fileDriver->fileClose($fileResource);
+ return false;
+ }
+ usleep($this->sleepCycle);
+ }
+ } catch (FileSystemException $exception) {
+ throw new RuntimeException(new Phrase('Cannot acquire a lock.'), $exception);
+ }
+
+ $this->locks[$lockFile] = $fileResource;
+ return true;
+ }
+
+ /**
+ * Checks if a lock exists by name
+ *
+ * @param string $name The lock name
+ * @return bool Returns true if the lock exists, otherwise returns false
+ * @throws RuntimeException Throws RuntimeException if cannot check that the lock exists
+ */
+ public function isLocked(string $name): bool
+ {
+ $lockFile = $this->getLockPath($name);
+ $result = false;
+
+ try {
+ if ($this->fileDriver->isExists($lockFile)) {
+ $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+');
+ if ($this->tryToLock($fileResource)) {
+ $result = false;
+ } else {
+ $result = true;
+ }
+ $this->fileDriver->fileClose($fileResource);
+ }
+ } catch (FileSystemException $exception) {
+ throw new RuntimeException(new Phrase('Cannot verify that the lock exists.'), $exception);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove the lock by name
+ *
+ * @param string $name The lock name
+ * @return bool If the lock is removed returns true, otherwise returns false
+ */
+ public function unlock(string $name): bool
+ {
+ $lockFile = $this->getLockPath($name);
+
+ if (isset($this->locks[$lockFile]) && $this->tryToUnlock($this->locks[$lockFile])) {
+ unset($this->locks[$lockFile]);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the full path to the lock file by name
+ *
+ * @param string $name The lock name
+ * @return string The path to the lock file
+ */
+ private function getLockPath(string $name): string
+ {
+ return $this->path . $name;
+ }
+
+ /**
+ * Tries to lock a file resource
+ *
+ * @param resource $resource The file resource
+ * @return bool If the lock is acquired returns true, otherwise returns false
+ */
+ private function tryToLock($resource): bool
+ {
+ try {
+ return $this->fileDriver->fileLock($resource, LOCK_EX | LOCK_NB);
+ } catch (FileSystemException $exception) {
+ return false;
+ }
+ }
+
+ /**
+ * Tries to unlock a file resource
+ *
+ * @param resource $resource The file resource
+ * @return bool If the lock is removed returns true, otherwise returns false
+ */
+ private function tryToUnlock($resource): bool
+ {
+ try {
+ return $this->fileDriver->fileLock($resource, LOCK_UN | LOCK_NB);
+ } catch (FileSystemException $exception) {
+ return false;
+ }
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php
new file mode 100644
index 0000000000000..cbba981ae1b51
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php
@@ -0,0 +1,280 @@
+\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']];
+
+ /**
+ * The mapping list of the lock name with the full lock path
+ *
+ * @var array
+ */
+ private $locks = [];
+
+ /**
+ * The default path to storage locks
+ */
+ const DEFAULT_PATH = '/magento/locks';
+
+ /**
+ * @param string $host The host to connect to Zookeeper
+ * @param string $path The base path to locks in Zookeeper
+ * @throws RuntimeException
+ */
+ public function __construct(string $host, string $path = self::DEFAULT_PATH)
+ {
+ if (!$path) {
+ throw new RuntimeException(
+ new Phrase('The path needs to be a non-empty string.')
+ );
+ }
+
+ if (!$host) {
+ throw new RuntimeException(
+ new Phrase('The host needs to be a non-empty string.')
+ );
+ }
+
+ $this->host = $host;
+ $this->path = rtrim($path, '/') . '/';
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * You can see the lock algorithm by the link
+ * @link https://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_recipes_Locks
+ *
+ * @throws RuntimeException
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+ $skipDeadline = $timeout < 0;
+ $lockPath = $this->getFullPathToLock($name);
+ $deadline = microtime(true) + $timeout;
+
+ if (!$this->checkAndCreateParentNode($lockPath)) {
+ throw new RuntimeException(new Phrase('Failed creating the path %1', [$lockPath]));
+ }
+
+ $lockKey = $this->getProvider()
+ ->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL | \Zookeeper::SEQUENCE);
+
+ if (!$lockKey) {
+ throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath]));
+ }
+
+ while ($this->isAnyLock($lockKey, $this->getIndex($lockKey))) {
+ if (!$skipDeadline && $deadline <= microtime(true)) {
+ $this->getProvider()->delete($lockKey);
+ return false;
+ }
+
+ usleep($this->sleepCycle);
+ }
+
+ $this->locks[$name] = $lockKey;
+
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function unlock(string $name): bool
+ {
+ if (!isset($this->locks[$name])) {
+ return false;
+ }
+
+ return $this->getProvider()->delete($this->locks[$name]);
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function isLocked(string $name): bool
+ {
+ return $this->isAnyLock($this->getFullPathToLock($name));
+ }
+
+ /**
+ * Gets full path to lock by its name
+ *
+ * @param string $name
+ * @return string
+ */
+ private function getFullPathToLock(string $name): string
+ {
+ return $this->path . $name . '/' . $this->lockName;
+ }
+
+ /**
+ * Initiolizes and returns Zookeeper provider
+ *
+ * @return \Zookeeper
+ * @throws RuntimeException
+ */
+ private function getProvider(): \Zookeeper
+ {
+ if (!$this->zookeeper) {
+ $this->zookeeper = new \Zookeeper($this->host);
+ }
+
+ $deadline = microtime(true) + $this->connectionTimeout;
+ while ($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) {
+ if ($deadline <= microtime(true)) {
+ throw new RuntimeException(new Phrase('Zookeeper connection timed out!'));
+ }
+ usleep($this->sleepCycle);
+ }
+
+ return $this->zookeeper;
+ }
+
+ /**
+ * Checks and creates base path recursively
+ *
+ * @param string $path
+ * @return bool
+ * @throws RuntimeException
+ */
+ private function checkAndCreateParentNode(string $path): bool
+ {
+ $path = dirname($path);
+ if ($this->getProvider()->exists($path)) {
+ return true;
+ }
+
+ if (!$this->checkAndCreateParentNode($path)) {
+ return false;
+ }
+
+ if ($this->getProvider()->create($path, '1', $this->acl)) {
+ return true;
+ }
+
+ return $this->getProvider()->exists($path);
+ }
+
+ /**
+ * Gets int increment of lock key
+ *
+ * @param string $key
+ * @return int|null
+ */
+ private function getIndex(string $key)
+ {
+ if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) {
+ return null;
+ }
+
+ return intval($matches[1]);
+ }
+
+ /**
+ * Checks if there is any sequence node under parent of $fullKey.
+ *
+ * At first checks that the $fullKey node is present, if not - returns false.
+ * If $indexKey is non-null and there is a smaller index than $indexKey then returns true,
+ * otherwise returns false.
+ *
+ * @param string $fullKey The full path without any sequence info
+ * @param int|null $indexKey The index to compare
+ * @return bool
+ * @throws RuntimeException
+ */
+ private function isAnyLock(string $fullKey, int $indexKey = null): bool
+ {
+ $parent = dirname($fullKey);
+
+ if (!$this->getProvider()->exists($parent)) {
+ return false;
+ }
+
+ $children = $this->getProvider()->getChildren($parent);
+
+ if (null === $indexKey && !empty($children)) {
+ return true;
+ }
+
+ foreach ($children as $childKey) {
+ $childIndex = $this->getIndex($childKey);
+
+ if (null === $childIndex) {
+ continue;
+ }
+
+ if ($childIndex < $indexKey) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php
new file mode 100644
index 0000000000000..b142085ef6563
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php
@@ -0,0 +1,111 @@
+ DatabaseLock::class,
+ self::LOCK_ZOOKEEPER => ZookeeperLock::class,
+ self::LOCK_CACHE => CacheLock::class,
+ self::LOCK_FILE => FileLock::class,
+ ];
+
+ /**
+ * @param ObjectManagerInterface $objectManager The Object Manager instance
+ * @param DeploymentConfig $deploymentConfig The Application deployment configuration
+ */
+ public function __construct(
+ ObjectManagerInterface $objectManager,
+ DeploymentConfig $deploymentConfig
+ ) {
+ $this->objectManager = $objectManager;
+ $this->deploymentConfig = $deploymentConfig;
+ }
+
+ /**
+ * Creates an instance of LockManagerInterface using information from deployment config
+ *
+ * @return LockManagerInterface
+ * @throws RuntimeException
+ */
+ public function create(): LockManagerInterface
+ {
+ $provider = $this->deploymentConfig->get('lock/provider', self::LOCK_DB);
+ $config = $this->deploymentConfig->get('lock/config', []);
+
+ if (!isset($this->lockers[$provider])) {
+ throw new RuntimeException(new Phrase('Unknown locks provider: %1', [$provider]));
+ }
+
+ if (self::LOCK_ZOOKEEPER === $provider && !extension_loaded(self::LOCK_ZOOKEEPER)) {
+ throw new RuntimeException(new Phrase('php extension Zookeeper is not installed.'));
+ }
+
+ return $this->objectManager->create($this->lockers[$provider], $config);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/LockManagerInterface.php b/lib/internal/Magento/Framework/Lock/LockManagerInterface.php
index 9df65f45adac3..76cc8506eb182 100644
--- a/lib/internal/Magento/Framework/Lock/LockManagerInterface.php
+++ b/lib/internal/Magento/Framework/Lock/LockManagerInterface.php
@@ -3,8 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-
declare(strict_types=1);
+
namespace Magento\Framework\Lock;
/**
diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php
new file mode 100644
index 0000000000000..2718bf6cb3456
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Proxy.php
@@ -0,0 +1,83 @@
+factory = $factory;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function isLocked(string $name): bool
+ {
+ return $this->getLocker()->isLocked($name);
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+ return $this->getLocker()->lock($name, $timeout);
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function unlock(string $name): bool
+ {
+ return $this->getLocker()->unlock($name);
+ }
+
+ /**
+ * Gets LockManagerInterface implementation using Factory
+ *
+ * @return LockManagerInterface
+ * @throws RuntimeException
+ */
+ private function getLocker(): LockManagerInterface
+ {
+ if (!$this->locker) {
+ $this->locker = $this->factory->create();
+ }
+
+ return $this->locker;
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php
new file mode 100644
index 0000000000000..62521b9de3082
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php
@@ -0,0 +1,68 @@
+markTestSkipped('Test was skipped because php extension Zookeeper is not installed.');
+ }
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\RuntimeException
+ * @expectedExceptionMessage The path needs to be a non-empty string.
+ * @return void
+ */
+ public function testConstructionWithPathException()
+ {
+ $this->zookeeperProvider = new ZookeeperProvider($this->host, '');
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\RuntimeException
+ * @expectedExceptionMessage The host needs to be a non-empty string.
+ * @return void
+ */
+ public function testConstructionWithHostException()
+ {
+ $this->zookeeperProvider = new ZookeeperProvider('', $this->path);
+ }
+
+ /**
+ * @return void
+ */
+ public function testConstructionWithoutException()
+ {
+ $this->zookeeperProvider = new ZookeeperProvider($this->host, $this->path);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php
new file mode 100644
index 0000000000000..ebf2f54f3e093
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php
@@ -0,0 +1,116 @@
+objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class);
+ $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class);
+ $this->factory = new LockBackendFactory($this->objectManagerMock, $this->deploymentConfigMock);
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\RuntimeException
+ * @expectedExceptionMessage Unknown locks provider: someProvider
+ */
+ public function testCreateWithException()
+ {
+ $this->deploymentConfigMock->expects($this->exactly(2))
+ ->method('get')
+ ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []])
+ ->willReturnOnConsecutiveCalls('someProvider', []);
+
+ $this->factory->create();
+ }
+
+ /**
+ * @param string $lockProvider
+ * @param string $lockProviderClass
+ * @param array $config
+ * @dataProvider createDataProvider
+ */
+ public function testCreate(string $lockProvider, string $lockProviderClass, array $config)
+ {
+ $lockManagerMock = $this->getMockForAbstractClass(LockManagerInterface::class);
+ $this->deploymentConfigMock->expects($this->exactly(2))
+ ->method('get')
+ ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []])
+ ->willReturnOnConsecutiveCalls($lockProvider, $config);
+ $this->objectManagerMock->expects($this->once())
+ ->method('create')
+ ->with($lockProviderClass, $config)
+ ->willReturn($lockManagerMock);
+
+ $this->assertSame($lockManagerMock, $this->factory->create());
+ }
+
+ /**
+ * @return array
+ */
+ public function createDataProvider(): array
+ {
+ $data = [
+ 'db' => [
+ 'lockProvider' => LockBackendFactory::LOCK_DB,
+ 'lockProviderClass' => DatabaseLock::class,
+ 'config' => ['prefix' => 'somePrefix'],
+ ],
+ 'cache' => [
+ 'lockProvider' => LockBackendFactory::LOCK_CACHE,
+ 'lockProviderClass' => CacheLock::class,
+ 'config' => [],
+ ],
+ 'file' => [
+ 'lockProvider' => LockBackendFactory::LOCK_FILE,
+ 'lockProviderClass' => FileLock::class,
+ 'config' => ['path' => '/my/path'],
+ ],
+ ];
+
+ if (extension_loaded('zookeeper')) {
+ $data['zookeeper'] = [
+ 'lockProvider' => LockBackendFactory::LOCK_ZOOKEEPER,
+ 'lockProviderClass' => ZookeeperLock::class,
+ 'config' => ['host' => 'some host'],
+ ];
+ }
+
+ return $data;
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php
new file mode 100644
index 0000000000000..c71dad701d715
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php
@@ -0,0 +1,106 @@
+factoryMock = $this->createMock(LockBackendFactory::class);
+ $this->lockerMock = $this->getMockForAbstractClass(LockManagerInterface::class);
+ $this->proxy = new Proxy($this->factoryMock);
+ }
+
+ /**
+ * @return void
+ */
+ public function testIsLocked()
+ {
+ $lockName = 'testLock';
+ $this->factoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->lockerMock);
+ $this->lockerMock->expects($this->exactly(2))
+ ->method('isLocked')
+ ->with($lockName)
+ ->willReturn(true);
+
+ $this->assertTrue($this->proxy->isLocked($lockName));
+
+ // Call one more time to check that method Factory::create is called one time
+ $this->assertTrue($this->proxy->isLocked($lockName));
+ }
+
+ /**
+ * @return void
+ */
+ public function testLock()
+ {
+ $lockName = 'testLock';
+ $timeout = 123;
+ $this->factoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->lockerMock);
+ $this->lockerMock->expects($this->exactly(2))
+ ->method('lock')
+ ->with($lockName, $timeout)
+ ->willReturn(true);
+
+ $this->assertTrue($this->proxy->lock($lockName, $timeout));
+
+ // Call one more time to check that method Factory::create is called one time
+ $this->assertTrue($this->proxy->lock($lockName, $timeout));
+ }
+
+ /**
+ * @return void
+ */
+ public function testUnlock()
+ {
+ $lockName = 'testLock';
+ $this->factoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->lockerMock);
+ $this->lockerMock->expects($this->exactly(2))
+ ->method('unlock')
+ ->with($lockName)
+ ->willReturn(true);
+
+ $this->assertTrue($this->proxy->unlock($lockName));
+
+ // Call one more time to check that method Factory::create is called one time
+ $this->assertTrue($this->proxy->unlock($lockName));
+ }
+}
diff --git a/lib/internal/Magento/Framework/Message/Manager.php b/lib/internal/Magento/Framework/Message/Manager.php
index 4e73b6112f9d8..d71e196deea88 100644
--- a/lib/internal/Magento/Framework/Message/Manager.php
+++ b/lib/internal/Magento/Framework/Message/Manager.php
@@ -8,6 +8,7 @@
use Magento\Framework\Event;
use Psr\Log\LoggerInterface;
use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Debug;
/**
* Message manager model
@@ -69,7 +70,7 @@ class Manager implements ManagerInterface
* @param Event\ManagerInterface $eventManager
* @param LoggerInterface $logger
* @param string $defaultGroup
- * @param ExceptionMessageFactoryInterface|null exceptionMessageFactory
+ * @param ExceptionMessageFactoryInterface|null $exceptionMessageFactory
*/
public function __construct(
Session $session,
@@ -91,7 +92,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getDefaultGroup()
{
@@ -112,8 +113,8 @@ protected function prepareGroup($group)
/**
* @inheritdoc
*
- * @param string|null $group
* @param bool $clear
+ * @param string|null $group
* @return Collection
*/
public function getMessages($clear = false, $group = null)
@@ -250,7 +251,12 @@ public function addException(\Exception $exception, $alternativeText = null, $gr
'Exception message: %s%sTrace: %s',
$exception->getMessage(),
"\n",
- $exception->getTraceAsString()
+ Debug::trace(
+ $exception->getTrace(),
+ true,
+ true,
+ (bool)getenv('MAGE_DEBUG_SHOW_ARGS')
+ )
);
$this->logger->critical($message);
@@ -288,7 +294,12 @@ public function addExceptionMessage(\Exception $exception, $alternativeText = nu
'Exception message: %s%sTrace: %s',
$exception->getMessage(),
"\n",
- $exception->getTraceAsString()
+ Debug::trace(
+ $exception->getTrace(),
+ true,
+ true,
+ (bool)getenv('MAGE_DEBUG_SHOW_ARGS')
+ )
);
$this->logger->critical($message);
diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php
index 0d0838b47d9cf..11393674012f3 100644
--- a/lib/internal/Magento/Framework/Session/SessionManager.php
+++ b/lib/internal/Magento/Framework/Session/SessionManager.php
@@ -12,6 +12,7 @@
/**
* Session Manager
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class SessionManager implements SessionManagerInterface
{
@@ -92,6 +93,11 @@ class SessionManager implements SessionManagerInterface
*/
private $appState;
+ /**
+ * @var SessionStartChecker
+ */
+ private $sessionStartChecker;
+
/**
* @param \Magento\Framework\App\Request\Http $request
* @param SidResolverInterface $sidResolver
@@ -102,7 +108,10 @@ class SessionManager implements SessionManagerInterface
* @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
* @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
* @param \Magento\Framework\App\State $appState
+ * @param SessionStartChecker|null $sessionStartChecker
* @throws \Magento\Framework\Exception\SessionException
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Framework\App\Request\Http $request,
@@ -113,7 +122,8 @@ public function __construct(
StorageInterface $storage,
\Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
\Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory,
- \Magento\Framework\App\State $appState
+ \Magento\Framework\App\State $appState,
+ SessionStartChecker $sessionStartChecker = null
) {
$this->request = $request;
$this->sidResolver = $sidResolver;
@@ -124,6 +134,9 @@ public function __construct(
$this->cookieManager = $cookieManager;
$this->cookieMetadataFactory = $cookieMetadataFactory;
$this->appState = $appState;
+ $this->sessionStartChecker = $sessionStartChecker ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
+ SessionStartChecker::class
+ );
// Enable session.use_only_cookies
ini_set('session.use_only_cookies', '1');
@@ -131,7 +144,8 @@ public function __construct(
}
/**
- * This method needs to support sessions with APC enabled
+ * This method needs to support sessions with APC enabled.
+ *
* @return void
*/
public function writeClose()
@@ -166,47 +180,50 @@ public function __call($method, $args)
*/
public function start()
{
- if (!$this->isSessionExists()) {
- \Magento\Framework\Profiler::start('session_start');
-
- try {
- $this->appState->getAreaCode();
- } catch (\Magento\Framework\Exception\LocalizedException $e) {
- throw new \Magento\Framework\Exception\SessionException(
- new \Magento\Framework\Phrase(
- 'Area code not set: Area code must be set before starting a session.'
- ),
- $e
- );
- }
-
- // Need to apply the config options so they can be ready by session_start
- $this->initIniOptions();
- $this->registerSaveHandler();
- if (isset($_SESSION['new_session_id'])) {
- // Not fully expired yet. Could be lost cookie by unstable network.
- session_commit();
- session_id($_SESSION['new_session_id']);
- }
- $sid = $this->sidResolver->getSid($this);
- // potential custom logic for session id (ex. switching between hosts)
- $this->setSessionId($sid);
- session_start();
- if (isset($_SESSION['destroyed'])
- && $_SESSION['destroyed'] < time() - $this->sessionConfig->getCookieLifetime()
- ) {
- $this->destroy(['clear_storage' => true]);
+ if ($this->sessionStartChecker->check()) {
+ if (!$this->isSessionExists()) {
+ \Magento\Framework\Profiler::start('session_start');
+
+ try {
+ $this->appState->getAreaCode();
+ } catch (\Magento\Framework\Exception\LocalizedException $e) {
+ throw new \Magento\Framework\Exception\SessionException(
+ new \Magento\Framework\Phrase(
+ 'Area code not set: Area code must be set before starting a session.'
+ ),
+ $e
+ );
+ }
+
+ // Need to apply the config options so they can be ready by session_start
+ $this->initIniOptions();
+ $this->registerSaveHandler();
+ if (isset($_SESSION['new_session_id'])) {
+ // Not fully expired yet. Could be lost cookie by unstable network.
+ session_commit();
+ session_id($_SESSION['new_session_id']);
+ }
+ $sid = $this->sidResolver->getSid($this);
+ // potential custom logic for session id (ex. switching between hosts)
+ $this->setSessionId($sid);
+ session_start();
+ if (isset($_SESSION['destroyed'])
+ && $_SESSION['destroyed'] < time() - $this->sessionConfig->getCookieLifetime()
+ ) {
+ $this->destroy(['clear_storage' => true]);
+ }
+
+ $this->validator->validate($this);
+ $this->renewCookie($sid);
+
+ register_shutdown_function([$this, 'writeClose']);
+
+ $this->_addHost();
+ \Magento\Framework\Profiler::stop('session_start');
}
-
- $this->validator->validate($this);
- $this->renewCookie($sid);
-
- register_shutdown_function([$this, 'writeClose']);
-
- $this->_addHost();
- \Magento\Framework\Profiler::stop('session_start');
+ $this->storage->init(isset($_SESSION) ? $_SESSION : []);
}
- $this->storage->init(isset($_SESSION) ? $_SESSION : []);
+
return $this;
}
diff --git a/lib/internal/Magento/Framework/Session/SessionStartChecker.php b/lib/internal/Magento/Framework/Session/SessionStartChecker.php
new file mode 100644
index 0000000000000..9cc32268d574a
--- /dev/null
+++ b/lib/internal/Magento/Framework/Session/SessionStartChecker.php
@@ -0,0 +1,38 @@
+checkSapi = $checkSapi;
+ }
+
+ /**
+ * Can session be started or not.
+ *
+ * @return bool
+ */
+ public function check() : bool
+ {
+ return !($this->checkSapi && PHP_SAPI === 'cli');
+ }
+}
diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php
index f2089c662e955..2a296f7cdcb24 100644
--- a/lib/internal/Magento/Framework/Validator/Factory.php
+++ b/lib/internal/Magento/Framework/Validator/Factory.php
@@ -6,22 +6,33 @@
namespace Magento\Framework\Validator;
+use Magento\Framework\Module\Dir\Reader;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Phrase;
+use Magento\Framework\Validator;
use Magento\Framework\Cache\FrontendInterface;
+/**
+ * Factory for \Magento\Framework\Validator and \Magento\Framework\Validator\Builder.
+ */
class Factory
{
- /** cache key */
+ /**
+ * cache key
+ *
+ * @deprecated
+ */
const CACHE_KEY = __CLASS__;
/**
- * @var \Magento\Framework\ObjectManagerInterface
+ * @var ObjectManagerInterface
*/
protected $_objectManager;
/**
* Validator config files
*
- * @var array|null
+ * @var iterable|null
*/
protected $_configFiles = null;
@@ -31,40 +42,25 @@ class Factory
private $isDefaultTranslatorInitialized = false;
/**
- * @var \Magento\Framework\Module\Dir\Reader
+ * @var Reader
*/
private $moduleReader;
- /**
- * @var FrontendInterface
- */
- private $cache;
-
- /**
- * @var \Magento\Framework\Serialize\SerializerInterface
- */
- private $serializer;
-
- /**
- * @var \Magento\Framework\Config\FileIteratorFactory
- */
- private $fileIteratorFactory;
-
/**
* Initialize dependencies
*
- * @param \Magento\Framework\ObjectManagerInterface $objectManager
- * @param \Magento\Framework\Module\Dir\Reader $moduleReader
- * @param FrontendInterface $cache
+ * @param ObjectManagerInterface $objectManager
+ * @param Reader $moduleReader
+ * @param FrontendInterface $cache @deprecated
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
- \Magento\Framework\ObjectManagerInterface $objectManager,
- \Magento\Framework\Module\Dir\Reader $moduleReader,
+ ObjectManagerInterface $objectManager,
+ Reader $moduleReader,
FrontendInterface $cache
) {
$this->_objectManager = $objectManager;
$this->moduleReader = $moduleReader;
- $this->cache = $cache;
}
/**
@@ -75,17 +71,7 @@ public function __construct(
protected function _initializeConfigList()
{
if (!$this->_configFiles) {
- $this->_configFiles = $this->cache->load(self::CACHE_KEY);
- if (!$this->_configFiles) {
- $this->_configFiles = $this->moduleReader->getConfigurationFiles('validation.xml');
- $this->cache->save(
- $this->getSerializer()->serialize($this->_configFiles->toArray()),
- self::CACHE_KEY
- );
- } else {
- $filesArray = $this->getSerializer()->unserialize($this->_configFiles);
- $this->_configFiles = $this->getFileIteratorFactory()->create(array_keys($filesArray));
- }
+ $this->_configFiles = $this->moduleReader->getConfigurationFiles('validation.xml');
}
}
@@ -93,6 +79,7 @@ protected function _initializeConfigList()
* Create and set default translator to \Magento\Framework\Validator\AbstractValidator.
*
* @return void
+ * @throws \Zend_Translate_Exception
*/
protected function _initializeDefaultTranslator()
{
@@ -100,7 +87,7 @@ protected function _initializeDefaultTranslator()
// Pass translations to \Magento\Framework\TranslateInterface from validators
$translatorCallback = function () {
$argc = func_get_args();
- return (string)new \Magento\Framework\Phrase(array_shift($argc), $argc);
+ return (string)new Phrase(array_shift($argc), $argc);
};
/** @var \Magento\Framework\Translate\Adapter $translator */
$translator = $this->_objectManager->create(\Magento\Framework\Translate\Adapter::class);
@@ -115,14 +102,15 @@ protected function _initializeDefaultTranslator()
*
* Will instantiate \Magento\Framework\Validator\Config
*
- * @return \Magento\Framework\Validator\Config
+ * @return Config
+ * @throws \Zend_Translate_Exception
*/
public function getValidatorConfig()
{
$this->_initializeConfigList();
$this->_initializeDefaultTranslator();
return $this->_objectManager->create(
- \Magento\Framework\Validator\Config::class,
+ Config::class,
['configFiles' => $this->_configFiles]
);
}
@@ -133,7 +121,8 @@ public function getValidatorConfig()
* @param string $entityName
* @param string $groupName
* @param array|null $builderConfig
- * @return \Magento\Framework\Validator\Builder
+ * @return Builder
+ * @throws \Zend_Translate_Exception
*/
public function createValidatorBuilder($entityName, $groupName, array $builderConfig = null)
{
@@ -147,43 +136,12 @@ public function createValidatorBuilder($entityName, $groupName, array $builderCo
* @param string $entityName
* @param string $groupName
* @param array|null $builderConfig
- * @return \Magento\Framework\Validator
+ * @return Validator
+ * @throws \Zend_Translate_Exception
*/
public function createValidator($entityName, $groupName, array $builderConfig = null)
{
$this->_initializeDefaultTranslator();
return $this->getValidatorConfig()->createValidator($entityName, $groupName, $builderConfig);
}
-
- /**
- * Get serializer
- *
- * @return \Magento\Framework\Serialize\SerializerInterface
- * @deprecated 100.2.0
- */
- private function getSerializer()
- {
- if ($this->serializer === null) {
- $this->serializer = $this->_objectManager->get(
- \Magento\Framework\Serialize\SerializerInterface::class
- );
- }
- return $this->serializer;
- }
-
- /**
- * Get file iterator factory
- *
- * @return \Magento\Framework\Config\FileIteratorFactory
- * @deprecated 100.2.0
- */
- private function getFileIteratorFactory()
- {
- if ($this->fileIteratorFactory === null) {
- $this->fileIteratorFactory = $this->_objectManager->get(
- \Magento\Framework\Config\FileIteratorFactory::class
- );
- }
- return $this->fileIteratorFactory;
- }
}
diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php
index 5511627c6dcc3..73a8c95c9a2ff 100644
--- a/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php
+++ b/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php
@@ -25,21 +25,6 @@ class FactoryTest extends \PHPUnit\Framework\TestCase
*/
private $validatorConfigMock;
- /**
- * @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject
- */
- private $cacheMock;
-
- /**
- * @var \Magento\Framework\Serialize\SerializerInterface|\PHPUnit_Framework_MockObject_MockObject
- */
- private $serializerMock;
-
- /**
- * @var \Magento\Framework\Config\FileIteratorFactory|\PHPUnit_Framework_MockObject_MockObject
- */
- private $fileIteratorFactoryMock;
-
/**
* @var \Magento\Framework\Config\FileIterator|\PHPUnit_Framework_MockObject_MockObject
*/
@@ -55,11 +40,6 @@ class FactoryTest extends \PHPUnit\Framework\TestCase
*/
private $factory;
- /**
- * @var string
- */
- private $jsonString = '["\/tmp\/moduleOne\/etc\/validation.xml"]';
-
/**
* @var array
*/
@@ -99,23 +79,9 @@ protected function setUp()
\Magento\Framework\Validator\Factory::class,
[
'objectManager' => $this->objectManagerMock,
- 'moduleReader' => $this->readerMock,
- 'cache' => $this->cacheMock
+ 'moduleReader' => $this->readerMock
]
);
-
- $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\SerializerInterface::class);
- $this->fileIteratorFactoryMock = $this->createMock(\Magento\Framework\Config\FileIteratorFactory::class);
- $objectManager->setBackwardCompatibleProperty(
- $this->factory,
- 'serializer',
- $this->serializerMock
- );
- $objectManager->setBackwardCompatibleProperty(
- $this->factory,
- 'fileIteratorFactory',
- $this->fileIteratorFactoryMock
- );
}
/**
@@ -147,46 +113,6 @@ public function testGetValidatorConfig()
);
}
- public function testGetValidatorConfigCacheNotExist()
- {
- $this->cacheMock->expects($this->once())
- ->method('load')
- ->willReturn(false);
- $this->readerMock->expects($this->once())
- ->method('getConfigurationFiles')
- ->willReturn($this->fileIteratorMock);
- $this->fileIteratorMock->method('toArray')
- ->willReturn($this->data);
- $this->cacheMock->expects($this->once())
- ->method('save')
- ->with($this->jsonString);
- $this->serializerMock->expects($this->once())
- ->method('serialize')
- ->with($this->data)
- ->willReturn($this->jsonString);
- $this->factory->getValidatorConfig();
- $this->factory->getValidatorConfig();
- }
-
- public function testGetValidatorConfigCacheExist()
- {
- $this->cacheMock->expects($this->once())
- ->method('load')
- ->willReturn($this->jsonString);
- $this->readerMock->expects($this->never())
- ->method('getConfigurationFiles');
- $this->cacheMock->expects($this->never())
- ->method('save');
- $this->serializerMock->expects($this->once())
- ->method('unserialize')
- ->with($this->jsonString)
- ->willReturn($this->data);
- $this->fileIteratorFactoryMock->method('create')
- ->willReturn($this->fileIteratorMock);
- $this->factory->getValidatorConfig();
- $this->factory->getValidatorConfig();
- }
-
public function testCreateValidatorBuilder()
{
$this->readerMock->method('getConfigurationFiles')
diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php
index 226abc538112b..da7bcb128f4b8 100644
--- a/lib/internal/Magento/Framework/View/Page/Config.php
+++ b/lib/internal/Magento/Framework/View/Page/Config.php
@@ -498,7 +498,7 @@ public function addRss($title, $href)
*/
public function addBodyClass($className)
{
- $className = preg_replace('#[^a-z0-9]+#', '-', strtolower($className));
+ $className = preg_replace('#[^a-z0-9-_]+#', '-', strtolower($className));
$bodyClasses = $this->getElementAttribute(self::ELEMENT_TYPE_BODY, self::BODY_ATTRIBUTE_CLASS);
$bodyClasses = $bodyClasses ? explode(' ', $bodyClasses) : [];
$bodyClasses[] = $className;
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php
index 0f59c302f943f..ed926afa00856 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php
@@ -57,13 +57,13 @@ public function testProcess()
->method('getPageConfigStructure')
->willReturn($structureMock);
- $bodyClasses = ['class_1', 'class_2'];
+ $bodyClasses = ['class_1', 'class--2'];
$structureMock->expects($this->once())
->method('getBodyClasses')
->will($this->returnValue($bodyClasses));
$this->pageConfigMock->expects($this->exactly(2))
->method('addBodyClass')
- ->withConsecutive(['class_1'], ['class_2']);
+ ->withConsecutive(['class_1'], ['class--2']);
$this->assertEquals(
$this->bodyGenerator,
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php
index ed15a356cc4c7..d2eba5d2fa1b3 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php
@@ -58,7 +58,7 @@ public function testSetElementAttribute()
public function testSetBodyClass()
{
$class1 = 'class_1';
- $class2 = 'class_2';
+ $class2 = 'class--2';
$expected = [$class1, $class2];
$this->structure->setBodyClass($class1);
$this->structure->setBodyClass($class2);
diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json
index 105c3f0721819..c674010af1a1f 100644
--- a/lib/internal/Magento/Framework/composer.json
+++ b/lib/internal/Magento/Framework/composer.json
@@ -2,7 +2,7 @@
"name": "magento/framework",
"description": "N/A",
"type": "magento2-library",
- "version": "101.0.7",
+ "version": "101.0.8",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/lib/web/css/source/lib/_icons.less b/lib/web/css/source/lib/_icons.less
index d113935e2b1cd..abb8b43368f13 100644
--- a/lib/web/css/source/lib/_icons.less
+++ b/lib/web/css/source/lib/_icons.less
@@ -25,9 +25,12 @@
@_icon-font-text-hide: @icon-font__text-hide,
@_icon-font-display: @icon-font__display
) when (@_icon-font-position = before) {
- ._lib-icon-text-hide(@_icon-font-text-hide);
.lib-css(display, @_icon-font-display);
- text-decoration: none;
+ text-decoration: none;
+
+ & when not (@_icon-font-content = false) {
+ ._lib-icon-text-hide(@_icon-font-text-hide);
+ }
&:before {
._lib-icon-font(
@@ -68,10 +71,13 @@
@_icon-font-text-hide: @icon-font__text-hide,
@_icon-font-display: @icon-font__display
) when (@_icon-font-position = after) {
- ._lib-icon-text-hide(@_icon-font-text-hide);
.lib-css(display, @_icon-font-display);
text-decoration: none;
-
+
+ & when not (@_icon-font-content = false) {
+ ._lib-icon-text-hide(@_icon-font-text-hide);
+ }
+
&:after {
._lib-icon-font(
@_icon-font-content,
@@ -151,8 +157,11 @@
@_icon-image-text-hide: @icon__text-hide
) when (@_icon-image-position = before) {
display: inline-block;
- ._lib-icon-text-hide(@_icon-image-text-hide);
-
+
+ & when not (@_icon-image = false) {
+ ._lib-icon-text-hide(@_icon-image-text-hide);
+ }
+
&:before {
._lib-icon-image(
@_icon-image,
@@ -179,7 +188,10 @@
@_icon-image-text-hide: @icon__text-hide
) when (@_icon-image-position = after) {
display: inline-block;
- ._lib-icon-text-hide(@_icon-image-text-hide);
+
+ & when not (@_icon-image = false) {
+ ._lib-icon-text-hide(@_icon-font-text-hide);
+ }
&:after {
._lib-icon-image(
diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js
index 4f323e4312f6b..33d731937707c 100644
--- a/lib/web/fotorama/fotorama.js
+++ b/lib/web/fotorama/fotorama.js
@@ -1455,16 +1455,24 @@ fotoramaVersion = '4.6.4';
}
} else {
stopEvent(e);
- (options.onMove || noop).call(el, e, {touch: touchFLAG});
+ if (movedEnough(xDiff,yDiff)) {
+ (options.onMove || noop).call(el, e, {touch: touchFLAG});
+ }
}
- if (!moved && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance) {
+ if (!moved && movedEnough(xDiff, yDiff) && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance)
+ {
moved = true;
}
tail.checked = tail.checked || xWin || yWin;
}
+
+ function movedEnough(xDiff, yDiff) {
+ return xDiff > yDiff && xDiff > 1.5;
+ }
+
function onEnd(e) {
(options.onTouchEnd || noop)();
diff --git a/lib/web/fotorama/fotorama.min.js b/lib/web/fotorama/fotorama.min.js
index e8eb9fbda63ef..f416d57488925 100644
--- a/lib/web/fotorama/fotorama.min.js
+++ b/lib/web/fotorama/fotorama.min.js
@@ -1,4 +1 @@
-/*!
- * Fotorama 4.6.4 | http://fotorama.io/license/
- */
-fotoramaVersion="4.6.4";(function(bo,k,a3,bV,aP){var ag="fotorama",bH="fotorama__fullscreen",ae=ag+"__wrap",ah=ae+"--css2",aX=ae+"--css3",bt=ae+"--video",ar=ae+"--fade",aw=ae+"--slide",P=ae+"--no-controls",aM=ae+"--no-shadows",U=ae+"--pan-y",a0=ae+"--rtl",az=ae+"--only-active",bN=ae+"--no-captions",f=ae+"--toggle-arrows",a7=ag+"__stage",x=a7+"__frame",l=x+"--video",B=a7+"__shaft",aB=ag+"__grab",bC=ag+"__pointer",aK=ag+"__arr",F=aK+"--disabled",bc=aK+"--prev",r=aK+"--next",bO=ag+"__nav",bq=bO+"-wrap",aH=bO+"__shaft",b=bq+"--vertical",ax=bq+"--list",bZ=bq+"--horizontal",bW=bO+"--dots",ai=bO+"--thumbs",aG=bO+"__frame",br=ag+"__fade",al=br+"-front",n=br+"-rear",aW=ag+"__shadow",bz=aW+"s",S=bz+"--left",aL=bz+"--right",a2=bz+"--top",aR=bz+"--bottom",a4=ag+"__active",a9=ag+"__select",bs=ag+"--hidden",M=ag+"--fullscreen",aJ=ag+"__fullscreen-icon",bP=ag+"__error",bM=ag+"__loading",c=ag+"__loaded",b3=c+"--full",bg=c+"--img",bR=ag+"__grabbing",J=ag+"__img",Y=J+"--full",bS=ag+"__thumb",b0=bS+"__arr--left",H=bS+"__arr--right",cb=bS+"-border",bd=ag+"__html",af=ag+"-video-container",bJ=ag+"__video",T=bJ+"-play",w=bJ+"-close",au=ag+"_horizontal_ratio",aY=ag+"_vertical_ratio",ca=ag+"__spinner",Z=ca+"--show";var E=bV&&bV.fn.jquery.split(".");if(!E||E[0]<1||(E[0]==1&&E[1]<8)){throw"Fotorama requires jQuery 1.8 or later and will not run without it."}var bx={};var ap=(function(co,ct,cj){var cf="2.8.3",cm={},cD=ct.documentElement,cE="modernizr",cB=ct.createElement(cE),cp=cB.style,cg,cw={}.toString,cy=" -webkit- -moz- -o- -ms- ".split(" "),cd="Webkit Moz O ms",cG=cd.split(" "),cq=cd.toLowerCase().split(" "),ck={},ce={},cu={},cA=[],cv=cA.slice,cc,cz=function(cQ,cS,cK,cR){var cJ,cP,cM,cN,cI=ct.createElement("div"),cO=ct.body,cL=cO||ct.createElement("body");if(parseInt(cK,10)){while(cK--){cM=ct.createElement("div");cM.id=cR?cR[cK]:cE+(cK+1);cI.appendChild(cM)}}cJ=["",'"].join("");cI.id=cE;(cO?cI:cL).innerHTML+=cJ;cL.appendChild(cI);if(!cO){cL.style.background="";cL.style.overflow="hidden";cN=cD.style.overflow;cD.style.overflow="hidden";cD.appendChild(cL)}cP=cS(cI,cQ);if(!cO){cL.parentNode.removeChild(cL);cD.style.overflow=cN}else{cI.parentNode.removeChild(cI)}return !!cP},cs=({}).hasOwnProperty,cC;if(!cl(cs,"undefined")&&!cl(cs.call,"undefined")){cC=function(cI,cJ){return cs.call(cI,cJ)}}else{cC=function(cI,cJ){return((cJ in cI)&&cl(cI.constructor.prototype[cJ],"undefined"))}}if(!Function.prototype.bind){Function.prototype.bind=function cH(cK){var cL=this;if(typeof cL!="function"){throw new TypeError()}var cI=cv.call(arguments,1),cJ=function(){if(this instanceof cJ){var cO=function(){};cO.prototype=cL.prototype;var cN=new cO();var cM=cL.apply(cN,cI.concat(cv.call(arguments)));if(Object(cM)===cM){return cM}return cN}else{return cL.apply(cK,cI.concat(cv.call(arguments)))}};return cJ}}function cr(cI){cp.cssText=cI}function ci(cJ,cI){return cr(cy.join(cJ+";")+(cI||""))}function cl(cJ,cI){return typeof cJ===cI}function cn(cJ,cI){return !!~(""+cJ).indexOf(cI)}function cF(cK,cI){for(var cJ in cK){var cL=cK[cJ];if(!cn(cL,"-")&&cp[cL]!==cj){return cI=="pfx"?cL:true}}return false}function cx(cJ,cM,cL){for(var cI in cJ){var cK=cM[cJ[cI]];if(cK!==cj){if(cL===false){return cJ[cI]}if(cl(cK,"function")){return cK.bind(cL||cM)}return cK}}return false}function i(cM,cI,cL){var cJ=cM.charAt(0).toUpperCase()+cM.slice(1),cK=(cM+" "+cG.join(cJ+" ")+cJ).split(" ");if(cl(cI,"string")||cl(cI,"undefined")){return cF(cK,cI)}else{cK=(cM+" "+(cq).join(cJ+" ")+cJ).split(" ");return cx(cK,cI,cL)}}ck.touch=function(){var cI;if(("ontouchstart" in co)||co.DocumentTouch&&ct instanceof DocumentTouch){cI=true}else{cz(["@media (",cy.join("touch-enabled),("),cE,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(cJ){cI=cJ.offsetTop===9})}return cI};ck.csstransforms3d=function(){var cI=!!i("perspective");if(cI&&"webkitPerspective" in cD.style){cz("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(cJ,cK){cI=cJ.offsetLeft===9&&cJ.offsetHeight===3})}return cI};ck.csstransitions=function(){return i("transition")};for(var ch in ck){if(cC(ck,ch)){cc=ch.toLowerCase();cm[cc]=ck[ch]();cA.push((cm[cc]?"":"no-")+cc)}}cm.addTest=function(cJ,cK){if(typeof cJ=="object"){for(var cI in cJ){if(cC(cJ,cI)){cm.addTest(cI,cJ[cI])}}}else{cJ=cJ.toLowerCase();if(cm[cJ]!==cj){return cm}cK=typeof cK=="function"?cK():cK;if(typeof enableClasses!=="undefined"&&enableClasses){cD.className+=" "+(cK?"":"no-")+cJ}cm[cJ]=cK}return cm};cr("");cB=cg=null;cm._version=cf;cm._prefixes=cy;cm._domPrefixes=cq;cm._cssomPrefixes=cG;cm.testProp=function(cI){return cF([cI])};cm.testAllProps=i;cm.testStyles=cz;cm.prefixed=function(cK,cJ,cI){if(!cJ){return i(cK,"pfx")}else{return i(cK,cJ,cI)}};return cm})(bo,k);var bB={ok:false,is:function(){return false},request:function(){},cancel:function(){},event:"",prefix:""},h="webkit moz o ms khtml".split(" ");if(typeof k.cancelFullScreen!="undefined"){bB.ok=true}else{for(var bv=0,N=h.length;bv=i?"bottom":"top bottom"):(ce<=cd?"left":ce>=i?"right":"left right")}function z(cc,cd,i){i=i||{};cc.each(function(){var cg=bV(this),cf=cg.data(),ce;if(cf.clickOn){return}cf.clickOn=true;bV.extend(aI(cg,{onStart:function(ch){ce=ch;(i.onStart||g).call(this,ch)},onMove:i.onMove||g,onTouchEnd:i.onTouchEnd||g,onEnd:function(ch){if(ch.moved){return}cd.call(this,ce)}}),{noMove:true})})}function ab(i,cc){return''+(cc||"")+"
"}function aT(i){return"."+i}function q(i){var cc='';return cc}function aC(cf){var cc=cf.length;while(cc){var ce=Math.floor(Math.random()*cc--);var cd=cf[cc];cf[cc]=cf[ce];cf[ce]=cd}return cf}function bG(i){return Object.prototype.toString.call(i)=="[object Array]"&&bV.map(i,function(cc){return bV.extend({},cc)})}function bU(i,cd,cc){i.scrollLeft(cd||0).scrollTop(cc||0)}function bA(i){if(i){var cc={};bV.each(i,function(cd,ce){cc[cd.toLowerCase()]=ce});return cc}}function bm(i){if(!i){return}var cc=+i;if(!isNaN(cc)){return cc}else{cc=i.split("/");return +cc[0]/+cc[1]||aP}}function D(cd,ce,cc,i){if(!ce){return}cd.addEventListener?cd.addEventListener(ce,cc,!!i):cd.attachEvent("on"+ce,cc)}function a5(i,cc){if(i>cc.max){i=cc.max}else{if(i=(ci-cf)){if(cc==="horizontal"){cg=-ce.position().left}else{cg=-ce.position().top}}else{if((cj+i.margin)*(ch)<=Math.abs(cf)){if(cc==="horizontal"){cg=-ce.position().left+ci-(cj+i.margin)}else{cg=-ce.position().top+ci-(cj+i.margin)}}else{cg=cf}}cg=a5(cg,ck);return cg||0}function aj(i){return !!i.getAttribute("disabled")}function ad(cc,i){if(i){return{disabled:cc}}else{return{tabindex:cc*-1+"",disabled:cc}}}function a(cc,i){D(cc,"keyup",function(cd){aj(cc)||cd.keyCode==13&&i.call(cc,cd)})}function bL(cc,i){D(cc,"focus",cc.onfocusin=function(cd){i.call(cc,cd)},true)}function O(cc,i){cc.preventDefault?cc.preventDefault():(cc.returnValue=false);i&&cc.stopPropagation&&cc.stopPropagation()}function aE(cd,cc){var i=/iP(ad|hone|od)/i.test(bo.navigator.userAgent);if(i&&cc==="touchend"){cd.on("touchend",function(ce){bw.trigger("mouseup",ce)})}cd.on(cc,function(ce){O(ce,true);return false})}function ay(i){return i?">":"<"}var aS=(function(){function cd(ch,ce,cg){var cf=ce/cg;if(cf<=1){ch.parent().removeClass(au);ch.parent().addClass(aY)}else{ch.parent().removeClass(aY);ch.parent().addClass(au)}}function i(cf,cg,ch){var ce=ch;if(!cf.attr(ce)&&cf.attr(ce)!==aP){cf.attr(ce,cg)}if(cf.find("["+ce+"]").length){cf.find("["+ce+"]").each(function(){bV(this).attr(ce,cg)})}}function cc(cf,ce,ci){var cg=false,ch;cf.showCaption===ci||cf.showCaption===true?ch=true:ch=false;if(!ce){return false}if(cf.caption&&ch){cg=true}return cg}return{setRatio:cd,setThumbAttr:i,isExpectedCaption:cc}}(aS||{},jQuery));function A(ce,cd){var cc=ce.data(),i=Math.round(cd.pos),cf=function(){if(cc&&cc.sliding){cc.sliding=false}(cd.onEnd||g)()};if(typeof cd.overPos!=="undefined"&&cd.overPos!==cd.pos){i=cd.overPos}var cg=bV.extend(b2(i,cd.direction),cd.width&&{width:cd.width},cd.height&&{height:cd.height});if(cc&&cc.sliding){cc.sliding=true}if(aA){ce.css(bV.extend(b6(cd.time),cg));if(cd.time>10){X(ce,"transform",cf,cd.time)}else{cf()}}else{ce.stop().animate(cg,cd.time,u,cf)}}function aq(ck,cj,cc,cm,ce,i){var ch=typeof i!=="undefined";if(!ch){ce.push(arguments);Array.prototype.push.call(arguments,ce.length);if(ce.length>1){return}}ck=ck||bV(ck);cj=cj||bV(cj);var ci=ck[0],cg=cj[0],cf=cm.method==="crossfade",cl=function(){if(!cl.done){cl.done=true;var cn=(ch||ce.shift())&&ce.shift();cn&&aq.apply(this,cn);(cm.onEnd||g)(!!cn)}},cd=cm.time/(i||1);cc.removeClass(n+" "+al);ck.stop().addClass(n);cj.stop().addClass(al);cf&&cg&&ck.fadeTo(0,0);ck.fadeTo(cf?cd:0,1,cf&&cl);cj.fadeTo(cd,0,cl);(ci&&cf)||cg||cl()}var G,b5,e,j,bD;function bn(i){var cc=(i.touches||[])[0]||i;i._x=cc.pageX||cc.originalEvent.pageX;i._y=cc.clientY||cc.originalEvent.clientY;i._now=bV.now()}function aI(cr,cg){var cc=cr[0],cj={},i,cl,cf,cn,cs,cd,ce,co,ch;function cq(ct){cf=bV(ct.target);cj.checked=cd=ce=ch=false;if(i||cj.flow||(ct.touches&&ct.touches.length>1)||ct.which>1||(G&&G.type!==ct.type&&e)||(cd=cg.select&&cf.is(cg.select,cc))){return cd}cs=ct.type==="touchstart";ce=cf.is("a, a *",cc);cn=cj.control;co=(cj.noMove||cj.noSwipe||cn)?16:!cj.snap?4:0;bn(ct);cl=G=ct;b5=ct.type.replace(/down|start/,"move").replace(/Down/,"Move");(cg.onStart||g).call(cc,ct,{control:cn,$target:cf});i=cj.flow=true;if(!cs||cj.go){O(ct)}}function ck(cx){if((cx.touches&&cx.touches.length>1)||(aZ&&!cx.isPrimary)||b5!==cx.type||!i){i&&ci();(cg.onTouchEnd||g)();return}bn(cx);var cy=Math.abs(cx._x-cl._x),cu=Math.abs(cx._y-cl._y),cw=cy-cu,cv=(cj.go||cj.x||cw>=0)&&!cj.noSwipe,ct=cw<0;if(cs&&!cj.checked){if(i=cv){O(cx)}}else{O(cx);(cg.onMove||g).call(cc,cx,{touch:cs})}if(!ch&&Math.sqrt(Math.pow(cy,2)+Math.pow(cu,2))>co){ch=true}cj.checked=cj.checked||cv||ct}function ci(cu){(cg.onTouchEnd||g)();var ct=i;cj.control=i=false;if(ct){cj.flow=false}if(!ct||(ce&&!cj.checked)){return}cu&&O(cu);e=true;clearTimeout(j);j=setTimeout(function(){e=false},1000);(cg.onEnd||g).call(cc,{moved:ch,$target:cf,control:cn,touch:cs,startEvent:cl,aborted:!cu||cu.type==="MSPointerCancel"})}function cm(){if(cj.flow){return}cj.flow=true}function cp(){if(!cj.flow){return}cj.flow=false}if(aZ){D(cc,"MSPointerDown",cq);D(k,"MSPointerMove",ck);D(k,"MSPointerCancel",ci);D(k,"MSPointerUp",ci)}else{D(cc,"touchstart",cq);D(cc,"touchmove",ck);D(cc,"touchend",ci);D(k,"touchstart",cm);D(k,"touchend",cp);D(k,"touchcancel",cp);bf.on("scroll",cp);cr.on("mousedown pointerdown",cq);bw.on("mousemove pointermove",ck).on("mouseup pointerup",ci)}if(ap.touch){bD="a"}else{bD="div"}cr.on("click",bD,function(ct){cj.checked&&O(ct)});return cj}function ao(cz,cd){var cc=cz[0],ce=cz.data(),cm={},cw,cf,cx,cj,ch,cy,co,cg,cr,ct,cp,cq,i,cv,ci,cn;function cs(cA,cB){cn=true;cw=cf=(cq==="vertical")?cA._y:cA._x;co=cA._now;cy=[[co,cw]];cx=cj=cm.noMove||cB?0:a1(cz,(cd.getPos||g)());(cd.onStart||g).call(cc,cA)}function cu(cB,cA){cr=cm.min;ct=cm.max;cp=cm.snap,cq=cm.direction||"horizontal",cz.navdir=cq;i=cB.altKey;cn=ci=false;cv=cA.control;if(!cv&&!ce.sliding){cs(cB)}}function cl(cB,cA){if(!cm.noSwipe){if(!cn){cs(cB)}cf=(cq==="vertical")?cB._y:cB._x;cy.push([cB._now,cf]);cj=cx-(cw-cf);ch=bp(cj,cr,ct,cq);if(cj<=cr){cj=aF(cj,cr)}else{if(cj>=ct){cj=aF(cj,ct)}}if(!cm.noMove){cz.css(b2(cj,cq));if(!ci){ci=true;cA.touch||aZ||cz.addClass(bR)}(cd.onMove||g).call(cc,cB,{pos:cj,edge:ch})}}}function ck(cJ){if(cm.noSwipe&&cJ.moved){return}if(!cn){cs(cJ.startEvent,true)}cJ.touch||aZ||cz.removeClass(bR);cg=bV.now();var cG=cg-b8,cK,cP,cQ,cS=null,cA,cE,cN,cD,cF,cI=ba,cO,cH=cd.friction;for(var cC=cy.length-1;cC>=0;cC--){cK=cy[cC][0];cP=Math.abs(cK-cG);if(cS===null||cPcQ){break}}cQ=cP}cD=bb(cj,cr,ct);var cT=cA-cf,cR=cT>=0,cL=cg-cS,cB=cL>b8,cM=!cB&&cj!==cx&&cD===cj;if(cp){cD=bb(Math[cM?(cR?"floor":"ceil"):"round"](cj/cp)*cp,cr,ct);cr=ct=cD}if(cM&&(cp||cD===cj)){cO=-(cT/cL);cI*=bb(Math.abs(cO),cd.timeLow,cd.timeHigh);cE=Math.round(cj+cO*cI/cH);if(!cp){cD=cE}if(!cR&&cE>ct||cR&&cE"),c9=bV(ab(bs)),dk=d6.find(aT(ae)),cf=dk.find(aT(a7)),dY=cf[0],cl=d6.find(aT(B)),c8=bV(),dW=d6.find(aT(bc)),da=d6.find(aT(r)),cU=d6.find(aT(aK)),dU=d6.find(aT(bq)),dO=dU.find(aT(bO)),cF=dO.find(aT(aH)),dA,cB=bV(),cW=bV(),dS=cl.data(),cX=cF.data(),c7=d6.find(aT(cb)),eg=d6.find(aT(b0)),dX=d6.find(aT(H)),dM=d6.find(aT(aJ)),dD=dM[0],cH=bV(ab(T)),dt=d6.find(aT(w)),d1=dt[0],eb=d6.find(aT(ca)),dg,eo=false,dF,ea,c2,ed,dw,d4,cN,cK,dx,dj,cq,c0,d8,c4,d2,cv,ch,ej,ds,cu,ec,dH,dE,d0={},en={},dG,d5={},cG={},dy={},ef={},cs,cT,ee,cj,el,cd={},er={},dZ,c6,dz,dr,d3=0,cI=[];dk[bu]=bV('
');dk[bl]=bV(bV.Fotorama.jst.thumb());dk[b7]=bV(bV.Fotorama.jst.dots());cd[bu]=[];cd[bl]=[];cd[b7]=[];er[bu]={};dk.addClass(aA?aX:ah);cR.fotorama=this;function ep(){bV.each(dP,function(ey,eA){if(!eA.i){eA.i=cY++;var ez=at(eA.video,true);if(ez){var ex={};eA.video=ez;if(!eA.img&&!eA.thumb){ex=aQ(eA,dP,cg)}else{eA.thumbsReady=true}v(dP,{img:ex.img,thumb:ex.thumb},eA.i,cg)}}})}function df(ex){return dE[ex]}function i(){if(cf!==aP){if(c3.navdir=="vertical"){var ex=c3.thumbwidth+c3.thumbmargin;cf.css("left",ex);da.css("right",ex);dM.css("right",ex);dk.css("width",dk.css("width")+ex);cl.css("max-width",dk.width()-ex)}else{cf.css("left","");da.css("right","");dM.css("right","");dk.css("width",dk.css("width")+ex);cl.css("max-width","")}}}function ek(eB){var eC="keydown."+ag,eD=ag+cC,ex="keydown."+eD,eA="keyup."+eD,ey="resize."+eD+" orientationchange."+eD,ez;if(eB){bw.on(ex,function(eG){var eF,eE;if(dg&&eG.keyCode===27){eF=true;cO(dg,true,true)}else{if(cg.fullScreen||(c3.keyboard&&!cg.index)){if(eG.keyCode===27){eF=true;cg.cancelFullScreen()}else{if((eG.shiftKey&&eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===37&&df("left"))||(eG.keyCode===38&&df("up")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE="<"}else{if((eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===39&&df("right"))||(eG.keyCode===40&&df("down")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE=">"}else{if(eG.keyCode===36&&df("home")){cg.longPress.progress();eE="<<"}else{if(eG.keyCode===35&&df("end")){cg.longPress.progress();eE=">>"}}}}}}}(eF||eE)&&O(eG);ez={index:eE,slow:eG.altKey,user:true};eE&&(cg.longPress.inProgress?cg.showWhileLongPress(ez):cg.show(ez))});if(eB){bw.on(eA,function(eE){if(cg.longPress.inProgress){cg.showEndLongPress({user:true})}cg.longPress.reset()})}if(!cg.index){bw.off(eC).on(eC,"textarea, input, select",function(eE){!I.hasClass(bH)&&eE.stopPropagation()})}bf.on(ey,cg.resize)}else{bw.off(ex);bf.off(ey)}}function dd(ex){if(ex===dd.f){return}if(ex){d6.addClass(ag+" "+cQ).before(c9).before(de);C(cg)}else{c9.detach();de.detach();d6.html(cR.urtext).removeClass(cQ);av(cg)}ek(ex);dd.f=ex}function dn(){dP=cg.data=dP||bG(c3.data)||bI(d6);c1=cg.size=dP.length;eq.ok&&c3.shuffle&&aC(dP);ep();eo=cn(eo);c1&&dd(true)}function em(){var ex=c1<2||dg;d5.noMove=ex||cv;d5.noSwipe=ex||!c3.swipe;!cu&&cl.toggleClass(aB,!c3.click&&!d5.noMove&&!d5.noSwipe);aZ&&dk.toggleClass(U,!d5.noSwipe)}function dq(ex){if(ex===true){ex=""}c3.autoplay=Math.max(+ex||bQ,ds*1.5)}function db(ex){if(ex.navarrows&&ex.nav==="thumbs"){eg.show();dX.show()}else{eg.hide();dX.hide()}}function ck(ex,ey){return Math.floor(dk.width()/(ey.thumbwidth+ey.thumbmargin))}function dQ(){if(!c3.nav||c3.nav==="dots"){c3.navdir="horizontal"}cg.options=c3=bA(c3);b4=ck(dk,c3);cv=(c3.transition==="crossfade"||c3.transition==="dissolve");dj=c3.loop&&(c1>2||(cv&&(!cu||cu!=="slide")));ds=+c3.transitionduration||ba;dH=c3.direction==="rtl";dE=bV.extend({},c3.keyboard&&p,c3.keyboard);db(c3);var ey={add:[],remove:[]};function ex(ez,eA){ey[ez?"add":"remove"].push(eA)}if(c1>1){cq=c3.nav;d8=c3.navposition==="top";ey.remove.push(a9);cU.toggle(!!c3.arrows)}else{cq=false;cU.hide()}dh();cJ();ev();if(c3.autoplay){dq(c3.autoplay)}ch=m(c3.thumbwidth)||L;ej=m(c3.thumbheight)||L;cG.ok=ef.ok=c3.trackpad&&!bh;em();dL(c3,[en]);c0=cq==="thumbs";if(dU.filter(":hidden")&&!!cq){dU.show()}if(c0){dl(c1,"navThumb");dA=cW;dr=bl;an(de,bV.Fotorama.jst.style({w:ch,h:ej,b:c3.thumbborderwidth,m:c3.thumbmargin,s:cC,q:!aN}));dO.addClass(ai).removeClass(bW)}else{if(cq==="dots"){dl(c1,"navDot");dA=cB;dr=b7;dO.addClass(bW).removeClass(ai)}else{dU.hide();cq=false;dO.removeClass(ai+" "+bW)}}if(cq){if(d8){dU.insertBefore(cf)}else{dU.insertAfter(cf)}cz.nav=false;cz(dA,cF,"nav")}c4=c3.allowfullscreen;if(c4){dM.prependTo(cf);d2=s&&c4==="native";aE(dM,"touchend")}else{dM.detach();d2=false}ex(cv,ar);ex(!cv,aw);ex(!c3.captions,bN);ex(dH,a0);ex(c3.arrows,f);ec=c3.shadows&&!bh;ex(!ec,aM);dk.addClass(ey.add.join(" ")).removeClass(ey.remove.join(" "));d0=bV.extend({},c3);i()}function cZ(ex){return ex<0?(c1+(ex%c1))%c1:ex>=c1?ex%c1:ex}function cn(ex){return bb(ex,0,c1-1)}function du(ex){return dj?cZ(ex):cn(ex)}function dB(ex){return ex>0||dj?ex-1:false}function ci(ex){return ex1&&dP[eE]===eD&&!eD.html&&!eD.deleted&&!eD.video&&!eN){eD.deleted=true;cg.splice(eE,1)}}}function eL(){bV.Fotorama.measures[eF]=eO.measures=bV.Fotorama.measures[eF]||{width:eS.width,height:eS.height,ratio:eS.width/eS.height};cc(eO.measures.width,eO.measures.height,eO.measures.ratio,eE);eG.off("load error").addClass(""+(eN?Y:J)).attr("aria-hidden","false").prependTo(eC);if(eC.hasClass(x)&&!eC.hasClass(af)){eC.attr("href",eG.attr("src"))}V(eG,(bV.isFunction(eA)?eA():eA)||en);bV.Fotorama.cache[eF]=eB.state="loaded";setTimeout(function(){eC.trigger("f:load").removeClass(bM+" "+bP).addClass(c+" "+(eN?b3:bg));if(ey==="stage"){eH("load")}else{if(eD.thumbratio===bF||!eD.thumbratio&&c3.thumbratio===bF){eD.thumbratio=eO.measures.ratio;dV()}}},0)}if(!eF){eK();return}function eI(){var eT=10;bX(function(){return !c6||!eT--&&!bh},function(){eL()})}if(!bV.Fotorama.cache[eF]){bV.Fotorama.cache[eF]="*";eG.on("load",eI).on("error",eK)}else{(function eQ(){if(bV.Fotorama.cache[eF]==="error"){eK()}else{if(bV.Fotorama.cache[eF]==="loaded"){setTimeout(eI,0)}else{setTimeout(eQ,100)}}})()}eB.state="";eS.src=eF;if(eB.data.caption){eS.alt=eB.data.caption||""}if(eB.data.full){bV(eS).data("original",eB.data.full)}if(aS.isExpectedCaption(eD,c3.showcaption)){bV(eS).attr("aria-labelledby",eD.labelledby)}})}function cy(){var ex=dF[bu];if(ex&&!ex.data().state){eb.addClass(Z);ex.on("f:load f:error",function(){ex.off("f:load f:error");eb.removeClass(Z)})}}function cL(ex){a(ex,dJ);bL(ex,function(){setTimeout(function(){bU(dO)},0);dT({time:ds,guessIndex:bV(this).data().eq,minMax:dy})})}function dl(ex,ey){dm(ex,ey,function(eB,ez,eG,eD,eA,eC){if(eD){return}eD=eG[eA]=dk[eA].clone();eC=eD.data();eC.data=eG;var eF=eD[0],eE="labelledby"+bV.now();if(ey==="stage"){if(eG.html){bV('
').append(eG._html?bV(eG.html).removeAttr("id").html(eG._html):eG.html).appendTo(eD)}if(eG.id){eE=eG.id||eE}eG.labelledby=eE;if(aS.isExpectedCaption(eG,c3.showcaption)){bV(bV.Fotorama.jst.frameCaption({caption:eG.caption,labelledby:eE})).appendTo(eD)}eG.video&&eD.addClass(l).append(cH.clone());bL(eF,function(){setTimeout(function(){bU(cf)},0);cm({index:eC.eq,user:true})});c8=c8.add(eD)}else{if(ey==="navDot"){cL(eF);cB=cB.add(eD)}else{if(ey==="navThumb"){cL(eF);eC.$wrap=eD.children(":first");cW=cW.add(eD);if(eG.video){eC.$wrap.append(cH.clone())}}}}})}function cM(ey,ex){return ey&&ey.length&&V(ey,ex)}function di(ex){dm(ex,"stage",function(eB,ez,eE,eD,eA,eC){if(!eD){return}var ey=cZ(ez);eC.eq=ey;er[bu][ey]=eD.css(bV.extend({left:cv?0:a8(ez,en.w,c3.margin,c2)},cv&&b6(0)));if(be(eD[0])){eD.appendTo(cl);cO(eE.$video)}cM(eC.$img,en);cM(eC.$full,en);if(eD.hasClass(x)&&!(eD.attr("aria-hidden")==="false"&&eD.hasClass(a4))){eD.attr("aria-hidden","true")}})}function dp(eB,ex){var ey,ez,eA;if(cq!=="thumbs"||isNaN(eB)){return}ey=-eB;ez=-eB+en.nw;if(c3.navdir==="vertical"){eB=eB-c3.thumbheight;ez=-eB+en.h}cW.each(function(){var eH=bV(this),eD=eH.data(),eC=eD.eq,eG=function(){return{h:ej,w:eD.w}},eF=eG(),eE=c3.navdir==="vertical"?eD.t>ez:eD.l>ez;eF.w=eD.w;if(eD.l+eD.wen.w/3}function cE(ex){return !dj&&(!(eo+ex)||!(eo-c1+ex))&&!dg}function dh(){var ey=cE(0),ex=cE(1);dW.toggleClass(F,ey).attr(ad(ey,false));da.toggleClass(F,ex).attr(ad(ex,false))}function ev(){var ex=false,ey=false;if(c3.navtype==="thumbs"&&!c3.loop){(eo==0)?ex=true:ex=false;(eo==c3.data.length-1)?ey=true:ey=false}if(c3.navtype==="slides"){var ez=aa(cF,c3.navdir);ez>=dy.max?ex=true:ex=false;ez<=dy.min?ey=true:ey=false}eg.toggleClass(F,ex).attr(ad(ex,true));dX.toggleClass(F,ey).attr(ad(ey,true))}function cJ(){if(cG.ok){cG.prevent={"<":cE(0),">":cE(1)}}}function dI(eD){var eA=eD.data(),eC,eB,ez,ex;if(c0){eC=eA.l;eB=eA.t;ez=eA.w;ex=eA.h}else{eC=eD.position().left;ez=eD.width()}var ey={c:eC+ez/2,min:-eC+c3.thumbmargin*10,max:-eC+en.w-ez-c3.thumbmargin*10};var eE={c:eB+ex/2,min:-eB+c3.thumbmargin*10,max:-eB+en.h-ex-c3.thumbmargin*10};return c3.navdir==="vertical"?eE:ey}function d7(ey){var ex=dF[dr].data();A(c7,{time:ey*1.2,pos:(c3.navdir==="vertical"?ex.t:ex.l),width:ex.w,height:ex.h,direction:c3.navdir})}function dT(eH){var eB=dP[eH.guessIndex][dr],ez=c3.navtype;var eD,ex,eA,eG,eC,ey,eE,eF;if(eB){if(ez==="thumbs"){eD=dy.min!==dy.max;eA=eH.minMax||eD&&dI(dF[dr]);eG=eD&&(eH.keep&&dT.t?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));eC=eD&&(eH.keep&&dT.l?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));ey=(c3.navdir==="vertical"?eG:eC);eE=eD&&bb(ey,dy.min,dy.max)||0;ex=eH.time*1.1;A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir));dT.l=ey}else{eF=aa(cF,c3.navdir);ex=eH.time*1.11;eE=aD(c3,dy,eH.guessIndex,eF,eB,dU,c3.navdir);A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir))}}}function cS(){dN(dr);cd[dr].push(dF[dr].addClass(a4).attr("data-active",true))}function dN(ey){var ex=cd[ey];while(ex.length){ex.shift().removeClass(a4).attr("data-active",false)}}function ce(ey){var ex=er[ey];bV.each(ea,function(eA,ez){delete ex[cZ(ez)]});bV.each(ex,function(ez,eA){delete ex[ez];eA.detach()})}function dC(ey){c2=ed=eo;var ex=dF[bu];if(ex){dN(bu);cd[bu].push(ex.addClass(a4).attr("data-active",true));if(ex.hasClass(x)){ex.attr("aria-hidden","false")}ey||cg.showStage.onEnd(true);a1(cl,0,true);ce(bu);di(ea);d9();c5();a(cl[0],function(){if(!d6.hasClass(M)){cg.requestFullScreen();dM.focus()}})}}function dL(ey,ex){if(!ey){return}bV.each(ex,function(ez,eA){if(!eA){return}bV.extend(eA,{width:ey.width||eA.width,height:ey.height,minwidth:ey.minwidth,maxwidth:ey.maxwidth,minheight:ey.minheight,maxheight:ey.maxheight,ratio:bm(ey.ratio)})})}function dc(ey,ex){d6.trigger(ag+":"+ey,[cg,ex])}function dR(){clearTimeout(cr.t);c6=1;if(c3.stopautoplayontouch){cg.stopAutoplay()}else{cj=true}}function cr(){if(!c6){return}if(!c3.stopautoplayontouch){cw();es()}cr.t=setTimeout(function(){c6=0},ba+b8)}function cw(){cj=!!(dg||el)}function es(){clearTimeout(es.t);bX.stop(es.w);if(!c3.autoplay||cj){if(cg.autoplay){cg.autoplay=false;dc("stopautoplay")}return}if(!cg.autoplay){cg.autoplay=true;dc("startautoplay")}var ey=eo;var ex=dF[bu].data();es.w=bX(function(){return ex.state||ey!==eo},function(){es.t=setTimeout(function(){if(cj||ey!==eo){return}var ez=cK,eA=dP[ez][bu].data();es.w=bX(function(){return eA.state||ez!==cK},function(){if(cj||ez!==cK){return}cg.show(dj?ay(!dH):cK)})},c3.autoplay)})}cg.startAutoplay=function(ex){if(cg.autoplay){return this}cj=el=false;dq(ex||c3.autoplay);es();return this};cg.stopAutoplay=function(){if(cg.autoplay){cj=el=true;es()}return this};cg.showSlide=function(ez){var eA=aa(cF,c3.navdir),eC,eB=500*1.1,ey=c3.navdir==="horizontal"?c3.thumbwidth:c3.thumbheight,ex=function(){ev()};if(ez==="next"){eC=eA-(ey+c3.margin)*b4}if(ez==="prev"){eC=eA+(ey+c3.margin)*b4}eC=a5(eC,dy);dp(eC,true);A(cF,{time:eB,pos:eC,direction:c3.navdir,onEnd:ex})};cg.showWhileLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showNav(ex,eA,eB);return this};cg.showEndLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;return this};function dK(ey){var ex;if(typeof ey!=="object"){ex=ey;ey={}}else{ex=ey.index}ex=ex===">"?ed+1:ex==="<"?ed-1:ex==="<<"?0:ex===">>"?c1-1:ex;ex=isNaN(ex)?aP:ex;ex=typeof ex==="undefined"?eo||0:ex;return ex}function ew(ex){cg.activeIndex=eo=du(ex);d4=dB(eo);cN=ci(eo);cK=cZ(eo+(dH?-1:1));ea=[eo,d4,cN];ed=dj?ex:eo}function cA(ey){var ex=Math.abs(dw-ed),ez=bj(ey.time,function(){return Math.min(ds*(1+(ex-1)/12),ds*2)});if(ey.slow){ez*=10}return ez}cg.showStage=function(ey,eA,eD){cO(dg,dF.i!==dP[cZ(c2)].i);dl(ea,"stage");di(bh?[ed]:[ed,dB(ed),ci(ed)]);cD("go",true);ey||dc("show",{user:eA.user,time:eD});cj=true;var eC=eA.overPos;var ez=cg.showStage.onEnd=function(eE){if(ez.ok){return}ez.ok=true;eE||dC(true);if(!ey){dc("showend",{user:eA.user})}if(!eE&&cu&&cu!==c3.transition){cg.setOptions({transition:cu});cu=false;return}cy();cx(ea,"stage");cD("go",false);cJ();ei();cw();es();if(cg.fullScreen){dF[bu].find("."+Y).attr("aria-hidden",false);dF[bu].find("."+J).attr("aria-hidden",true)}else{dF[bu].find("."+Y).attr("aria-hidden",true);dF[bu].find("."+J).attr("aria-hidden",false)}};if(!cv){A(cl,{pos:-a8(ed,en.w,c3.margin,c2),overPos:eC,time:eD,onEnd:ez})}else{var ex=dF[bu],eB=dP[dw]&&eo!==dw?dP[dw][bu]:null;aq(ex,eB,c8,{time:eD,method:c3.transition,onEnd:ez},cI)}dh()};cg.showNav=function(ey,ez,eA){ev();if(cq){cS();var ex=cn(eo+bb(ed-dw,-1,1));dT({time:eA,coo:ex!==eo&&ez.coo,guessIndex:typeof ez.coo!=="undefined"?ex:eo,keep:ey});if(c0){d7(eA)}}};cg.show=function(eA){cg.longPress.singlePressInProgress=true;var ez=dK(eA);ew(ez);var eB=cA(eA);var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);cg.showNav(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;cg.longPress.singlePressInProgress=false;return this};cg.requestFullScreen=function(){if(c4&&!cg.fullScreen){var ex=bV((cg.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container");if(ex){return}cs=bf.scrollTop();cT=bf.scrollLeft();bU(bf);cD("x",true);dZ=bV.extend({},en);d6.addClass(M).appendTo(I.addClass(bH));R.addClass(bH);cO(dg,true,true);cg.fullScreen=true;if(d2){bB.request(eu)}cg.resize();cx(ea,"stage");cy();dc("fullscreenenter");if(!("ontouchstart" in bo)){dM.focus()}}return this};function cP(){if(cg.fullScreen){cg.fullScreen=false;if(s){bB.cancel(eu)}I.removeClass(bH);R.removeClass(bH);d6.removeClass(M).insertAfter(c9);en=bV.extend({},dZ);cO(dg,true,true);cD("x",false);cg.resize();cx(ea,"stage");bU(bf,cT,cs);dc("fullscreenexit")}}cg.cancelFullScreen=function(){if(d2&&bB.is()){bB.cancel(k)}else{cP()}return this};cg.toggleFullScreen=function(){return cg[(cg.fullScreen?"cancel":"request")+"FullScreen"]()};cg.resize=function(ez){if(!dP){return this}var eC=arguments[1]||0,ey=arguments[2];b4=ck(dk,c3);dL(!cg.fullScreen?bA(ez):{width:bV(bo).width(),maxwidth:null,minwidth:null,height:bV(bo).height(),maxheight:null,minheight:null},[en,ey||cg.fullScreen||c3]);var eB=en.width,ex=en.height,eA=en.ratio,eD=bf.height()-(cq?dO.height():0);if(t(eB)){dk.css({width:""});dk.css({height:""});cf.css({width:""});cf.css({height:""});cl.css({width:""});cl.css({height:""});dO.css({width:""});dO.css({height:""});dk.css({minWidth:en.minwidth||0,maxWidth:en.maxwidth||bK});if(cq==="dots"){dU.hide()}eB=en.W=en.w=dk.width();en.nw=cq&&d(c3.navwidth,eB)||eB;cl.css({width:en.w,marginLeft:(en.W-en.w)/2});ex=d(ex,eD);ex=ex||(eA&&eB/eA);if(ex){eB=Math.round(eB);ex=en.h=Math.round(bb(ex,d(en.minheight,eD),d(en.maxheight,eD)));cf.css({width:eB,height:ex});if(c3.navdir==="vertical"&&!cg.fullscreen){dO.width(c3.thumbwidth+c3.thumbmargin*2)}if(c3.navdir==="horizontal"&&!cg.fullscreen){dO.height(c3.thumbheight+c3.thumbmargin*2)}if(cq==="dots"){dO.width(eB).height("auto");dU.show()}if(c3.navdir==="vertical"&&cg.fullScreen){cf.css("height",bf.height())}if(c3.navdir==="horizontal"&&cg.fullScreen){cf.css("height",bf.height()-dO.height())}if(cq){switch(c3.navdir){case"vertical":dU.removeClass(bZ);dU.removeClass(ax);dU.addClass(b);dO.stop().animate({height:en.h,width:c3.thumbwidth},eC);break;case"list":dU.removeClass(b);dU.removeClass(bZ);dU.addClass(ax);break;default:dU.removeClass(b);dU.removeClass(ax);dU.addClass(bZ);dO.stop().animate({width:en.nw},eC);break}dC();dT({guessIndex:eo,time:eC,keep:true});if(c0&&cz.nav){d7(eC)}}dG=ey||true;eq.ok=true;eq()}}d3=cf.offset().left;i();return this};cg.setOptions=function(ex){bV.extend(c3,ex);dV();return this};cg.shuffle=function(){dP&&aC(dP)&&dV();return this};function co(ex,ey){if(ec){ex.removeClass(S+" "+aL);ex.removeClass(a2+" "+aR);ey&&!dg&&ex.addClass(ey.replace(/^|\s/g," "+bz+"--"))}}cg.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){if(!this.inProgress){this.count++;this.inProgress=this.count>this.threshold}},end:function(){if(this.inProgress){this.isEnded=true}},reset:function(){this.count=0;this.inProgress=false;this.isEnded=false}};cg.destroy=function(){cg.cancelFullScreen();cg.stopAutoplay();dP=cg.data=null;dd();ea=[];ce(bu);dV.ok=false;return this};cg.playVideo=function(){var ez=dF,ex=ez.video,ey=eo;if(typeof ex==="object"&&ez.videoReady){d2&&cg.fullScreen&&cg.cancelFullScreen();bX(function(){return !bB.is()||ey!==eo},function(){if(ey===eo){ez.$video=ez.$video||bV(ab(bJ)).append(q(ex));ez.$video.appendTo(ez[bu]);dk.addClass(bt);dg=ez.$video;em();cU.blur();dM.blur();dc("loadvideo")}})}return this};cg.stopVideo=function(){cO(dg,true,true);return this};cg.spliceByIndex=function(ex,ey){ey.i=ex+1;ey.img&&bV.ajax({url:ey.img,type:"HEAD",success:function(){dP.splice(ex,1,ey);dV()}})};function cO(ex,ez,ey){if(ez){dk.removeClass(bt);dg=false;em()}if(ex&&ex!==dg){ex.remove();dc("unloadvideo")}if(ey){cw();es()}}function cp(ex){dk.toggleClass(P,ex)}function ei(ez){if(d5.flow){return}var ex=ez?ez.pageX:ei.x,ey=ex&&!cE(eh(ex))&&c3.click;if(ei.p!==ey&&cf.toggleClass(bC,ey)){ei.p=ey;ei.x=ex}}cf.on("mousemove",ei);function cm(ex){clearTimeout(cm.t);if(c3.clicktransition&&c3.clicktransition!==c3.transition){setTimeout(function(){var ey=c3.transition;cg.setOptions({transition:c3.clicktransition});cu=ey;cm.t=setTimeout(function(){cg.show(ex)},10)},0)}else{cg.show(ex)}}function ct(eA,ey){var ez=eA.target,ex=bV(ez);if(ex.hasClass(T)){cg.playVideo()}else{if(ez===dD){cg.toggleFullScreen()}else{if(dg){ez===d1&&cO(dg,true,true)}else{if(!d6.hasClass(M)){cg.requestFullScreen()}}}}O(eA,true)}function cD(ex,ey){d5[ex]=dy[ex]=ey}d5=ao(cl,{onStart:dR,onMove:function(ey,ex){co(cf,ex.edge)},onTouchEnd:cr,onEnd:function(ex){var ez;co(cf);ez=(aZ&&!dz||ex.touch)&&c3.arrows;if((ex.moved||(ez&&ex.pos!==ex.newPos&&!ex.control))&&ex.$target[0]!==dM[0]){var ey=by(ex.newPos,en.w,c3.margin,c2);cg.show({index:ey,time:cv?ds:ex.time,overPos:ex.overPos,user:true})}else{if(!ex.aborted&&!ex.control){ct(ex.startEvent,ez)}}},timeLow:1,timeHigh:1,friction:2,select:"."+a9+", ."+a9+" *",$wrap:cf,direction:"horizontal"});dy=ao(cF,{onStart:dR,onMove:function(ey,ex){co(dO,ex.edge)},onTouchEnd:cr,onEnd:function(ex){function ey(){dT.l=ex.newPos;cw();es();dp(ex.newPos,true);ev()}if(!ex.moved){var ez=ex.$target.closest("."+aG,cF)[0];ez&&dJ.call(ez,ex.startEvent)}else{if(ex.pos!==ex.newPos){cj=true;A(cF,{time:ex.time,pos:ex.newPos,overPos:ex.overPos,direction:c3.navdir,onEnd:ey});dp(ex.newPos);ec&&co(dO,bp(ex.newPos,dy.min,dy.max,ex.dir))}else{ey()}}},timeLow:0.5,timeHigh:2,friction:5,$wrap:dO,direction:c3.navdir});cG=o(cf,{shift:true,onEnd:function(ey,ex){dR();cr();cg.show({index:ex,slow:ey.altKey})}});ef=o(dO,{onEnd:function(ez,ey){dR();cr();var ex=a1(cF)+ey*0.25;cF.css(b2(bb(ex,dy.min,dy.max),c3.navdir));ec&&co(dO,bp(ex,dy.min,dy.max,c3.navdir));ef.prevent={"<":ex>=dy.max,">":ex<=dy.min};clearTimeout(ef.t);ef.t=setTimeout(function(){dT.l=ex;dp(ex,true)},b8);dp(ex)}});dk.hover(function(){setTimeout(function(){if(c6){return}cp(!(dz=true))},0)},function(){if(!dz){return}cp(!(dz=false))});function dJ(ey){var ex=bV(this).data().eq;if(c3.navtype==="thumbs"){cm({index:ex,slow:ey.altKey,user:true,coo:ey._x-dO.offset().left})}else{cm({index:ex,slow:ey.altKey,user:true})}}function et(ex){cm({index:cU.index(this)?">":"<",slow:ex.altKey,user:true})}z(cU,function(ex){O(ex);et.call(this,ex)},{onStart:function(){dR();d5.control=true},onTouchEnd:cr});z(eg,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show("<")}else{cg.showSlide("prev")}});z(dX,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show(">")}else{cg.showSlide("next")}});function dv(ex){bL(ex,function(){setTimeout(function(){bU(cf)},0);cp(false)})}cU.each(function(){a(this,function(ex){et.call(this,ex)});dv(this)});a(dD,function(){if(d6.hasClass(M)){cg.cancelFullScreen();cl.focus()}else{cg.requestFullScreen();dM.focus()}});dv(dD);function dV(){dn();dQ();if(!dV.i){dV.i=true;var ex=c3.startindex;eo=c2=ed=dw=dx=du(ex)||0}if(c1){if(cV()){return}if(dg){cO(dg,true)}ea=[];ce(bu);dV.ok=true;cg.show({index:eo,time:0});cg.resize()}else{cg.destroy()}}function cV(){if(!cV.f===dH){cV.f=dH;eo=c1-1-eo;cg.reverse();return true}}bV.each("load push pop shift unshift reverse sort splice".split(" "),function(ex,ey){cg[ey]=function(){dP=dP||[];if(ey!=="load"){Array.prototype[ey].apply(dP,arguments)}else{if(arguments[0]&&typeof arguments[0]==="object"&&arguments[0].length){dP=bG(arguments[0])}}dV();return cg}});function eq(){if(eq.ok){eq.ok=false;dc("ready")}}dV()};bV.fn.fotorama=function(i){return this.each(function(){var ce=this,cd=bV(this),cc=cd.data(),cf=cc.fotorama;if(!cf){bX(function(){return !W(ce)},function(){cc.urtext=cd.html();new bV.Fotorama(cd,bV.extend({},Q,bo.fotoramaDefaults,i,cc))})}else{cf.setOptions(i,true)}})};bV.Fotorama.instances=[];function b1(){bV.each(bV.Fotorama.instances,function(cc,i){i.index=cc})}function C(i){bV.Fotorama.instances.push(i);b1()}function av(i){bV.Fotorama.instances.splice(i.index,1);b1()}bV.Fotorama.cache={};bV.Fotorama.measures={};bV=bV||{};bV.Fotorama=bV.Fotorama||{};bV.Fotorama.jst=bV.Fotorama.jst||{};bV.Fotorama.jst.dots=function(cc){var i,ce="",cd=bx.escape;ce+='';return ce};bV.Fotorama.jst.frameCaption=function(cc){var i,ce="",cd=bx.escape;ce+='\r\n
'+((i=(cc.caption))==null?"":i)+"
\r\n
\r\n";return ce};bV.Fotorama.jst.style=function(cc){var i,ce="",cd=bx.escape;ce+=".fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+((i=(cc.m))==null?"":i)+"px;\r\nheight:"+((i=(cc.h))==null?"":i)+"px}\r\n.fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__thumb-border{\r\nheight:"+((i=(cc.h))==null?"":i)+"px;\r\nborder-width:"+((i=(cc.b))==null?"":i)+"px;\r\nmargin-top:"+((i=(cc.m))==null?"":i)+"px}";return ce};bV.Fotorama.jst.thumb=function(cc){var i,ce="",cd=bx.escape;ce+='';return ce}})(window,document,location,typeof jQuery!=="undefined"&&jQuery);
+fotoramaVersion="4.6.4",function(t,e,n,o,a){"use strict";var i="fotorama",r="fotorama__fullscreen",s=i+"__wrap",u=s+"--css2",l=s+"--css3",c=s+"--video",d=s+"--fade",h=s+"--slide",f=s+"--no-controls",m=s+"--no-shadows",v=s+"--pan-y",p=s+"--rtl",g=s+"--no-captions",w=s+"--toggle-arrows",b=i+"__stage",y=b+"__frame",x=y+"--video",_=b+"__shaft",C=i+"__grab",k=i+"__pointer",P=i+"__arr",S=P+"--disabled",T=P+"--prev",F=P+"--next",E=i+"__nav",M=E+"-wrap",j=E+"__shaft",$=M+"--vertical",z=M+"--list",q=M+"--horizontal",N=E+"--dots",A=E+"--thumbs",L=E+"__frame",O=i+"__fade",D=O+"-front",I=O+"-rear",W=i+"__shadow"+"s",R=W+"--left",H=W+"--right",K=W+"--top",Q=W+"--bottom",V=i+"__active",X=i+"__select",B=i+"--hidden",Y=i+"--fullscreen",U=i+"__fullscreen-icon",G=i+"__error",J=i+"__loading",Z=i+"__loaded",tt=Z+"--full",et=Z+"--img",nt=i+"__grabbing",ot=i+"__img",at=ot+"--full",it=i+"__thumb",rt=it+"__arr--left",st=it+"__arr--right",ut=it+"-border",lt=i+"__html",ct=i+"-video-container",dt=i+"__video",ht=dt+"-play",ft=dt+"-close",mt=i+"_horizontal_ratio",vt=i+"_vertical_ratio",pt=i+"__spinner",gt=pt+"--show",wt=o&&o.fn.jquery.split(".");if(!wt||wt[0]<1||1==wt[0]&&wt[1]<8)throw"Fotorama requires jQuery 1.8 or later and will not run without it.";var bt=function(t,e,n){var o,a,i={},r=e.documentElement,s="modernizr",u=e.createElement(s),l=u.style,c=" -webkit- -moz- -o- -ms- ".split(" "),d="Webkit Moz O ms",h=d.split(" "),f=d.toLowerCase().split(" "),m={},v=[],p=v.slice,g=function(t,n,o,a){var i,u,l,c,d=e.createElement("div"),h=e.body,f=h||e.createElement("body");if(parseInt(o,10))for(;o--;)(l=e.createElement("div")).id=a?a[o]:s+(o+1),d.appendChild(l);return i=["",'"].join(""),d.id=s,(h?d:f).innerHTML+=i,f.appendChild(d),h||(f.style.background="",f.style.overflow="hidden",c=r.style.overflow,r.style.overflow="hidden",r.appendChild(f)),u=n(d,t),h?d.parentNode.removeChild(d):(f.parentNode.removeChild(f),r.style.overflow=c),!!u},w={}.hasOwnProperty;function b(t){l.cssText=t}function y(t,e){return typeof t===e}function x(t,e){for(var o in t){var a=t[o];if(!~(""+a).indexOf("-")&&l[a]!==n)return"pfx"!=e||a}return!1}function _(t,e,o){var a=t.charAt(0).toUpperCase()+t.slice(1),i=(t+" "+h.join(a+" ")+a).split(" ");return y(e,"string")||y(e,"undefined")?x(i,e):function(t,e,o){for(var a in t){var i=e[t[a]];if(i!==n)return!1===o?t[a]:y(i,"function")?i.bind(o||e):i}return!1}(i=(t+" "+f.join(a+" ")+a).split(" "),e,o)}for(var C in a=y(w,"undefined")||y(w.call,"undefined")?function(t,e){return e in t&&y(t.constructor.prototype[e],"undefined")}:function(t,e){return w.call(t,e)},Function.prototype.bind||(Function.prototype.bind=function(t){var e=this;if("function"!=typeof e)throw new TypeError;var n=p.call(arguments,1),o=function(){if(this instanceof o){var a=function(){};a.prototype=e.prototype;var i=new a,r=e.apply(i,n.concat(p.call(arguments)));return Object(r)===r?r:i}return e.apply(t,n.concat(p.call(arguments)))};return o}),m.touch=function(){var n;return"ontouchstart"in t||t.DocumentTouch&&e instanceof DocumentTouch?n=!0:g(["@media (",c.join("touch-enabled),("),s,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(t){n=9===t.offsetTop}),n},m.csstransforms3d=function(){var t=!!_("perspective");return t&&"webkitPerspective"in r.style&&g("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(e,n){t=9===e.offsetLeft&&3===e.offsetHeight}),t},m.csstransitions=function(){return _("transition")},m)a(m,C)&&(o=C.toLowerCase(),i[o]=m[C](),v.push((i[o]?"":"no-")+o));return i.addTest=function(t,e){if("object"==typeof t)for(var o in t)a(t,o)&&i.addTest(o,t[o]);else{if(t=t.toLowerCase(),i[t]!==n)return i;e="function"==typeof e?e():e,"undefined"!=typeof enableClasses&&enableClasses&&(r.className+=" "+(e?"":"no-")+t),i[t]=e}return i},b(""),u=null,i._version="2.8.3",i._prefixes=c,i._domPrefixes=f,i._cssomPrefixes=h,i.testProp=function(t){return x([t])},i.testAllProps=_,i.testStyles=g,i.prefixed=function(t,e,n){return e?_(t,e,n):_(t,"pfx")},i}(t,e),yt={ok:!1,is:function(){return!1},request:function(){},cancel:function(){},event:"",prefix:""},xt="webkit moz o ms khtml".split(" ");if(void 0!==e.cancelFullScreen)yt.ok=!0;else for(var _t=0,Ct=xt.length;_t=n?"bottom":"top bottom":t<=e?"left":t>=n?"right":"left right")}function be(t,e,n){n=n||{},t.each(function(){var t,a=o(this),i=a.data();i.clickOn||(i.clickOn=!0,o.extend(Re(a,{onStart:function(e){t=e,(n.onStart||Jt).call(this,e)},onMove:n.onMove||Jt,onTouchEnd:n.onTouchEnd||Jt,onEnd:function(n){n.moved||e.call(this,t)}}),{noMove:!0}))})}function ye(t,e){return''+(e||"")+"
"}function xe(t){return"."+t}function _e(t){for(var e=t.length;e;){var n=Math.floor(Math.random()*e--),o=t[e];t[e]=t[n],t[n]=o}return t}function Ce(t){return"[object Array]"==Object.prototype.toString.call(t)&&o.map(t,function(t){return o.extend({},t)})}function ke(t,e,n){t.scrollLeft(e||0).scrollTop(n||0)}function Pe(t){if(t){var e={};return o.each(t,function(t,n){e[t.toLowerCase()]=n}),e}}function Se(t){if(t){var e=+t;return isNaN(e)?+(e=t.split("/"))[0]/+e[1]||a:e}}function Te(t,e,n,o){e&&(t.addEventListener?t.addEventListener(e,n,!!o):t.attachEvent("on"+e,n))}function Fe(t,e){return t>e.max?t=e.max:t":"<"}pe.stop=function(t){pe.ii[t]=!1};var qe,Ne,Ae,Le,Oe,De=function(){return{setRatio:function(t,e,n){e/n<=1?(t.parent().removeClass(mt),t.parent().addClass(vt)):(t.parent().removeClass(vt),t.parent().addClass(mt))},setThumbAttr:function(t,e,n){var i=n;t.attr(i)||t.attr(i)===a||t.attr(i,e),t.find("["+i+"]").length&&t.find("["+i+"]").each(function(){o(this).attr(i,e)})},isExpectedCaption:function(t,e,n){var o,a=!1;return o=t.showCaption===n||!0===t.showCaption,!!e&&(t.caption&&o&&(a=!0),a)}}}(jQuery);function Ie(t,e){var n=t.data(),a=Math.round(e.pos),i=function(){n&&n.sliding&&(n.sliding=!1),(e.onEnd||Jt)()};void 0!==e.overPos&&e.overPos!==e.pos&&(a=e.overPos);var r=o.extend(ee(a,e.direction),e.width&&{width:e.width},e.height&&{height:e.height});n&&n.sliding&&(n.sliding=!0),Mt?(t.css(o.extend(ne(e.time),r)),e.time>10?ue(t,"transform",i,e.time):i()):t.stop().animate(r,e.time,Bt,i)}function We(t){var e=(t.touches||[])[0]||t;t._x=e.pageX||e.originalEvent.pageX,t._y=e.clientY||e.originalEvent.clientY,t._now=o.now()}function Re(t,n){var a,i,r,s,u,l,c,d,h,f=t[0],m={};function v(t){if(r=o(t.target),m.checked=l=c=h=!1,a||m.flow||t.touches&&t.touches.length>1||t.which>1||qe&&qe.type!==t.type&&Ae||(l=n.select&&r.is(n.select,f)))return l;u="touchstart"===t.type,c=r.is("a, a *",f),s=m.control,d=m.noMove||m.noSwipe||s?16:m.snap?0:4,We(t),i=qe=t,Ne=t.type.replace(/down|start/,"move").replace(/Down/,"Move"),(n.onStart||Jt).call(f,t,{control:s,$target:r}),a=m.flow=!0,u&&!m.go||$e(t)}function p(t){if(t.touches&&t.touches.length>1||Nt&&!t.isPrimary||Ne!==t.type||!a)return a&&w(),void(n.onTouchEnd||Jt)();We(t);var e=Math.abs(t._x-i._x),o=Math.abs(t._y-i._y),r=e-o,s=(m.go||m.x||r>=0)&&!m.noSwipe,l=r<0;u&&!m.checked?(a=s)&&$e(t):($e(t),g(e,o)&&(n.onMove||Jt).call(f,t,{touch:u})),!h&&g(e,o)&&Math.sqrt(Math.pow(e,2)+Math.pow(o,2))>d&&(h=!0),m.checked=m.checked||s||l}function g(t,e){return t>e&&t>1.5}function w(t){(n.onTouchEnd||Jt)();var e=a;m.control=a=!1,e&&(m.flow=!1),!e||c&&!m.checked||(t&&$e(t),Ae=!0,clearTimeout(Le),Le=setTimeout(function(){Ae=!1},1e3),(n.onEnd||Jt).call(f,{moved:h,$target:r,control:s,touch:u,startEvent:i,aborted:!t||"MSPointerCancel"===t.type}))}function b(){m.flow&&(m.flow=!1)}return Nt?(Te(f,"MSPointerDown",v),Te(e,"MSPointerMove",p),Te(e,"MSPointerCancel",w),Te(e,"MSPointerUp",w)):(Te(f,"touchstart",v),Te(f,"touchmove",p),Te(f,"touchend",w),Te(e,"touchstart",function(){m.flow||(m.flow=!0)}),Te(e,"touchend",b),Te(e,"touchcancel",b),St.on("scroll",b),t.on("mousedown pointerdown",v),Tt.on("mousemove pointermove",p).on("mouseup pointerup",w)),Oe=bt.touch?"a":"div",t.on("click",Oe,function(t){m.checked&&$e(t)}),m}function He(t,e){var n,a,i,r,s,u,l,c,d,h,f,m,v,p,g,w=t[0],b=t.data(),y={};function x(o,s){g=!0,n=a="vertical"===m?o._y:o._x,l=o._now,u=[[l,n]],i=r=y.noMove||s?0:le(t,(e.getPos||Jt)()),(e.onStart||Jt).call(w,o)}return y=o.extend(Re(e.$wrap,o.extend({},e,{onStart:function(e,n){d=y.min,h=y.max,f=y.snap,m=y.direction||"horizontal",t.navdir=m,v=e.altKey,g=p=!1,n.control||b.sliding||x(e)},onMove:function(o,l){y.noSwipe||(g||x(o),a="vertical"===m?o._y:o._x,u.push([o._now,a]),s=we(r=i-(n-a),d,h,m),r<=d?r=de(r,d):r>=h&&(r=de(r,h)),y.noMove||(t.css(ee(r,m)),p||(p=!0,l.touch||Nt||t.addClass(nt)),(e.onMove||Jt).call(w,o,{pos:r,edge:s})))},onEnd:function(n){if(!y.noSwipe||!n.moved){g||x(n.startEvent,!0),n.touch||Nt||t.removeClass(nt);for(var s,l,p,b,_,C,k,P,S,T=(c=o.now())-Lt,F=null,E=Ot,M=e.friction,j=u.length-1;j>=0;j--){if(s=u[j][0],l=Math.abs(s-T),null===F||lp)break;p=l}k=Zt(r,d,h);var $=b-a,z=$>=0,q=c-F,N=q>Lt,A=!N&&r!==i&&k===r;f&&(k=Zt(Math[A?z?"floor":"ceil":"round"](r/f)*f,d,h),d=h=k),A&&(f||k===r)&&(S=-$/q,E*=Zt(Math.abs(S),e.timeLow,e.timeHigh),_=Math.round(r+S*E/M),f||(k=_),(!z&&_>h||z&&_"),ln=o(ye(B)),cn=n.find(xe(s)),dn=cn.find(xe(b)),hn=(dn[0],n.find(xe(_))),fn=o(),mn=n.find(xe(T)),vn=n.find(xe(F)),pn=n.find(xe(P)),gn=n.find(xe(M)),wn=gn.find(xe(E)),bn=wn.find(xe(j)),yn=o(),xn=o(),_n=(hn.data(),bn.data(),n.find(xe(ut))),Cn=n.find(xe(rt)),kn=n.find(xe(st)),Pn=n.find(xe(U)),Sn=Pn[0],Tn=o(ye(ht)),Fn=n.find(xe(ft))[0],En=n.find(xe(pt)),Mn=!1,jn={},$n={},zn={},qn={},Nn={},An={},Ln={},On=0,Dn=[];function In(){o.each(nt,function(t,e){if(!e.i){e.i=rn++;var n=fe(e.video,!0);if(n){var a={};e.video=n,e.img||e.thumb?e.thumbsReady=!0:(r=nt,s=en,"youtube"===(c=(i=e).video).type?(u=(l=he()+"img.youtube.com/vi/"+c.id+"/default.jpg").replace(/\/default.jpg$/,"/hqdefault.jpg"),i.thumbsReady=!0):"vimeo"===c.type?o.ajax({url:he()+"vimeo.com/api/v2/video/"+c.id+".json",dataType:"jsonp",success:function(t){i.thumbsReady=!0,me(r,{img:t[0].thumbnail_large,thumb:t[0].thumbnail_small},i.i,s)}}):i.thumbsReady=!0,a={img:u,thumb:l}),me(nt,{img:a.img,thumb:a.thumb},e.i,en)}}var i,r,s,u,l,c})}function Wn(t){return Re[t]}function Rn(){if(dn!==a)if("vertical"==O.navdir){var t=O.thumbwidth+O.thumbmargin;dn.css("left",t),vn.css("right",t),Pn.css("right",t),cn.css("width",cn.css("width")+t),hn.css("max-width",cn.width()-t)}else dn.css("left",""),vn.css("right",""),Pn.css("right",""),cn.css("width",cn.css("width")+t),hn.css("max-width","")}function Hn(t){var e,a,s,u,l,c,d,h,f;t!==Hn.f&&(t?(n.addClass(i+" "+on).before(ln).before(un),a=en,o.Fotorama.instances.push(a),Qe()):(ln.detach(),un.detach(),n.html(sn.urtext).removeClass(on),e=en,o.Fotorama.instances.splice(e.index,1),Qe()),l="keydown."+i,d="keydown."+(c=i+nn),h="keyup."+c,f="resize."+c+" orientationchange."+c,(s=t)?(Tt.on(d,function(t){var e,n;vt&&27===t.keyCode?(e=!0,Mo(vt,!0,!0)):(en.fullScreen||O.keyboard&&!en.index)&&(27===t.keyCode?(e=!0,en.cancelFullScreen()):t.shiftKey&&32===t.keyCode&&Wn("space")||37===t.keyCode&&Wn("left")||38===t.keyCode&&Wn("up")&&o(":focus").attr("data-gallery-role")?(en.longPress.progress(),n="<"):32===t.keyCode&&Wn("space")||39===t.keyCode&&Wn("right")||40===t.keyCode&&Wn("down")&&o(":focus").attr("data-gallery-role")?(en.longPress.progress(),n=">"):36===t.keyCode&&Wn("home")?(en.longPress.progress(),n="<<"):35===t.keyCode&&Wn("end")&&(en.longPress.progress(),n=">>")),(e||n)&&$e(t),u={index:n,slow:t.altKey,user:!0},n&&(en.longPress.inProgress?en.showWhileLongPress(u):en.show(u))}),s&&Tt.on(h,function(t){en.longPress.inProgress&&en.showEndLongPress({user:!0}),en.longPress.reset()}),en.index||Tt.off(l).on(l,"textarea, input, select",function(t){!Pt.hasClass(r)&&t.stopPropagation()}),St.on(f,en.resize)):(Tt.off(d),St.off(f)),Hn.f=t)}function Kn(){var t=it<2||vt;$n.noMove=t||Te,$n.noSwipe=t||!O.swipe,!Le&&hn.toggleClass(C,!O.click&&!$n.noMove&&!$n.noSwipe),Nt&&cn.toggleClass(v,!$n.noSwipe)}function Qn(t){!0===t&&(t=""),O.autoplay=Math.max(+t||It,1.5*Ae)}function Vn(t,e){return Math.floor(cn.width()/(e.thumbwidth+e.thumbmargin))}function Xn(){var t;O.nav&&"dots"!==O.nav||(O.navdir="horizontal"),en.options=O=Pe(O),Yt=Vn(0,O),Te="crossfade"===O.transition||"dissolve"===O.transition,Dt=O.loop&&(it>2||Te&&(!Le||"slide"!==Le)),Ae=+O.transitionduration||Ot,We="rtl"===O.direction,Re=o.extend({},O.keyboard&&Gt,O.keyboard),(t=O).navarrows&&"thumbs"===t.nav?(Cn.show(),kn.show()):(Cn.hide(),kn.hide());var e,n,a,i,r={add:[],remove:[]};function s(t,e){r[t?"add":"remove"].push(e)}it>1?(Bt=O.nav,oe="top"===O.navposition,r.remove.push(X),pn.toggle(O.arrows)):(Bt=!1,pn.hide()),co(),fo(),ho(),O.autoplay&&Qn(O.autoplay),qe=ae(O.thumbwidth)||Wt,Ne=ae(O.thumbheight)||Wt,zn.ok=Nn.ok=O.trackpad&&!qt,Kn(),yo(O,[jn]),Ut="thumbs"===Bt,gn.filter(":hidden")&&Bt&&gn.show(),Ut?(ao(it,"navThumb"),mt=xn,tn=Vt,e=un,n=o.Fotorama.jst.style({w:qe,h:Ne,b:O.thumbborderwidth,m:O.thumbmargin,s:nn,q:!jt}),(a=e[0]).styleSheet?a.styleSheet.cssText=n:e.html(n),wn.addClass(A).removeClass(N)):"dots"===Bt?(ao(it,"navDot"),mt=yn,tn=Qt,wn.addClass(N).removeClass(A)):(gn.hide(),Bt=!1,wn.removeClass(A+" "+N)),Bt&&(oe?gn.insertBefore(dn):gn.insertAfter(dn),uo.nav=!1,uo(mt,bn,"nav")),(ue=O.allowfullscreen)?(Pn.prependTo(dn),de=$t&&"native"===ue,i="touchend",Pn.on(i,function(t){return $e(t,!0),!1})):(Pn.detach(),de=!1),s(Te,d),s(!Te,h),s(!O.captions,g),s(We,p),s(O.arrows,w),s(!(Oe=O.shadows&&!qt),m),cn.addClass(r.add.join(" ")).removeClass(r.remove.join(" ")),o.extend({},O),Rn()}function Bn(t){return t<0?(it+t%it)%it:t>=it?t%it:t}function Yn(t){return Zt(t,0,it-1)}function Un(t){return Dt?Bn(t):Yn(t)}function Gn(t){return!!(t>0||Dt)&&t-1}function Jn(t){return!!(t1&&nt[i]===r)||r.html||r.deleted||r.video||c||(r.deleted=!0,en.splice(i,1))):(r[m]=v=p,l.$full=null,eo([i],e,n,!0))}function b(){var t=10;pe(function(){return!Je||!t--&&!qt},function(){o.Fotorama.measures[v]=f.measures=o.Fotorama.measures[v]||{width:d.width,height:d.height,ratio:d.width/d.height},to(f.measures.width,f.measures.height,f.measures.ratio,i),h.off("load error").addClass(""+(c?at:ot)).attr("aria-hidden","false").prependTo(s),s.hasClass(y)&&!s.hasClass(ct)&&s.attr("href",h.attr("src")),ge(h,(o.isFunction(n)?n():n)||jn),o.Fotorama.cache[v]=l.state="loaded",setTimeout(function(){s.trigger("f:load").removeClass(J+" "+G).addClass(Z+" "+(c?tt:et)),"stage"===e?g("load"):(r.thumbratio===Xt||!r.thumbratio&&O.thumbratio===Xt)&&(r.thumbratio=f.measures.ratio,Oo())},0)})}})}function no(){var t=wt[Kt];t&&!t.data().state&&(En.addClass(gt),t.on("f:load f:error",function(){t.off("f:load f:error"),En.removeClass(gt)}))}function oo(t){Me(t,No),je(t,function(){setTimeout(function(){ke(wn)},0),po({time:Ae,guessIndex:o(this).data().eq,minMax:qn})})}function ao(t,e){Zn(t,e,function(t,n,a,i,r,s){if(!i){i=a[r]=cn[r].clone(),(s=i.data()).data=a;var u=i[0],l="labelledby"+o.now();"stage"===e?(a.html&&o('
').append(a._html?o(a.html).removeAttr("id").html(a._html):a.html).appendTo(i),a.id&&(l=a.id||l),a.labelledby=l,De.isExpectedCaption(a,O.showcaption)&&o(o.Fotorama.jst.frameCaption({caption:a.caption,labelledby:l})).appendTo(i),a.video&&i.addClass(x).append(Tn.clone()),je(u,function(){setTimeout(function(){ke(dn)},0),zo({index:s.eq,user:!0})}),fn=fn.add(i)):"navDot"===e?(oo(u),yn=yn.add(i)):"navThumb"===e&&(oo(u),s.$wrap=i.children(":first"),xn=xn.add(i),a.video&&s.$wrap.append(Tn.clone()))}})}function io(t,e){return t&&t.length&&ge(t,e)}function ro(t){Zn(t,"stage",function(t,n,a,i,r,s){if(i){var u,l=Bn(n);s.eq=l,Ln[Kt][l]=i.css(o.extend({left:Te?0:se(n,jn.w,O.margin,xt)},Te&&ne(0))),u=i[0],o.contains(e.documentElement,u)||(i.appendTo(hn),Mo(a.$video)),io(s.$img,jn),io(s.$full,jn),!i.hasClass(y)||"false"===i.attr("aria-hidden")&&i.hasClass(V)||i.attr("aria-hidden","true")}})}function so(t,e){var n,a;"thumbs"!==Bt||isNaN(t)||(n=-t,a=-t+jn.nw,"vertical"===O.navdir&&(t-=O.thumbheight,a=-t+jn.h),xn.each(function(){var t=o(this).data(),i=t.eq,r=function(){return{h:Ne,w:t.w}},s=r(),u="vertical"===O.navdir?t.t>a:t.l>a;s.w=t.w,t.l+t.w=qn.max,e=n<=qn.min}Cn.toggleClass(S,t).attr(Ee(t,!0)),kn.toggleClass(S,e).attr(Ee(e,!0))}function fo(){zn.ok&&(zn.prevent={"<":lo(0),">":lo(1)})}function mo(t){var e,n,o,a,i=t.data();Ut?(e=i.l,n=i.t,o=i.w,a=i.h):(e=t.position().left,o=t.width());var r={c:e+o/2,min:-e+10*O.thumbmargin,max:-e+jn.w-o-10*O.thumbmargin},s={c:n+a/2,min:-n+10*O.thumbmargin,max:-n+jn.h-a-10*O.thumbmargin};return"vertical"===O.navdir?s:r}function vo(t){var e=wt[tn].data();Ie(_n,{time:1.2*t,pos:"vertical"===O.navdir?e.t:e.l,width:e.w,height:e.h,direction:O.navdir})}function po(t){var e,n,o,a,i,r,s,u,l,c,d,h,f,m,v,p,g,w=nt[t.guessIndex][tn],b=O.navtype;w&&("thumbs"===b?(e=qn.min!==qn.max,o=t.minMax||e&&mo(wt[tn]),a=e&&(t.keep&&po.t?po.l:Zt((t.coo||jn.nw/2)-mo(w).c,o.min,o.max)),i=e&&(t.keep&&po.l?po.l:Zt((t.coo||jn.nw/2)-mo(w).c,o.min,o.max)),r="vertical"===O.navdir?a:i,s=e&&Zt(r,qn.min,qn.max)||0,n=1.1*t.time,Ie(bn,{time:n,pos:s,direction:O.navdir,onEnd:function(){so(s,!0),ho()}}),Eo(wn,we(s,qn.min,qn.max,O.navdir)),po.l=r):(u=te(bn,O.navdir),n=1.11*t.time,l=O,c=qn,d=t.guessIndex,h=u,f=w,m=gn,"horizontal"===(v=O.navdir)?(p=l.thumbwidth,g=m.width()):(p=l.thumbheight,g=m.height()),s=Fe((p+l.margin)*(d+1)>=g-h?"horizontal"===v?-f.position().left:-f.position().top:(p+l.margin)*d<=Math.abs(h)?"horizontal"===v?-f.position().left+g-(p+l.margin):-f.position().top+g-(p+l.margin):h,c)||0,Ie(bn,{time:n,pos:s,direction:O.navdir,onEnd:function(){so(s,!0),ho()}}),Eo(wn,we(s,qn.min,qn.max,O.navdir))))}function go(t){for(var e=An[t];e.length;)e.shift().removeClass(V).attr("data-active",!1)}function wo(t){var e=Ln[t];o.each(bt,function(t,n){delete e[Bn(n)]}),o.each(e,function(t,n){delete e[t],n.detach()})}function bo(t){xt=_t=Mn;var e,o,a,i=wt[Kt];i&&(go(Kt),An[Kt].push(i.addClass(V).attr("data-active",!0)),i.hasClass(y)&&i.attr("aria-hidden","false"),t||en.showStage.onEnd(!0),le(hn,0),wo(Kt),ro(bt),$n.min=Dt?-1/0:-se(it-1,jn.w,O.margin,xt),$n.max=Dt?1/0:-se(0,jn.w,O.margin,xt),$n.snap=jn.w+O.margin,e="vertical"===O.navdir,o=e?bn.height():bn.width(),a=e?jn.h:jn.nw,qn.min=Math.min(0,a-o),qn.max=0,qn.direction=O.navdir,bn.toggleClass(C,!(qn.noMove=qn.min===qn.max)),Me(hn[0],function(){n.hasClass(Y)||(en.requestFullScreen(),Pn.focus())}))}function yo(t,e){t&&o.each(e,function(e,n){n&&o.extend(n,{width:t.width||n.width,height:t.height,minwidth:t.minwidth,maxwidth:t.maxwidth,minheight:t.minheight,maxheight:t.maxheight,ratio:Se(t.ratio)})})}function xo(t,e){n.trigger(i+":"+t,[en,e])}function _o(){clearTimeout(Co.t),Je=1,O.stopautoplayontouch?en.stopAutoplay():Ye=!0}function Co(){Je&&(O.stopautoplayontouch||(ko(),Po()),Co.t=setTimeout(function(){Je=0},Ot+Lt))}function ko(){Ye=!(!vt&&!Ue)}function Po(){if(clearTimeout(Po.t),pe.stop(Po.w),O.autoplay&&!Ye){en.autoplay||(en.autoplay=!0,xo("startautoplay"));var t=Mn,e=wt[Kt].data();Po.w=pe(function(){return e.state||t!==Mn},function(){Po.t=setTimeout(function(){if(!Ye&&t===Mn){var e=zt,n=nt[e][Kt].data();Po.w=pe(function(){return n.state||e!==zt},function(){Ye||e!==zt||en.show(Dt?ze(!We):zt)})}},O.autoplay)})}else en.autoplay&&(en.autoplay=!1,xo("stopautoplay"))}function So(t){var e;return"object"!=typeof t?(e=t,t={}):e=t.index,e=">"===e?_t+1:"<"===e?_t-1:"<<"===e?0:">>"===e?it-1:e,e=void 0===(e=isNaN(e)?a:e)?Mn||0:e}function To(t){en.activeIndex=Mn=Un(t),Ft=Gn(Mn),Et=Jn(Mn),zt=Bn(Mn+(We?-1:1)),bt=[Mn,Ft,Et],_t=Dt?t:Mn}function Fo(t){var e=Math.abs(Ct-_t),n=ce(t.time,function(){return Math.min(Ae*(1+(e-1)/12),2*Ae)});return t.slow&&(n*=10),n}function Eo(t,e){Oe&&(t.removeClass(R+" "+H),t.removeClass(K+" "+Q),e&&!vt&&t.addClass(e.replace(/^|\s/g," "+W+"--")))}function Mo(t,e,n){e&&(cn.removeClass(c),vt=!1,Kn()),t&&t!==vt&&(t.remove(),xo("unloadvideo")),n&&(ko(),Po())}function jo(t){cn.toggleClass(f,t)}function $o(t){if(!$n.flow){var e,n=t?t.pageX:$o.x,o=n&&!lo((e=n,e-On>jn.w/3))&&O.click;$o.p!==o&&dn.toggleClass(k,o)&&($o.p=o,$o.x=n)}}function zo(t){clearTimeout(zo.t),O.clicktransition&&O.clicktransition!==O.transition?setTimeout(function(){var e=O.transition;en.setOptions({transition:O.clicktransition}),Le=e,zo.t=setTimeout(function(){en.show(t)},10)},0):en.show(t)}function qo(t,e){$n[t]=qn[t]=e}function No(t){var e=o(this).data().eq;"thumbs"===O.navtype?zo({index:e,slow:t.altKey,user:!0,coo:t._x-wn.offset().left}):zo({index:e,slow:t.altKey,user:!0})}function Ao(t){zo({index:pn.index(this)?">":"<",slow:t.altKey,user:!0})}function Lo(t){je(t,function(){setTimeout(function(){ke(dn)},0),jo(!1)})}function Oo(){if(nt=en.data=nt||Ce(O.data)||ve(n),it=en.size=nt.length,Do.ok&&O.shuffle&&_e(nt),In(),Mn=Yn(Mn),it&&Hn(!0),Xn(),!Oo.i){Oo.i=!0;var t=O.startindex;Mn=xt=_t=Ct=At=Un(t)||0}if(it){if(function t(){if(!t.f===We)return t.f=We,Mn=it-1-Mn,en.reverse(),!0}())return;vt&&Mo(vt,!0),bt=[],wo(Kt),Oo.ok=!0,en.show({index:Mn,time:0}),en.resize()}else en.destroy()}function Do(){Do.ok&&(Do.ok=!1,xo("ready"))}cn[Kt]=o('
'),cn[Vt]=o(o.Fotorama.jst.thumb()),cn[Qt]=o(o.Fotorama.jst.dots()),An[Kt]=[],An[Vt]=[],An[Qt]=[],Ln[Kt]={},cn.addClass(Mt?l:u),sn.fotorama=this,en.startAutoplay=function(t){return en.autoplay?this:(Ye=Ue=!1,Qn(t||O.autoplay),Po(),this)},en.stopAutoplay=function(){return en.autoplay&&(Ye=Ue=!0,Po()),this},en.showSlide=function(t){var e,n=te(bn,O.navdir),o="horizontal"===O.navdir?O.thumbwidth:O.thumbheight;"next"===t&&(e=n-(o+O.margin)*Yt),"prev"===t&&(e=n+(o+O.margin)*Yt),so(e=Fe(e,qn),!0),Ie(bn,{time:550,pos:e,direction:O.navdir,onEnd:function(){ho()}})},en.showWhileLongPress=function(t){if(!en.longPress.singlePressInProgress){To(So(t));var e=Fo(t)/50,n=wt;en.activeFrame=wt=nt[Mn];var o=n===wt&&!t.user;return en.showNav(o,t,e),this}},en.showEndLongPress=function(t){if(!en.longPress.singlePressInProgress){To(So(t));var e=Fo(t)/50,n=wt;en.activeFrame=wt=nt[Mn];var o=n===wt&&!t.user;return en.showStage(o,t,e),void 0!==Ct&&Ct!==Mn,Ct=Mn,this}},en.showStage=function(t,e,n){Mo(vt,wt.i!==nt[Bn(xt)].i),ao(bt,"stage"),ro(qt?[_t]:[_t,Gn(_t),Jn(_t)]),qo("go",!0),t||xo("show",{user:e.user,time:n}),Ye=!0;var a=e.overPos,i=en.showStage.onEnd=function(n){if(!i.ok){if(i.ok=!0,n||bo(!0),t||xo("showend",{user:e.user}),!n&&Le&&Le!==O.transition)return en.setOptions({transition:Le}),void(Le=!1);no(),eo(bt,"stage"),qo("go",!1),fo(),$o(),ko(),Po(),en.fullScreen?(wt[Kt].find("."+at).attr("aria-hidden",!1),wt[Kt].find("."+ot).attr("aria-hidden",!0)):(wt[Kt].find("."+at).attr("aria-hidden",!0),wt[Kt].find("."+ot).attr("aria-hidden",!1))}};Te?function t(e,n,a,i,r,s){var u=void 0!==s;if(u||(r.push(arguments),Array.prototype.push.call(arguments,r.length),!(r.length>1))){e=e||o(e),n=n||o(n);var l=e[0],c=n[0],d="crossfade"===i.method,h=function(){if(!h.done){h.done=!0;var e=(u||r.shift())&&r.shift();e&&t.apply(this,e),(i.onEnd||Jt)(!!e)}},f=i.time/(s||1);a.removeClass(I+" "+D),e.stop().addClass(I),n.stop().addClass(D),d&&c&&e.fadeTo(0,0),e.fadeTo(d?f:0,1,d&&h),n.fadeTo(f,0,h),l&&d||c||h()}}(wt[Kt],nt[Ct]&&Mn!==Ct?nt[Ct][Kt]:null,fn,{time:n,method:O.transition,onEnd:i},Dn):Ie(hn,{pos:-se(_t,jn.w,O.margin,xt),overPos:a,time:n,onEnd:i});co()},en.showNav=function(t,e,n){if(ho(),Bt){go(tn),An[tn].push(wt[tn].addClass(V).attr("data-active",!0));var o=Yn(Mn+Zt(_t-Ct,-1,1));po({time:n,coo:o!==Mn&&e.coo,guessIndex:void 0!==e.coo?o:Mn,keep:t}),Ut&&vo(n)}},en.show=function(t){en.longPress.singlePressInProgress=!0,To(So(t));var e=Fo(t),n=wt;en.activeFrame=wt=nt[Mn];var o=n===wt&&!t.user;return en.showStage(o,t,e),en.showNav(o,t,e),void 0!==Ct&&Ct!==Mn,Ct=Mn,en.longPress.singlePressInProgress=!1,this},en.requestFullScreen=function(){if(ue&&!en.fullScreen){if(o((en.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container"))return;Xe=St.scrollTop(),Be=St.scrollLeft(),ke(St),qo("x",!0),Ge=o.extend({},jn),n.addClass(Y).appendTo(Pt.addClass(r)),kt.addClass(r),Mo(vt,!0,!0),en.fullScreen=!0,de&&yt.request(an),en.resize(),eo(bt,"stage"),no(),xo("fullscreenenter"),"ontouchstart"in t||Pn.focus()}return this},en.cancelFullScreen=function(){return de&&yt.is()?yt.cancel(e):en.fullScreen&&(en.fullScreen=!1,$t&&yt.cancel(an),Pt.removeClass(r),kt.removeClass(r),n.removeClass(Y).insertAfter(ln),jn=o.extend({},Ge),Mo(vt,!0,!0),qo("x",!1),en.resize(),eo(bt,"stage"),ke(St,Be,Xe),xo("fullscreenexit")),this},en.toggleFullScreen=function(){return en[(en.fullScreen?"cancel":"request")+"FullScreen"]()},en.resize=function(e){if(!nt)return this;var n=arguments[1]||0,a=arguments[2];Yt=Vn(0,O),yo(en.fullScreen?{width:o(t).width(),maxwidth:null,minwidth:null,height:o(t).height(),maxheight:null,minheight:null}:Pe(e),[jn,a||en.fullScreen||O]);var i=jn.width,r=jn.height,s=jn.ratio,u=St.height()-(Bt?wn.height():0);if(re(i)&&(cn.css({width:""}),cn.css({height:""}),dn.css({width:""}),dn.css({height:""}),hn.css({width:""}),hn.css({height:""}),wn.css({width:""}),wn.css({height:""}),cn.css({minWidth:jn.minwidth||0,maxWidth:jn.maxwidth||1200}),"dots"===Bt&&gn.hide(),i=jn.W=jn.w=cn.width(),jn.nw=Bt&&ie(O.navwidth,i)||i,hn.css({width:jn.w,marginLeft:(jn.W-jn.w)/2}),r=(r=ie(r,u))||s&&i/s)){if(i=Math.round(i),r=jn.h=Math.round(Zt(r,ie(jn.minheight,u),ie(jn.maxheight,u))),dn.css({width:i,height:r}),"vertical"!==O.navdir||en.fullscreen||wn.width(O.thumbwidth+2*O.thumbmargin),"horizontal"!==O.navdir||en.fullscreen||wn.height(O.thumbheight+2*O.thumbmargin),"dots"===Bt&&(wn.width(i).height("auto"),gn.show()),"vertical"===O.navdir&&en.fullScreen&&dn.css("height",St.height()),"horizontal"===O.navdir&&en.fullScreen&&dn.css("height",St.height()-wn.height()),Bt){switch(O.navdir){case"vertical":gn.removeClass(q),gn.removeClass(z),gn.addClass($),wn.stop().animate({height:jn.h,width:O.thumbwidth},n);break;case"list":gn.removeClass($),gn.removeClass(q),gn.addClass(z);break;default:gn.removeClass($),gn.removeClass(z),gn.addClass(q),wn.stop().animate({width:jn.nw},n)}bo(),po({guessIndex:Mn,time:n,keep:!0}),Ut&&uo.nav&&vo(n)}Ve=a||!0,Do.ok=!0,Do()}return On=dn.offset().left,Rn(),this},en.setOptions=function(t){return o.extend(O,t),Oo(),this},en.shuffle=function(){return nt&&_e(nt)&&Oo(),this},en.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){this.inProgress||(this.count++,this.inProgress=this.count>this.threshold)},end:function(){this.inProgress&&(this.isEnded=!0)},reset:function(){this.count=0,this.inProgress=!1,this.isEnded=!1}},en.destroy=function(){return en.cancelFullScreen(),en.stopAutoplay(),nt=en.data=null,Hn(),bt=[],wo(Kt),Oo.ok=!1,this},en.playVideo=function(){var t=wt,e=t.video,n=Mn;return"object"==typeof e&&t.videoReady&&(de&&en.fullScreen&&en.cancelFullScreen(),pe(function(){return!yt.is()||n!==Mn},function(){var a;n===Mn&&(t.$video=t.$video||o(ye(dt)).append(''),t.$video.appendTo(t[Kt]),cn.addClass(c),vt=t.$video,Kn(),pn.blur(),Pn.blur(),xo("loadvideo"))})),this},en.stopVideo=function(){return Mo(vt,!0,!0),this},en.spliceByIndex=function(t,e){e.i=t+1,e.img&&o.ajax({url:e.img,type:"HEAD",success:function(){nt.splice(t,1,e),Oo()}})},dn.on("mousemove",$o),$n=He(hn,{onStart:_o,onMove:function(t,e){Eo(dn,e.edge)},onTouchEnd:Co,onEnd:function(t){var e,a,i,r,s,u,l;if(Eo(dn),e=(Nt&&!Ze||t.touch)&&O.arrows,(t.moved||e&&t.pos!==t.newPos&&!t.control)&&t.$target[0]!==Pn[0]){var c=(r=t.newPos,s=jn.w,u=O.margin,l=xt,-Math.round(r/(s+(u||0))-(l||0)));en.show({index:c,time:Te?Ae:t.time,overPos:t.overPos,user:!0})}else t.aborted||t.control||(a=t.startEvent,i=a.target,o(i).hasClass(ht)?en.playVideo():i===Sn?en.toggleFullScreen():vt?i===Fn&&Mo(vt,!0,!0):n.hasClass(Y)||en.requestFullScreen())},timeLow:1,timeHigh:1,friction:2,select:"."+X+", ."+X+" *",$wrap:dn,direction:"horizontal"}),qn=He(bn,{onStart:_o,onMove:function(t,e){Eo(wn,e.edge)},onTouchEnd:Co,onEnd:function(t){function e(){po.l=t.newPos,ko(),Po(),so(t.newPos,!0),ho()}if(t.moved)t.pos!==t.newPos?(Ye=!0,Ie(bn,{time:t.time,pos:t.newPos,overPos:t.overPos,direction:O.navdir,onEnd:e}),so(t.newPos),Oe&&Eo(wn,we(t.newPos,qn.min,qn.max,t.dir))):e();else{var n=t.$target.closest("."+L,bn)[0];n&&No.call(n,t.startEvent)}},timeLow:.5,timeHigh:2,friction:5,$wrap:wn,direction:O.navdir}),zn=Ke(dn,{shift:!0,onEnd:function(t,e){_o(),Co(),en.show({index:e,slow:t.altKey})}}),Nn=Ke(wn,{onEnd:function(t,e){_o(),Co();var n=le(bn)+.25*e;bn.css(ee(Zt(n,qn.min,qn.max),O.navdir)),Oe&&Eo(wn,we(n,qn.min,qn.max,O.navdir)),Nn.prevent={"<":n>=qn.max,">":n<=qn.min},clearTimeout(Nn.t),Nn.t=setTimeout(function(){po.l=n,so(n,!0)},Lt),so(n)}}),cn.hover(function(){setTimeout(function(){Je||jo(!(Ze=!0))},0)},function(){Ze&&jo(!(Ze=!1))}),be(pn,function(t){$e(t),Ao.call(this,t)},{onStart:function(){_o(),$n.control=!0},onTouchEnd:Co}),be(Cn,function(t){$e(t),"thumbs"===O.navtype?en.show("<"):en.showSlide("prev")}),be(kn,function(t){$e(t),"thumbs"===O.navtype?en.show(">"):en.showSlide("next")}),pn.each(function(){Me(this,function(t){Ao.call(this,t)}),Lo(this)}),Me(Sn,function(){n.hasClass(Y)?(en.cancelFullScreen(),hn.focus()):(en.requestFullScreen(),Pn.focus())}),Lo(Sn),o.each("load push pop shift unshift reverse sort splice".split(" "),function(t,e){en[e]=function(){return nt=nt||[],"load"!==e?Array.prototype[e].apply(nt,arguments):arguments[0]&&"object"==typeof arguments[0]&&arguments[0].length&&(nt=Ce(arguments[0])),Oo(),en}}),Oo()},o.fn.fotorama=function(e){return this.each(function(){var n=this,a=o(this),i=a.data(),r=i.fotorama;r?r.setOptions(e,!0):pe(function(){return!(0===(t=n).offsetWidth&&0===t.offsetHeight);var t},function(){i.urtext=a.html(),new o.Fotorama(a,o.extend({},Ut,t.fotoramaDefaults,e,i))})})},o.Fotorama.instances=[],o.Fotorama.cache={},o.Fotorama.measures={},(o=o||{}).Fotorama=o.Fotorama||{},o.Fotorama.jst=o.Fotorama.jst||{},o.Fotorama.jst.dots=function(t){return'',''},o.Fotorama.jst.frameCaption=function(t){var e,n="";return n+='\r\n
'+(null==(e=t.caption)?"":e)+"
\r\n
\r\n"},o.Fotorama.jst.style=function(t){var e,n="";return n+=".fotorama"+(null==(e=t.s)?"":e)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+(null==(e=t.m)?"":e)+"px;\r\nheight:"+(null==(e=t.h)?"":e)+"px}\r\n.fotorama"+(null==(e=t.s)?"":e)+" .fotorama__thumb-border{\r\nheight:"+(null==(e=t.h)?"":e)+"px;\r\nborder-width:"+(null==(e=t.b)?"":e)+"px;\r\nmargin-top:"+(null==(e=t.m)?"":e)+"px}"},o.Fotorama.jst.thumb=function(t){return'',''}}(window,document,location,"undefined"!=typeof jQuery&&jQuery);
\ No newline at end of file
diff --git a/lib/web/mage/adminhtml/tools.js b/lib/web/mage/adminhtml/tools.js
index ed4bab7102ae5..27f6efcfc5876 100644
--- a/lib/web/mage/adminhtml/tools.js
+++ b/lib/web/mage/adminhtml/tools.js
@@ -348,7 +348,7 @@ var Fieldset = {
},
saveState: function (url, parameters) {
new Ajax.Request(url, {
- method: 'get',
+ method: 'post',
parameters: Object.toQueryString(parameters),
loaderArea: false
});
diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js
index fee0104efe87a..23f24bfcd1a9b 100755
--- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js
+++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js
@@ -238,10 +238,18 @@ define([
* @param {Object} o
*/
openFileBrowser: function (o) {
- var typeTitle,
- storeId = this.config['store_id'] !== null ? this.config['store_id'] : 0,
- frameDialog = jQuery(o.win.frameElement).parents('[role="dialog"]'),
- wUrl = this.config['files_browser_window_url'] +
+ var targetElementID = tinyMCE.activeEditor.getElement().getAttribute('id'),
+ originId = this.id,
+ typeTitle,
+ storeId,
+ frameDialog,
+ wUrl;
+
+ this.initialize(targetElementID, this.config);
+
+ storeId = this.config['store_id'] !== null ? this.config['store_id'] : 0;
+ frameDialog = jQuery(o.win.frameElement).parents('[role="dialog"]');
+ wUrl = this.config['files_browser_window_url'] +
'target_element_id/' + this.id + '/' +
'store/' + storeId + '/';
@@ -255,6 +263,8 @@ define([
typeTitle = this.translate('Insert File...');
}
+ this.initialize(originId, this.config);
+
frameDialog.hide();
jQuery('#mceModalBlocker').hide();
diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js
index 15c3d01cf2be3..be78856b21fcd 100644
--- a/lib/web/mage/gallery/gallery.js
+++ b/lib/web/mage/gallery/gallery.js
@@ -141,7 +141,7 @@ define([
this.setupBreakpoints();
this.initFullscreenSettings();
- this.settings.$element.on('mouseup', '.fotorama__stage__frame', function () {
+ this.settings.$element.on('click', '.fotorama__stage__frame', function () {
if (
!$(this).parents('.fotorama__shadows--left, .fotorama__shadows--right').length &&
!$(this).hasClass('fotorama-video-container')
diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js
index a742b8e6bbb27..8c19669699b9d 100644
--- a/lib/web/mage/validation.js
+++ b/lib/web/mage/validation.js
@@ -1425,10 +1425,14 @@
],
'validate-per-page-value-list': [
function (v) {
- var isValid = !$.mage.isEmpty(v),
+ var isValid = true,
values = v.split(','),
i;
+ if ($.mage.isEmpty(v)) {
+ return isValid;
+ }
+
for (i = 0; i < values.length; i++) {
if (!/^[0-9]+$/.test(values[i])) {
isValid = false;
@@ -1944,7 +1948,7 @@
}
if (firstActive.length) {
- $('html, body').animate({
+ $('html, body').stop().animate({
scrollTop: firstActive.offset().top
});
firstActive.focus();
diff --git a/lib/web/tiny_mce/themes/advanced/js/source_editor.js b/lib/web/tiny_mce/themes/advanced/js/source_editor.js
index 9cf6b1a29cdaf..e90ee4d99628d 100644
--- a/lib/web/tiny_mce/themes/advanced/js/source_editor.js
+++ b/lib/web/tiny_mce/themes/advanced/js/source_editor.js
@@ -10,8 +10,9 @@ function onLoadInit() {
tinyMCEPopup.resizeToInnerSize();
// Remove Gecko spellchecking
- if (tinymce.isGecko)
- document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck");
+ if (tinymce.isGecko) {
+ document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck", false);
+ }
document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent({source_view : true});
diff --git a/nginx.conf.sample b/nginx.conf.sample
index 90604808f6ec0..2104a920258f8 100644
--- a/nginx.conf.sample
+++ b/nginx.conf.sample
@@ -33,6 +33,11 @@ charset UTF-8;
error_page 404 403 = /errors/404.php;
#add_header "X-UA-Compatible" "IE=Edge";
+# Deny access to sensitive files
+location /.user.ini {
+ deny all;
+}
+
# PHP entry point for setup application
location ~* ^/setup($|/) {
root $MAGE_ROOT;
@@ -159,6 +164,11 @@ location /media/downloadable/ {
location /media/import/ {
deny all;
}
+location /errors/ {
+ location ~* \.xml$ {
+ deny all;
+ }
+}
# PHP entry point for main application
location ~ ^/(index|get|static|errors/report|errors/404|errors/503|health_check)\.php$ {
@@ -198,6 +208,6 @@ gzip_types
gzip_vary on;
# Banned locations (only reached if the earlier PHP entry point regexes don't match)
-location ~* (\.php$|\.htaccess$|\.git) {
+location ~* (\.php$|\.phtml$|\.htaccess$|\.git) {
deny all;
}
diff --git a/pub/.htaccess b/pub/.htaccess
index 8ba04ff4415f3..9f07f3319837e 100644
--- a/pub/.htaccess
+++ b/pub/.htaccess
@@ -220,6 +220,16 @@ ErrorDocument 403 /errors/404.php
Require all denied
+## Deny access to .user.ini##
+
+
+ order allow,deny
+ deny from all
+
+ = 2.4>
+ Require all denied
+
+
############################################
diff --git a/pub/errors/.htaccess b/pub/errors/.htaccess
index 3692dd439e2ff..a7b9cbda05893 100644
--- a/pub/errors/.htaccess
+++ b/pub/errors/.htaccess
@@ -1,4 +1,7 @@
Options None
+
+ Deny from all
+
RewriteEngine Off
diff --git a/pub/media/.htaccess b/pub/media/.htaccess
index 28e65b490fbb8..d8793a891430a 100644
--- a/pub/media/.htaccess
+++ b/pub/media/.htaccess
@@ -23,6 +23,9 @@ SetHandler default-handler
Options +FollowSymLinks
RewriteEngine on
+ ## you can put here your pub/media folder path relative to web root
+ #RewriteBase /magento/pub/media/
+
############################################
## never rewrite for existing files
RewriteCond %{REQUEST_FILENAME} !-f
diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx
index 64ff3bc9cba60..73403e543e918 100644
--- a/setup/performance-toolkit/benchmark.jmx
+++ b/setup/performance-toolkit/benchmark.jmx
@@ -32789,7 +32789,15 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate
-
+
+
+ true
+ ${admin_form_key}
+ =
+ true
+ form_key
+
+
@@ -32798,7 +32806,7 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate
${request_protocol}
${base_path}${admin_path}/catalog/category/delete/id/${admin_category_id}/
- GET
+ POST
true
false
true
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php
index fa79139e73313..29a868b1f0eb2 100644
--- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php
@@ -44,7 +44,8 @@ class ConfigOptionsList implements ConfigOptionsListInterface
private $configOptionsListClasses = [
\Magento\Setup\Model\ConfigOptionsList\Session::class,
\Magento\Setup\Model\ConfigOptionsList\Cache::class,
- \Magento\Setup\Model\ConfigOptionsList\PageCache::class
+ \Magento\Setup\Model\ConfigOptionsList\PageCache::class,
+ \Magento\Setup\Model\ConfigOptionsList\Lock::class,
];
/**
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
index 04ec83a3d0ca2..8bdfd2b0a91a5 100644
--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
@@ -27,12 +27,14 @@ class Cache implements ConfigOptionsListInterface
const INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE = 'cache-backend-redis-db';
const INPUT_KEY_CACHE_BACKEND_REDIS_PORT = 'cache-backend-redis-port';
const INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD = 'cache-backend-redis-password';
+ const INPUT_KEY_CACHE_ID_PREFIX = 'cache-id-prefix';
const CONFIG_PATH_CACHE_BACKEND = 'cache/frontend/default/backend';
const CONFIG_PATH_CACHE_BACKEND_SERVER = 'cache/frontend/default/backend_options/server';
const CONFIG_PATH_CACHE_BACKEND_DATABASE = 'cache/frontend/default/backend_options/database';
const CONFIG_PATH_CACHE_BACKEND_PORT = 'cache/frontend/default/backend_options/port';
const CONFIG_PATH_CACHE_BACKEND_PASSWORD = 'cache/frontend/default/backend_options/password';
+ const CONFIG_PATH_CACHE_ID_PREFIX = 'cache/frontend/default/id_prefix';
/**
* @var array
@@ -112,6 +114,12 @@ public function getOptions()
TextConfigOption::FRONTEND_WIZARD_TEXT,
self::CONFIG_PATH_CACHE_BACKEND_PASSWORD,
'Redis server password'
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_CACHE_ID_PREFIX,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_CACHE_ID_PREFIX,
+ 'ID prefix for cache keys'
)
];
}
@@ -122,6 +130,11 @@ public function getOptions()
public function createConfig(array $options, DeploymentConfig $deploymentConfig)
{
$configData = new ConfigData(ConfigFilePool::APP_ENV);
+ if (isset($options[self::INPUT_KEY_CACHE_ID_PREFIX])) {
+ $configData->set(self::CONFIG_PATH_CACHE_ID_PREFIX, $options[self::INPUT_KEY_CACHE_ID_PREFIX]);
+ } else {
+ $configData->set(self::CONFIG_PATH_CACHE_ID_PREFIX, $this->generateCachePrefix());
+ }
if (isset($options[self::INPUT_KEY_CACHE_BACKEND])) {
if ($options[self::INPUT_KEY_CACHE_BACKEND] == self::INPUT_VALUE_CACHE_REDIS) {
@@ -241,4 +254,14 @@ private function getDefaultConfigValue($inputKey)
return '';
}
}
+
+ /**
+ * Generate default cache ID prefix based on installation dir
+ *
+ * @return string
+ */
+ private function generateCachePrefix(): string
+ {
+ return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
+ }
}
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php
new file mode 100644
index 0000000000000..66f41128c46b1
--- /dev/null
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php
@@ -0,0 +1,342 @@
+ [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ self::INPUT_KEY_LOCK_DB_PREFIX => self::CONFIG_PATH_LOCK_DB_PREFIX,
+ ],
+ LockBackendFactory::LOCK_ZOOKEEPER => [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ self::INPUT_KEY_LOCK_ZOOKEEPER_HOST => self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
+ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
+ ],
+ LockBackendFactory::LOCK_CACHE => [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ ],
+ LockBackendFactory::LOCK_FILE => [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ self::INPUT_KEY_LOCK_FILE_PATH => self::CONFIG_PATH_LOCK_FILE_PATH,
+ ],
+ ];
+
+ /**
+ * The list of default values
+ *
+ * @var array
+ */
+ private $defaultConfigValues = [
+ self::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB,
+ self::INPUT_KEY_LOCK_DB_PREFIX => null,
+ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => ZookeeperLock::DEFAULT_PATH,
+ ];
+
+ /**
+ * @inheritdoc
+ */
+ public function getOptions()
+ {
+ return [
+ new SelectConfigOption(
+ self::INPUT_KEY_LOCK_PROVIDER,
+ SelectConfigOption::FRONTEND_WIZARD_SELECT,
+ $this->validLockProviders,
+ self::CONFIG_PATH_LOCK_PROVIDER,
+ 'Lock provider name',
+ LockBackendFactory::LOCK_DB
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_DB_PREFIX,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_DB_PREFIX,
+ 'Installation specific lock prefix to avoid lock conflicts'
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_ZOOKEEPER_HOST,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
+ 'Host and port to connect to Zookeeper cluster. For example: 127.0.0.1:2181'
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
+ 'The path where Zookeeper will save locks. The default path is: ' . ZookeeperLock::DEFAULT_PATH
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_FILE_PATH,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_FILE_PATH,
+ 'The path where file locks will be saved.'
+ ),
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function createConfig(array $options, DeploymentConfig $deploymentConfig)
+ {
+ $configData = new ConfigData(ConfigFilePool::APP_ENV);
+ $configData->setOverrideWhenSave(true);
+ $lockProvider = $this->getLockProvider($options, $deploymentConfig);
+
+ $this->setDefaultConfiguration($configData, $deploymentConfig, $lockProvider);
+
+ foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) {
+ if (isset($options[$input])) {
+ $configData->set($path, $options[$input]);
+ }
+ }
+
+ return $configData;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function validate(array $options, DeploymentConfig $deploymentConfig)
+ {
+ $lockProvider = $this->getLockProvider($options, $deploymentConfig);
+ switch ($lockProvider) {
+ case LockBackendFactory::LOCK_ZOOKEEPER:
+ $errors = $this->validateZookeeperConfig($options, $deploymentConfig);
+ break;
+ case LockBackendFactory::LOCK_FILE:
+ $errors = $this->validateFileConfig($options, $deploymentConfig);
+ break;
+ case LockBackendFactory::LOCK_CACHE:
+ case LockBackendFactory::LOCK_DB:
+ $errors = [];
+ break;
+ default:
+ $errors[] = 'The lock provider ' . $lockProvider . ' does not exist.';
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Validates File locks configuration
+ *
+ * @param array $options
+ * @param DeploymentConfig $deploymentConfig
+ * @return array
+ */
+ private function validateFileConfig(array $options, DeploymentConfig $deploymentConfig): array
+ {
+ $errors = [];
+
+ $path = $options[self::INPUT_KEY_LOCK_FILE_PATH]
+ ?? $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_FILE_PATH,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_FILE_PATH)
+ );
+
+ if (!$path) {
+ $errors[] = 'The path needs to be a non-empty string.';
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Validates Zookeeper configuration
+ *
+ * @param array $options
+ * @param DeploymentConfig $deploymentConfig
+ * @return array
+ */
+ private function validateZookeeperConfig(array $options, DeploymentConfig $deploymentConfig): array
+ {
+ $errors = [];
+
+ if (!extension_loaded(LockBackendFactory::LOCK_ZOOKEEPER)) {
+ $errors[] = 'php extension Zookeeper is not installed.';
+ }
+
+ $host = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST]
+ ?? $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST)
+ );
+ $path = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH]
+ ?? $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH)
+ );
+
+ if (!$path) {
+ $errors[] = 'Zookeeper path needs to be a non-empty string.';
+ }
+
+ if (!$host) {
+ $errors[] = 'Zookeeper host is should be set.';
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Returns the name of lock provider
+ *
+ * @param array $options
+ * @param DeploymentConfig $deploymentConfig
+ * @return string
+ */
+ private function getLockProvider(array $options, DeploymentConfig $deploymentConfig): string
+ {
+ if (!isset($options[self::INPUT_KEY_LOCK_PROVIDER])) {
+ return (string) $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_PROVIDER,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_PROVIDER)
+ );
+ }
+
+ return (string) $options[self::INPUT_KEY_LOCK_PROVIDER];
+ }
+
+ /**
+ * Sets default configuration for locks
+ *
+ * @param ConfigData $configData
+ * @param DeploymentConfig $deploymentConfig
+ * @param string $lockProvider
+ * @return ConfigData
+ */
+ private function setDefaultConfiguration(
+ ConfigData $configData,
+ DeploymentConfig $deploymentConfig,
+ string $lockProvider
+ ) {
+ foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) {
+ $configData->set($path, $deploymentConfig->get($path, $this->getDefaultValue($input)));
+ }
+
+ return $configData;
+ }
+
+ /**
+ * Returns default value by input key
+ *
+ * If default value is not set returns null
+ *
+ * @param string $inputKey
+ * @return mixed|null
+ */
+ private function getDefaultValue(string $inputKey)
+ {
+ if (isset($this->defaultConfigValues[$inputKey])) {
+ return $this->defaultConfigValues[$inputKey];
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
index 944c543495751..a0dd19034621b 100644
--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
@@ -28,6 +28,7 @@ class PageCache implements ConfigOptionsListInterface
const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT = 'page-cache-redis-port';
const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA = 'page-cache-redis-compress-data';
const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD = 'page-cache-redis-password';
+ const INPUT_KEY_PAGE_CACHE_ID_PREFIX = 'page-cache-id-prefix';
const CONFIG_PATH_PAGE_CACHE_BACKEND = 'cache/frontend/page_cache/backend';
const CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER = 'cache/frontend/page_cache/backend_options/server';
@@ -35,6 +36,7 @@ class PageCache implements ConfigOptionsListInterface
const CONFIG_PATH_PAGE_CACHE_BACKEND_PORT = 'cache/frontend/page_cache/backend_options/port';
const CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA = 'cache/frontend/page_cache/backend_options/compress_data';
const CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD = 'cache/frontend/page_cache/backend_options/password';
+ const CONFIG_PATH_PAGE_CACHE_ID_PREFIX = 'cache/frontend/page_cache/id_prefix';
/**
* @var array
@@ -122,6 +124,12 @@ public function getOptions()
TextConfigOption::FRONTEND_WIZARD_TEXT,
self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD,
'Redis server password'
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_PAGE_CACHE_ID_PREFIX,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX,
+ 'ID prefix for cache keys'
)
];
}
@@ -132,6 +140,11 @@ public function getOptions()
public function createConfig(array $options, DeploymentConfig $deploymentConfig)
{
$configData = new ConfigData(ConfigFilePool::APP_ENV);
+ if (isset($options[self::INPUT_KEY_PAGE_CACHE_ID_PREFIX])) {
+ $configData->set(self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX, $options[self::INPUT_KEY_PAGE_CACHE_ID_PREFIX]);
+ } else {
+ $configData->set(self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX, $this->generateCachePrefix());
+ }
if (isset($options[self::INPUT_KEY_PAGE_CACHE_BACKEND])) {
if ($options[self::INPUT_KEY_PAGE_CACHE_BACKEND] == self::INPUT_VALUE_PAGE_CACHE_REDIS) {
@@ -252,4 +265,14 @@ private function getDefaultConfigValue($inputKey)
return '';
}
}
+
+ /**
+ * Generate default cache ID prefix based on installation dir
+ *
+ * @return string
+ */
+ private function generateCachePrefix(): string
+ {
+ return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_';
+ }
}
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php
index 39b95953c6347..f351bca65f89b 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php
@@ -28,6 +28,9 @@ class CacheTest extends \PHPUnit\Framework\TestCase
*/
private $deploymentConfigMock;
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
$this->validatorMock = $this->createMock(RedisConnectionValidator::class);
@@ -39,7 +42,7 @@ protected function setUp()
public function testGetOptions()
{
$options = $this->configOptionsList->getOptions();
- $this->assertCount(5, $options);
+ $this->assertCount(6, $options);
$this->assertArrayHasKey(0, $options);
$this->assertInstanceOf(SelectConfigOption::class, $options[0]);
@@ -60,6 +63,10 @@ public function testGetOptions()
$this->assertArrayHasKey(4, $options);
$this->assertInstanceOf(TextConfigOption::class, $options[4]);
$this->assertEquals('cache-backend-redis-password', $options[4]->getName());
+
+ $this->assertArrayHasKey(5, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[5]);
+ $this->assertEquals('cache-id-prefix', $options[5]->getName());
}
public function testCreateConfigCacheRedis()
@@ -76,7 +83,8 @@ public function testCreateConfigCacheRedis()
'port' => '',
'database' => '',
'password' => ''
- ]
+ ],
+ 'id_prefix' => $this->expectedIdPrefix(),
]
]
]
@@ -99,7 +107,8 @@ public function testCreateConfigWithRedisConfig()
'port' => '1234',
'database' => '5',
'password' => ''
- ]
+ ],
+ 'id_prefix' => $this->expectedIdPrefix(),
]
]
]
@@ -116,6 +125,48 @@ public function testCreateConfigWithRedisConfig()
$this->assertEquals($expectedConfigData, $configData->getData());
}
+ public function testCreateConfigWithFileCache()
+ {
+ $this->deploymentConfigMock->method('get')->willReturn('');
+
+ $expectedConfigData = [
+ 'cache' => [
+ 'frontend' => [
+ 'default' => [
+ 'id_prefix' => $this->expectedIdPrefix(),
+ ]
+ ]
+ ]
+ ];
+
+ $configData = $this->configOptionsList->createConfig([], $this->deploymentConfigMock);
+
+ $this->assertEquals($expectedConfigData, $configData->getData());
+ }
+
+ public function testCreateConfigWithIdPrefix()
+ {
+ $this->deploymentConfigMock->method('get')->willReturn('');
+
+ $explicitPrefix = 'XXX_';
+ $expectedConfigData = [
+ 'cache' => [
+ 'frontend' => [
+ 'default' => [
+ 'id_prefix' => $explicitPrefix,
+ ]
+ ]
+ ]
+ ];
+
+ $configData = $this->configOptionsList->createConfig(
+ ['cache-id-prefix' => $explicitPrefix],
+ $this->deploymentConfigMock
+ );
+
+ $this->assertEquals($expectedConfigData, $configData->getData());
+ }
+
public function testValidateWithValidInput()
{
$options = [
@@ -142,4 +193,14 @@ public function testValidateWithInvalidInput()
$this->assertCount(1, $errors);
$this->assertEquals("Invalid cache handler 'clay-tablet'", $errors[0]);
}
+
+ /**
+ * The default ID prefix, based on installation directory
+ *
+ * @return string
+ */
+ private function expectedIdPrefix(): string
+ {
+ return substr(\md5(dirname(__DIR__, 8)), 0, 3) . '_';
+ }
}
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php
new file mode 100644
index 0000000000000..1a46bddf5f21a
--- /dev/null
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php
@@ -0,0 +1,232 @@
+deploymentConfigMock = $this->createMock(DeploymentConfig::class);
+ $this->lockConfigOptionsList = new LockConfigOptionsList();
+ }
+
+ /**
+ * @return void
+ */
+ public function testGetOptions()
+ {
+ $options = $this->lockConfigOptionsList->getOptions();
+ $this->assertSame(5, count($options));
+
+ $this->assertArrayHasKey(0, $options);
+ $this->assertInstanceOf(SelectConfigOption::class, $options[0]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER, $options[0]->getName());
+
+ $this->assertArrayHasKey(1, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[1]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX, $options[1]->getName());
+
+ $this->assertArrayHasKey(2, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[2]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST, $options[2]->getName());
+
+ $this->assertArrayHasKey(3, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[3]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH, $options[3]->getName());
+
+ $this->assertArrayHasKey(4, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[4]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH, $options[4]->getName());
+ }
+
+ /**
+ * @param array $options
+ * @param array $expectedResult
+ * @dataProvider createConfigDataProvider
+ */
+ public function testCreateConfig(array $options, array $expectedResult)
+ {
+ $this->deploymentConfigMock->expects($this->any())
+ ->method('get')
+ ->willReturnArgument(1);
+ $data = $this->lockConfigOptionsList->createConfig($options, $this->deploymentConfigMock);
+ $this->assertInstanceOf(ConfigData::class, $data);
+ $this->assertTrue($data->isOverrideWhenSave());
+ $this->assertSame($expectedResult, $data->getData());
+ }
+
+ /**
+ * @return array
+ */
+ public function createConfigDataProvider(): array
+ {
+ return [
+ 'Check default values' => [
+ 'options' => [],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_DB,
+ 'config' => [
+ 'prefix' => null,
+ ],
+ ],
+ ],
+ ],
+ 'Check default value for cache lock' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_CACHE,
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_CACHE,
+ ],
+ ],
+ ],
+ 'Check default value for zookeeper lock' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER,
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_ZOOKEEPER,
+ 'config' => [
+ 'host' => null,
+ 'path' => ZookeeperLock::DEFAULT_PATH,
+ ],
+ ],
+ ],
+ ],
+ 'Check specific db lock options' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB,
+ LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX => 'my_prefix'
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_DB,
+ 'config' => [
+ 'prefix' => 'my_prefix',
+ ],
+ ],
+ ],
+ ],
+ 'Check specific zookeeper lock options' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER,
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '123.45.67.89:10',
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '/some/path',
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_ZOOKEEPER,
+ 'config' => [
+ 'host' => '123.45.67.89:10',
+ 'path' => '/some/path',
+ ],
+ ],
+ ],
+ ],
+ 'Check specific file lock options' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE,
+ LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '/my/path'
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_FILE,
+ 'config' => [
+ 'path' => '/my/path',
+ ],
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @param array $options
+ * @param array $expectedResult
+ * @dataProvider validateDataProvider
+ */
+ public function testValidate(array $options, array $expectedResult)
+ {
+ $this->deploymentConfigMock->expects($this->any())
+ ->method('get')
+ ->willReturnArgument(1);
+ $this->assertSame(
+ $expectedResult,
+ $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock)
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function validateDataProvider(): array
+ {
+ return [
+ 'Wrong lock provider' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => 'SomeProvider',
+ ],
+ 'expectedResult' => [
+ 'The lock provider SomeProvider does not exist.',
+ ],
+ ],
+ 'Empty host and path for Zookeeper' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER,
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '',
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '',
+ ],
+ 'expectedResult' => extension_loaded('zookeeper')
+ ? [
+ 'Zookeeper path needs to be a non-empty string.',
+ 'Zookeeper host is should be set.',
+ ]
+ : [
+ 'php extension Zookeeper is not installed.',
+ 'Zookeeper path needs to be a non-empty string.',
+ 'Zookeeper host is should be set.',
+ ],
+ ],
+ 'Empty path for File lock' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE,
+ LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '',
+ ],
+ 'expectedResult' => [
+ 'The path needs to be a non-empty string.',
+ ],
+ ],
+ ];
+ }
+}
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php
index ed0e567820ad1..0e7c851cb706b 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php
@@ -28,6 +28,9 @@ class PageCacheTest extends \PHPUnit\Framework\TestCase
*/
private $deploymentConfigMock;
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
$this->validatorMock = $this->createMock(RedisConnectionValidator::class, [], [], '', false);
@@ -39,7 +42,7 @@ protected function setUp()
public function testGetOptions()
{
$options = $this->configList->getOptions();
- $this->assertCount(6, $options);
+ $this->assertCount(7, $options);
$this->assertArrayHasKey(0, $options);
$this->assertInstanceOf(SelectConfigOption::class, $options[0]);
@@ -64,6 +67,10 @@ public function testGetOptions()
$this->assertArrayHasKey(5, $options);
$this->assertInstanceOf(TextConfigOption::class, $options[5]);
$this->assertEquals('page-cache-redis-password', $options[5]->getName());
+
+ $this->assertArrayHasKey(6, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[6]);
+ $this->assertEquals('page-cache-id-prefix', $options[6]->getName());
}
public function testCreateConfigWithRedis()
@@ -81,7 +88,8 @@ public function testCreateConfigWithRedis()
'database' => '',
'compress_data' => '',
'password' => ''
- ]
+ ],
+ 'id_prefix' => $this->expectedIdPrefix(),
]
]
]
@@ -105,7 +113,8 @@ public function testCreateConfigWithRedisConfiguration()
'database' => '6',
'compress_data' => '1',
'password' => ''
- ]
+ ],
+ 'id_prefix' => $this->expectedIdPrefix(),
]
]
]
@@ -124,6 +133,48 @@ public function testCreateConfigWithRedisConfiguration()
$this->assertEquals($expectedConfigData, $configData->getData());
}
+ public function testCreateConfigWithFileCache()
+ {
+ $this->deploymentConfigMock->method('get')->willReturn('');
+
+ $expectedConfigData = [
+ 'cache' => [
+ 'frontend' => [
+ 'page_cache' => [
+ 'id_prefix' => $this->expectedIdPrefix(),
+ ]
+ ]
+ ]
+ ];
+
+ $configData = $this->configList->createConfig([], $this->deploymentConfigMock);
+
+ $this->assertEquals($expectedConfigData, $configData->getData());
+ }
+
+ public function testCreateConfigWithIdPrefix()
+ {
+ $this->deploymentConfigMock->method('get')->willReturn('');
+
+ $explicitPrefix = 'XXX_';
+ $expectedConfigData = [
+ 'cache' => [
+ 'frontend' => [
+ 'page_cache' => [
+ 'id_prefix' => $explicitPrefix,
+ ]
+ ]
+ ]
+ ];
+
+ $configData = $this->configList->createConfig(
+ ['page-cache-id-prefix' => $explicitPrefix],
+ $this->deploymentConfigMock
+ );
+
+ $this->assertEquals($expectedConfigData, $configData->getData());
+ }
+
public function testValidationWithValidData()
{
$this->validatorMock->expects($this->once())
@@ -151,4 +202,14 @@ public function testValidationWithInvalidData()
$this->assertCount(1, $errors);
$this->assertEquals('Invalid cache handler \'foobar\'', $errors[0]);
}
+
+ /**
+ * The default ID prefix, based on installation directory
+ *
+ * @return string
+ */
+ private function expectedIdPrefix(): string
+ {
+ return substr(\md5(dirname(__DIR__, 8)), 0, 3) . '_';
+ }
}
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
index f342a11493498..99ef82dd9d355 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
@@ -7,6 +7,7 @@
namespace Magento\Setup\Test\Unit\Model;
use Magento\Framework\Config\ConfigOptionsListConstants;
+use Magento\Setup\Model\ConfigOptionsList\Lock;
use Magento\Setup\Model\ConfigGenerator;
use Magento\Setup\Model\ConfigOptionsList;
use Magento\Setup\Validator\DbValidator;
@@ -82,7 +83,7 @@ public function testCreateOptions()
$this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock);
$this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock);
- $configData = $this->object->createConfig([], $this->deploymentConfig);
+ $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig);
$this->assertGreaterThanOrEqual(6, count($configData));
}
@@ -96,7 +97,7 @@ public function testCreateOptionsWithOptionalNull()
$this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock);
$this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock);
- $configData = $this->object->createConfig([], $this->deploymentConfig);
+ $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig);
$this->assertGreaterThanOrEqual(6, count($configData));
}
@@ -109,7 +110,8 @@ public function testValidateSuccess()
ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name',
ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host',
ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user',
- ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass'
+ ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass',
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$this->prepareValidationMocks();
@@ -127,7 +129,8 @@ public function testValidateInvalidSessionHandler()
ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name',
ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host',
ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user',
- ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass'
+ ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass',
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$this->prepareValidationMocks();
@@ -141,7 +144,8 @@ public function testValidateEmptyEncryptionKey()
{
$options = [
ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true,
- ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => ''
+ ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '',
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$this->assertEquals(
['Invalid encryption key'],
@@ -167,7 +171,8 @@ public function testValidateCacheHosts($hosts, $expectedError)
{
$options = [
ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true,
- ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts
+ ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts,
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$result = $this->object->validate($options, $this->deploymentConfig);
if ($expectedError) {
diff --git a/setup/view/styles/lib/variables/_colors.less b/setup/view/styles/lib/variables/_colors.less
index 638490ac8673a..a72dc69ac7669 100644
--- a/setup/view/styles/lib/variables/_colors.less
+++ b/setup/view/styles/lib/variables/_colors.less
@@ -24,7 +24,7 @@
@color-green-apple: #79a22e;
@color-green-islamic: #090;
@color-dark-brownie: #41362f;
-@color-brown-darkie: #41362f;
+@color-brown-darker: #41362f;
@color-phoenix-down: #e04f00;
@color-phoenix: #eb5202;
@color-phoenix-almost-rise: #ef672f;