diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js
index cab130f7c2104..7a166b47256cb 100644
--- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js
+++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js
@@ -154,18 +154,12 @@ define([
$.each(elementValue, function (key, option) {
data[elementName + '[' + option + ']'] = option;
});
+ } else if (elementName.substr(elementName.length - 2) == '[]') { //eslint-disable-line eqeqeq, max-depth
+ elementName = elementName.substring(0, elementName.length - 2);
+
+ data[elementName + '[' + elementValue + ']'] = elementValue;
} else {
- if (elementValue) { //eslint-disable-line no-lonely-if
- if (elementName.substr(elementName.length - 2) == '[]') { //eslint-disable-line eqeqeq, max-depth
- elementName = elementName.substring(0, elementName.length - 2);
-
- if (elementValue) { //eslint-disable-line max-depth
- data[elementName + '[' + elementValue + ']'] = elementValue;
- }
- } else {
- data[elementName] = elementValue;
- }
- }
+ data[elementName] = elementValue;
}
return data;
diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less
index e8e2746717e6a..ae25b1102e581 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less
@@ -162,9 +162,12 @@
&.collapsible-block-wrapper-last {
border-bottom: 0;
}
+
.admin__dynamic-rows.admin__control-collapsible {
- .admin__collapsible-block-wrapper {
- border-bottom: none;
+ td {
+ &.admin__collapsible-block-wrapper {
+ border-bottom: none;
+ }
}
}
}
@@ -342,7 +345,7 @@
}
.value {
- padding-right: 4rem;
+ padding-right: 2rem;
}
}
diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less
index 3355950254072..ffbbaeb084162 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less
@@ -15,6 +15,14 @@
}
}
+.catalog-category-edit {
+ .admin__grid-control {
+ .admin__grid-control-value {
+ display: none;
+ }
+ }
+}
+
.product-composite-configure-inner {
.admin__control-text {
&.qty {
diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less
index 1e76679f594c1..fa1ae25628986 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less
@@ -92,6 +92,14 @@
margin: 0;
padding: 0;
}
+ .admin__data-grid-pager-wrap{
+ .selectmenu {
+ margin-bottom: 10px;
+ }
+ }
+ .data-grid-search-control-wrap {
+ margin-bottom: 10px;
+ }
}
//
diff --git a/app/design/adminhtml/Magento/backend/etc/view.xml b/app/design/adminhtml/Magento/backend/etc/view.xml
index f10f7789b0888..18c2d8f1b1722 100644
--- a/app/design/adminhtml/Magento/backend/etc/view.xml
+++ b/app/design/adminhtml/Magento/backend/etc/view.xml
@@ -23,6 +23,8 @@
+ - Lib::mage/captcha.js
+ - Lib::mage/captcha.min.js
- Lib::mage/common.js
- Lib::mage/cookies.js
- Lib::mage/dataPost.js
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less
index de24bf89620d4..03caa1c543bba 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less
@@ -76,7 +76,8 @@
position: absolute;
speak: none;
text-shadow: none;
- top: 1.3rem;
+ top: 50%;
+ margin-top: -1.25rem;
width: auto;
}
}
@@ -110,7 +111,7 @@
content: @alert-icon__error__content;
font-size: @alert-icon__error__font-size;
left: 2.2rem;
- margin-top: 0.5rem;
+ margin-top: .5rem;
}
}
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less
index 95d7f8f65fdc1..efc747e4d714a 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less
@@ -146,13 +146,13 @@
}
.action-close {
- padding: @modal-popup__padding;
+ padding: @modal-popup__padding - 2;
&:active,
&:focus {
background: transparent;
- padding-right: @modal-popup__padding + (@modal-action-close__font-size - @modal-action-close__active__font-size) / 2;
- padding-top: @modal-popup__padding + (@modal-action-close__font-size - @modal-action-close__active__font-size) / 2;
+ padding-right: @modal-popup__padding - 2;
+ padding-top: @modal-popup__padding - 2;
}
}
}
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less
index f971246ab469d..6c3756370d9ce 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less
@@ -85,7 +85,7 @@
cursor: pointer;
}
- &:focus {
+ &:active {
background-image+: url('../images/arrows-bg.svg');
background-position+: ~'calc(100% - 12px)' 13px;
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less
index f4eead6ed4ebc..f47ddcece1c79 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less
@@ -159,6 +159,14 @@
}
}
}
+ &.composite-bundle {
+ .admin__field-control {
+ padding-top: 7px;
+ }
+ .admin__field-option {
+ padding-top: 0;
+ }
+ }
}
.admin__fieldset-product-websites {
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less
index df031bebeb24a..bb0e4a573e643 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less
@@ -11,7 +11,7 @@
.admin__fieldset-wrapper-title {
&:extend(.abs-clearfix all);
border-bottom: 1px solid @color-gray80;
- line-height: 1.2;
+ line-height: 1.4;
margin-bottom: 0;
padding: 14px 0 16px;
diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less
index dde4dafeeed23..2dbe68ef96eec 100644
--- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less
+++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less
@@ -2738,7 +2738,8 @@
// ---------------------------------------------
#widget_instace_tabs_properties_section_content .widget-option-label {
- margin-top: 6px;
+ margin-top: 7px;
+ display: inline-block;
}
//
diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less
index 08a9b61977922..d3b314836ae8e 100644
--- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less
+++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less
@@ -488,6 +488,7 @@
.product-items-names {
.product-item {
+ display: flex;
margin-bottom: @indent__s;
}
diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_widgets.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_widgets.less
index 42b1bf2d0cc09..7181606090ccb 100644
--- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_widgets.less
+++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_widgets.less
@@ -23,6 +23,15 @@
}
.block.widget {
+ .products-grid .product-item {
+ margin-left: 2%;
+ width: calc(~'(100% - 2%)/2');
+
+ &:nth-child(2n + 1) {
+ margin-left: 0;
+ }
+ }
+
.product-item-info {
width: auto;
}
@@ -60,6 +69,15 @@
.page-layout-3columns .block.widget .products-grid .product-item {
width: 100%/3;
}
+
+ .page-layout-1column .block.widget .products-grid .product-item {
+ margin-left: 2%;
+ width: calc(~'(100% - 4%)/3');
+
+ &:nth-child(3n + 1) {
+ margin-left: 0;
+ }
+ }
}
//
@@ -82,7 +100,16 @@
}
.page-layout-1column .block.widget .products-grid .product-item {
- width: 100%/4;
+ margin-left: 2%;
+ width: calc(~'(100% - 6%)/4');
+
+ &:nth-child(3n + 1) {
+ margin-left: 2%;
+ }
+
+ &:nth-child(4n + 1) {
+ margin-left: 0;
+ }
}
.page-layout-3columns .block.widget .products-grid .product-item {
@@ -96,11 +123,11 @@
}
.page-layout-1column .block.widget .products-grid .product-item {
- margin-left: calc(~'(100% - 5 * (100%/6)) / 4');
- width: 100%/6;
+ margin-left: 2%;
+ width: calc(~'(100% - 8%)/5');
&:nth-child(4n + 1) {
- margin-left: calc(~'(100% - 5 * (100%/6)) / 4');
+ margin-left: 2%;
}
&:nth-child(5n + 1) {
diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less
index 951ca89a07988..b7af69fd5ca82 100644
--- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less
+++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less
@@ -29,15 +29,23 @@
.product {
&-items {
+ font-size: 0;
&:extend(.abs-reset-list all);
}
&-item {
+ font-size: 1.4rem;
vertical-align: top;
.products-grid & {
display: inline-block;
- width: 100%/2;
+ margin-left: 2%;
+ padding: 0;
+ width: calc(~'(100% - 2%) / 2');
+ }
+
+ &:nth-child(2n + 1) {
+ margin-left: 0;
}
&:extend(.abs-add-box-sizing all);
@@ -63,13 +71,26 @@
}
&-actions {
+ font-size: 0;
+
+ > * {
+ font-size: 1.4rem;
+ }
.actions-secondary {
+ display: inline-block;
+ font-size: 1.4rem;
+ vertical-align: middle;
+ white-space: nowrap;
> button.action {
.lib-button-reset();
}
> .action {
+ line-height: 35px;
+ text-align: center;
+ width: 35px;
+
&:extend(.abs-actions-addto-gridlist all);
&:before {
margin: 0;
@@ -80,6 +101,10 @@
}
}
}
+
+ .actions-primary {
+ display: inline-block;
+ }
}
&-description {
@@ -191,19 +216,6 @@
}
}
- .column.main {
- .product {
- &-items {
- margin-left: -@indent__base;
- }
-
- &-item {
- padding-left: @indent__base;
- }
- }
-
- }
-
.price-container {
.price {
.lib-font-size(14);
@@ -302,18 +314,10 @@
}
.actions-primary + .actions-secondary {
- display: table-cell;
- padding-left: 5px;
- white-space: nowrap;
- width: 50%;
> * {
white-space: normal;
}
}
-
- .actions-primary {
- display: table-cell;
- }
}
}
}
@@ -329,7 +333,13 @@
.page-products.page-layout-3columns {
.products-grid {
.product-item {
- width: 100%/3;
+ margin-left: 2%;
+ padding: 0;
+ width: calc(~'(100% - 4%) / 3');
+
+ &:nth-child(3n + 1) {
+ margin-left: 0;
+ }
}
}
}
@@ -343,7 +353,13 @@
.page-products {
.products-grid {
.product-item {
- width: 100%/3;
+ margin-left: 2%;
+ padding: 0;
+ width: calc(~'(100% - 4%) / 3');
+
+ &:nth-child(3n + 1) {
+ margin-left: 0;
+ }
}
}
}
@@ -394,9 +410,13 @@
}
.product-item {
- margin-left: calc(~'(100% - 4 * 23.233%) / 3');
+ margin-left: 2%;
padding: 0;
- width: 23.233%;
+ width: calc(~'(100% - 6%) / 4');
+
+ &:nth-child(3n + 1) {
+ margin-left: 2%;
+ }
&:nth-child(4n + 1) {
margin-left: 0;
diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less
index 673131563417d..65f3eeef63b01 100644
--- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less
+++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less
@@ -342,7 +342,7 @@
.item-qty {
margin-right: @indent__s;
text-align: center;
- width: 40px;
+ width: 45px;
}
.update-cart-item {
diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_sidebar-shipping-information.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_sidebar-shipping-information.less
index b54c0a264a03a..0f2a7abcbaa18 100644
--- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_sidebar-shipping-information.less
+++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_sidebar-shipping-information.less
@@ -67,3 +67,11 @@
}
}
}
+
+.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) {
+ .opc-block-shipping-information {
+ .shipping-information-title {
+ font-size: 2.3rem;
+ }
+ }
+}
diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less
index bf264a98f33b8..39b9a051e6592 100644
--- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less
+++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less
@@ -147,3 +147,32 @@
}
}
}
+
+//
+// Tablet
+// _____________________________________________
+
+@media only screen and (max-width: @screen__m) {
+ .field-tooltip .field-tooltip-content {
+ left: auto;
+ right: -10px;
+ top: 40px;
+ }
+ .field-tooltip .field-tooltip-content::before,
+ .field-tooltip .field-tooltip-content::after {
+ border: 10px solid transparent;
+ height: 0;
+ left: auto;
+ margin-top: -21px;
+ right: 10px;
+ top: 0;
+ width: 0;
+ }
+ .field-tooltip .field-tooltip-content::before {
+ border-bottom-color: @color-gray40;
+ }
+ .field-tooltip .field-tooltip-content::after {
+ border-bottom-color: @color-gray-light01;
+ top: 1px;
+ }
+}
diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
index 9df59ca5dac92..4d9cb41b3adac 100644
--- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
+++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
@@ -367,8 +367,8 @@
}
.account {
- .page.messages {
- margin-bottom: @indent__base;
+ .messages {
+ margin-bottom: 0;
}
.toolbar {
diff --git a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less
index 2761a2f74f990..c572c983d80d9 100644
--- a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less
+++ b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less
@@ -350,7 +350,7 @@
.product {
&-item {
&-checkbox {
- left: 20px;
+ left: 0;
position: absolute;
top: 20px;
}
diff --git a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less
index 0e8350261e002..9cd0439c13956 100644
--- a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less
+++ b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less
@@ -8,6 +8,24 @@
// _____________________________________________
& when (@media-common = true) {
+ .toolbar {
+ &.wishlist-toolbar {
+ .limiter {
+ float: right;
+ }
+ .main .pages {
+ display: inline-block;
+ position: relative;
+ z-index: 0;
+ }
+ .toolbar-amount,
+ .limiter {
+ display: inline-block;
+ z-index: 1;
+ }
+ }
+ }
+
.form.wishlist.items {
.actions-toolbar {
&:extend(.abs-reset-left-margin all);
@@ -177,10 +195,10 @@
.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
.products-grid.wishlist {
margin-bottom: @indent__l;
- margin-right: -@indent__s;
+ margin-right: 0;
.product {
&-item {
- padding: @indent__base @indent__s @indent__base @indent__base;
+ padding: @indent__base 0 @indent__base 0;
position: relative;
&-photo {
@@ -194,6 +212,7 @@
&-actions {
display: block;
+ float: left;
.action {
margin-right: 15px;
diff --git a/app/design/frontend/Magento/blank/web/css/source/_forms.less b/app/design/frontend/Magento/blank/web/css/source/_forms.less
index 94b993b53b508..c9f3c3d72ef4c 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_forms.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_forms.less
@@ -18,7 +18,7 @@
.fieldset {
.lib-form-fieldset();
&:last-child {
- margin-bottom: 0;
+ margin-bottom: @indent__base;
}
> .field,
diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less
index 4499886ef0f10..21b7315779764 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less
@@ -131,12 +131,18 @@
);
}
}
-
.switcher-dropdown {
.lib-list-reset-styles();
+ display: none;
padding: @indent__s 0;
}
-
+ .switcher-options {
+ &.active {
+ .switcher-dropdown {
+ display: block;
+ }
+ }
+ }
.header.links {
.lib-list-reset-styles();
border-bottom: 1px solid @color-gray82;
@@ -207,7 +213,7 @@
}
.nav-toggle {
- &:after{
+ &:after {
background: rgba(0, 0, 0, @overlay__opacity);
content: '';
display: block;
diff --git a/app/design/frontend/Magento/blank/web/css/source/_sections.less b/app/design/frontend/Magento/blank/web/css/source/_sections.less
index f0a3518c92a8b..1eee47bda817c 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_sections.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_sections.less
@@ -31,8 +31,19 @@
.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
.product.data.items {
.lib-data-accordion();
+
.data.item {
display: block;
}
+
+ .item.title {
+ > .switch {
+ padding: 1px 15px 1px;
+ }
+ }
+
+ > .item.content {
+ padding: 10px 15px 30px;
+ }
}
}
diff --git a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less
index 43ae23bab7895..45a01269bef66 100644
--- a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less
@@ -58,6 +58,7 @@
.field.choice {
input {
float: left;
+ margin-top: 4px;
}
.label {
@@ -253,7 +254,7 @@
.box-tocart {
.action.primary {
margin-right: 1%;
- width: 49%;
+ width: auto;
}
}
diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less
index 501a1d2918d6a..f15509ceb63eb 100644
--- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less
@@ -563,6 +563,7 @@
.product-items-names {
.product-item {
+ display: flex;
margin-bottom: @indent__s;
}
@@ -975,6 +976,15 @@
[class*='block-compare'] {
display: none;
}
+ .catalog-product_compare-index {
+ .columns {
+ .column {
+ &.main {
+ flex-basis: inherit;
+ }
+ }
+ }
+ }
}
//
diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
index 3cb3d4af53189..d477c08fc9553 100644
--- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
+++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
@@ -18,7 +18,7 @@
@product-name-link__text-decoration__visited: @link__hover__text-decoration;
@product-item__hover__background-color: @color-white;
-@product-item__hover__box-shadow: 3px 3px 4px 0 rgba(0, 0, 0, .3);
+@product-item__hover__box-shadow: 3px 4px 4px 0 rgba(0, 0, 0, .3);
@product-price__muted__color: @color-gray40;
@@ -34,15 +34,22 @@
.product {
&-items {
+ font-size: 0;
&:extend(.abs-reset-list all);
}
&-item {
+ font-size: 1.4rem;
vertical-align: top;
.products-grid & {
display: inline-block;
- width: 100%/2.04;
+ margin-left: 2%;
+ width: calc(~'(100% - 2%)/2');
+ }
+
+ &:nth-child(2n + 1) {
+ margin-left: 0;
}
&:extend(.abs-add-box-sizing all);
@@ -68,8 +75,17 @@
}
&-actions {
+ font-size: 0;
+
+ > * {
+ font-size: 1.4rem;
+ }
.actions-secondary {
+ display: inline-block;
+ font-size: 1.4rem;
+ vertical-align: middle;
+
> button.action {
.lib-button-reset();
}
@@ -79,12 +95,19 @@
&:before {
margin: 0;
}
+ line-height: 35px;
+ text-align: center;
+ width: 35px;
span {
&:extend(.abs-visually-hidden all);
}
}
}
+
+ .actions-primary {
+ display: inline-block;
+ }
}
&-description {
@@ -291,7 +314,7 @@
border: 1px solid @color-gray-light2;
border-top: none;
left: 0;
- margin: 9px 0 0 -1px;
+ margin: 10px 0 0 -1px;
padding: 0 9px 9px;
position: absolute;
right: -1px;
@@ -307,13 +330,13 @@
}
.actions-primary + .actions-secondary {
- display: table-cell;
- padding-left: 10px;
+ display: inline-block;
vertical-align: middle;
- width: 50%;
> .action {
- margin-right: 10px;
+ line-height: 35px;
+ text-align: center;
+ width: 35px;
&:last-child {
margin-right: 0;
@@ -322,7 +345,7 @@
}
.actions-primary {
- display: table-cell;
+ display: inline-block;
}
}
@@ -363,7 +386,7 @@
.products-grid {
.product-item {
margin-bottom: @indent__base;
- width: 100%/3.04;
+ width: 100%/3;
}
}
@@ -374,10 +397,24 @@
.page-products.page-layout-3columns {
.products-grid {
.product-item {
- width: 100%/3.04;
+ margin-left: 2%;
+ width: calc(~'(100% - 4%) / 3');
+
+ &:nth-child(3n + 1) {
+ margin-left: 0;
+ }
}
}
}
+
+ .block.widget .products-grid .product-item,
+ .page-layout-1column .block.widget .products-grid .product-item,
+ .page-layout-3columns .block.widget .products-grid .product-item {
+ .product-item-inner {
+ box-shadow: 3px 6px 4px 0 rgba(0, 0, 0, .3);
+ margin: 9px 0 0 -1px;
+ }
+ }
}
//
@@ -388,7 +425,12 @@
.page-products {
.products-grid {
.product-item {
- width: 100%/3.04;
+ margin-left: 2%;
+ width: calc(~'(100% - 4%) / 3');
+
+ &:nth-child(3n + 1) {
+ margin-left: 0;
+ }
}
}
}
@@ -441,9 +483,13 @@
}
.product-item {
- margin-left: calc(~'(100% - 4 * 24.439%) / 3');
+ margin-left: 2%;
padding: 5px;
- width: 24.09%;
+ width: calc(~'(100% - 6%)/4');
+
+ &:nth-child(3n + 1) {
+ margin-left: 2%;
+ }
&:nth-child(4n + 1) {
margin-left: 0;
diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less
index 5aaf0cd02fab9..6be6010fd2d2d 100644
--- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less
+++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less
@@ -492,6 +492,17 @@
}
}
}
+
+ .cart.table-wrapper,
+ .order-items.table-wrapper {
+ .col.price,
+ .col.qty,
+ .col.subtotal,
+ .col.msrp {
+ text-align: left;
+ }
+ }
+
}
//
@@ -689,6 +700,9 @@
position: static;
}
}
+ &.discount {
+ width: auto;
+ }
}
}
diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less
index b9b223f44021a..fd418e0c447b8 100644
--- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less
+++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less
@@ -355,7 +355,7 @@
.item-qty {
margin-right: @indent__s;
text-align: center;
- width: 40px;
+ width: 45px;
}
.update-cart-item {
diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less
index 0df0cace338c0..3ea1f5b7f6842 100644
--- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less
+++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less
@@ -48,6 +48,7 @@
.step-title {
&:extend(.abs-checkout-title all);
.lib-css(border-bottom, @checkout-step-title__border);
+ margin-bottom: 15px;
}
.step-content {
diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less
index 0b27454b206e3..3b584bc26fe34 100644
--- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less
+++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less
@@ -69,6 +69,13 @@
.payment-option-content {
.lib-css(padding, 0 0 @indent__base @checkout-payment-option-content__padding__xl);
+ .primary {
+ .action {
+ &.action-apply {
+ margin-right: 0;
+ }
+ }
+ }
}
.payment-option-inner {
diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
index 2e7856d390bd0..749a388ac2354 100755
--- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
@@ -421,6 +421,12 @@
.column.main {
width: 77.7%;
}
+
+ .sidebar-main {
+ .block {
+ margin-bottom: 0;
+ }
+ }
}
.account {
@@ -532,11 +538,18 @@
.column.main,
.sidebar-additional {
margin: 0;
+ padding: 0;
}
.data.table {
&:extend(.abs-table-striped-mobile all);
}
+
+ .sidebar-main {
+ .account-nav {
+ margin-bottom: 0;
+ }
+ }
}
}
@@ -554,8 +567,8 @@
}
.account {
- .page.messages {
- margin-bottom: @indent__base;
+ .messages {
+ margin-bottom: 0;
}
.column.main {
diff --git a/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less
index 41e6da39e1ef1..ff377a4b88acc 100644
--- a/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less
@@ -246,6 +246,9 @@
.gift-messages-order {
margin-bottom: @indent__m;
}
+ .gift-message-summary {
+ padding-right: 7rem;
+ }
}
//
@@ -282,10 +285,6 @@
}
}
- .gift-message-summary {
- padding-right: 7rem;
- }
-
//
// In-table block
// ---------------------------------------------
diff --git a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less
index 0b01c54a64378..7ed4a9e64e943 100644
--- a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less
@@ -429,7 +429,7 @@
.product {
&-item {
&-checkbox {
- left: 20px;
+ left: 0;
position: absolute;
top: 20px;
}
diff --git a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less
index ed6b53727da52..38f7c4ca873fd 100644
--- a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less
@@ -374,7 +374,7 @@
text-align: right;
.action {
- margin-left: @indent__s;
+ margin-left: 0;
&.back {
display: block;
@@ -496,4 +496,12 @@
margin-left: @indent__xl;
}
}
+
+ .multicheckout {
+ .actions-toolbar {
+ > .primary {
+ margin-right: 0;
+ }
+ }
+ }
}
diff --git a/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less
index 2c66420f65fbd..4324e5f1d6a93 100644
--- a/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less
@@ -297,6 +297,9 @@
a:not(:last-child) {
margin-right: 30px;
}
+ .action.add {
+ white-space: nowrap;
+ }
}
}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html
index a7b9b330ab9ce..269e46d752084 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html
@@ -11,7 +11,7 @@
"var this.getUrl($store, 'customer/account/')":"Customer Account URL",
"var order.getCustomerName()":"Customer Name",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status"
+"var order.getFrontendStatusLabel()":"Order Status"
} @-->
{{template config_path="design/email/header_template"}}
@@ -24,7 +24,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
{{trans 'You can check the status of your order by logging into your account .' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html
index 36279eb26005e..c8bdae7b08fa5 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html
@@ -10,7 +10,7 @@
"var creditmemo.increment_id":"Credit Memo Id",
"var billing.getName()":"Guest Customer Name",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status"
+"var order.getFrontendStatusLabel()":"Order Status"
} @-->
{{template config_path="design/email/header_template"}}
@@ -23,7 +23,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html
index a739c9f54b08f..8ec54f1e64d9c 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html
@@ -11,7 +11,7 @@
"var comment":"Invoice Comment",
"var invoice.increment_id":"Invoice Id",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status"
+"var order.getFrontendStatusLabel()":"Order Status"
} @-->
{{template config_path="design/email/header_template"}}
@@ -24,7 +24,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
{{trans 'You can check the status of your order by logging into your account .' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html
index a56ee6da9fa25..6028db7b97730 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html
@@ -10,7 +10,7 @@
"var comment":"Invoice Comment",
"var invoice.increment_id":"Invoice Id",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status"
+"var order.getFrontendStatusLabel()":"Order Status"
} @-->
{{template config_path="design/email/header_template"}}
@@ -23,7 +23,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html
index 3e4bf8df2f107..fa16ac2196bf4 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html
@@ -10,7 +10,7 @@
"var order.getCustomerName()":"Customer Name",
"var comment":"Order Comment",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status"
+"var order.getFrontendStatusLabel()":"Order Status"
} @-->
{{template config_path="design/email/header_template"}}
@@ -23,7 +23,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
{{trans 'You can check the status of your order by logging into your account .' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html
index 1075608db4341..8ead615fe01ca 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html
@@ -9,7 +9,7 @@
"var billing.getName()":"Guest Customer Name",
"var comment":"Order Comment",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status"
+"var order.getFrontendStatusLabel()":"Order Status"
} @-->
{{template config_path="design/email/header_template"}}
@@ -22,7 +22,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html
index 37bf92b866c74..4f9b7286f3ae4 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html
@@ -10,7 +10,7 @@
"var order.getCustomerName()":"Customer Name",
"var comment":"Order Comment",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status",
+"var order.getFrontendStatusLabel()":"Order Status",
"var shipment.increment_id":"Shipment Id"
} @-->
{{template config_path="design/email/header_template"}}
@@ -24,7 +24,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
{{trans 'You can check the status of your order by logging into your account .' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html
index 954819949860b..3ef26463ea755 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html
+++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html
@@ -9,7 +9,7 @@
"var billing.getName()":"Guest Customer Name",
"var comment":"Order Comment",
"var order.increment_id":"Order Id",
-"var order.getStatusLabel()":"Order Status",
+"var order.getFrontendStatusLabel()":"Order Status",
"var shipment.increment_id":"Shipment Id"
} @-->
{{template config_path="design/email/header_template"}}
@@ -23,7 +23,7 @@
"Your order #%increment_id has been updated with a status of %order_status ."
increment_id=$order.increment_id
- order_status=$order.getStatusLabel()
+ order_status=$order.getFrontendStatusLabel()
|raw}}
diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
index 2d49042198ff4..32bb22a632177 100644
--- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
@@ -144,6 +144,12 @@
}
}
+ .page-print {
+ .nav-toggle {
+ display: none;
+ }
+ }
+
.page-main {
> .page-title-wrapper {
.page-title + .action {
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 584eefb9bc643..3658902feb3a6 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
@@ -8,6 +8,24 @@
// _____________________________________________
& when (@media-common = true) {
+ .toolbar {
+ &.wishlist-toolbar {
+ .limiter {
+ float: right;
+ }
+ .main .pages {
+ display: inline-block;
+ position: relative;
+ z-index: 0;
+ }
+ .toolbar-amount,
+ .limiter {
+ display: inline-block;
+ z-index: 1;
+ }
+ }
+ }
+
.form.wishlist.items {
.actions-toolbar {
&:extend(.abs-reset-left-margin all);
@@ -185,11 +203,11 @@
.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
.products-grid.wishlist {
margin-bottom: @indent__l;
- margin-right: -@indent__s;
+ margin-right: 0;
.product {
&-item {
- padding: @indent__base @indent__s @indent__base @indent__base;
+ padding: @indent__base 0 @indent__base 0;
position: relative;
&-photo {
@@ -203,6 +221,7 @@
&-actions {
display: block;
+ float: left;
.action {
margin-right: 15px;
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 0c7150c18550b..98dd57dead74c 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_forms.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less
@@ -20,7 +20,7 @@
.lib-form-fieldset();
&:last-child {
- margin-bottom: 0;
+ margin-bottom: @indent__base;
}
> .field,
diff --git a/app/design/frontend/Magento/luma/web/css/source/_sections.less b/app/design/frontend/Magento/luma/web/css/source/_sections.less
index 73665fd22da23..95769c4f4b6ba 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_sections.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_sections.less
@@ -19,16 +19,16 @@
a {
position: relative;
.lib-icon-font(
- @_icon-font-content: @icon-down,
- @_icon-font-size: @font-size__base,
- @_icon-font-line-height: @icon-font__line-height,
- @_icon-font-color: @icon-font__color,
- @_icon-font-color-hover: @icon-font__color-hover,
- @_icon-font-color-active: @icon-font__color-active,
- @_icon-font-margin: @icon-font__margin,
- @_icon-font-vertical-align: @icon-font__vertical-align,
- @_icon-font-position: after,
- @_icon-font-display: false
+ @_icon-font-content: @icon-down,
+ @_icon-font-size: @font-size__base,
+ @_icon-font-line-height: @icon-font__line-height,
+ @_icon-font-color: @icon-font__color,
+ @_icon-font-color-hover: @icon-font__color-hover,
+ @_icon-font-color-active: @icon-font__color-active,
+ @_icon-font-margin: @icon-font__margin,
+ @_icon-font-vertical-align: @icon-font__vertical-align,
+ @_icon-font-position: after,
+ @_icon-font-display: false
);
&:after {
@@ -75,3 +75,17 @@
}
}
}
+
+.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
+ .product.data.items {
+ .item.title {
+ > .switch {
+ padding: 1px 15px 1px;
+ }
+ }
+
+ > .item.content {
+ padding: 10px 15px 30px;
+ }
+ }
+}
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 3814341efd05a..7e3ee14ca5fa4 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
@@ -58,7 +58,7 @@
.modal-custom {
.action-close {
- .lib-css(margin, @indent__m);
+ .lib-css(margin, 15px);
}
}
diff --git a/auth.json.sample b/auth.json.sample
index 81c8fd220eae2..48b13ee8b69da 100644
--- a/auth.json.sample
+++ b/auth.json.sample
@@ -1,8 +1,11 @@
{
- "http-basic": {
- "repo.magento.com": {
- "username": "",
- "password": ""
- }
- }
+ "github-oauth": {
+ "github.com": ""
+ },
+ "http-basic": {
+ "repo.magento.com": {
+ "username": "",
+ "password": ""
+ }
+ }
}
diff --git a/composer.json b/composer.json
index 62b3c95135669..3222de2d1fe0f 100644
--- a/composer.json
+++ b/composer.json
@@ -84,7 +84,7 @@
"require-dev": {
"friendsofphp/php-cs-fixer": "~2.13.0",
"lusitanian/oauth": "~0.8.10",
- "magento/magento2-functional-testing-framework": "~2.3.12",
+ "magento/magento2-functional-testing-framework": "~2.3.13",
"pdepend/pdepend": "2.5.2",
"phpmd/phpmd": "@stable",
"phpunit/phpunit": "~6.5.0",
diff --git a/composer.lock b/composer.lock
index d24dfd2b612fc..697e7df3e19aa 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": "e2fcf8723503311ee9fea99dece55225",
+ "content-hash": "3f58ddc5609e6a934ee3706006357646",
"packages": [
{
"name": "braintree/braintree_php",
@@ -2165,7 +2165,7 @@
},
{
"name": "Gert de Pagter",
- "email": "BackEndTea@gmail.com"
+ "email": "backendtea@gmail.com"
}
],
"description": "Symfony polyfill for ctype functions",
@@ -6503,23 +6503,24 @@
},
{
"name": "magento/magento2-functional-testing-framework",
- "version": "2.3.12",
+ "version": "2.3.13",
"source": {
"type": "git",
"url": "https://github.com/magento/magento2-functional-testing-framework.git",
- "reference": "599004be3e14ebbe6fac77de2edbab934d70f19c"
+ "reference": "2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/599004be3e14ebbe6fac77de2edbab934d70f19c",
- "reference": "599004be3e14ebbe6fac77de2edbab934d70f19c",
+ "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1",
+ "reference": "2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1",
"shasum": ""
},
"require": {
"allure-framework/allure-codeception": "~1.3.0",
- "codeception/codeception": "~2.3.4",
+ "codeception/codeception": "~2.3.4 || ~2.4.0 ",
"consolidation/robo": "^1.0.0",
"epfremme/swagger-php": "^2.0",
+ "ext-curl": "*",
"flow/jsonpath": ">0.2",
"fzaninotto/faker": "^1.6",
"monolog/monolog": "^1.0",
@@ -6536,6 +6537,7 @@
"goaop/framework": "2.2.0",
"php-coveralls/php-coveralls": "^1.0",
"phpmd/phpmd": "^2.6.0",
+ "phpunit/phpunit": "~6.5.0 || ~7.0.0",
"rregeer/phpunit-coverage-check": "^0.1.4",
"sebastian/phpcpd": "~3.0 || ~4.0",
"squizlabs/php_codesniffer": "~3.2",
@@ -6570,7 +6572,7 @@
"magento",
"testing"
],
- "time": "2018-12-19T17:04:11+00:00"
+ "time": "2019-01-29T15:31:14+00:00"
},
{
"name": "mikey179/vfsStream",
diff --git a/dev/tests/acceptance/tests/_data/import_updated.csv b/dev/tests/acceptance/tests/_data/import_updated.csv
new file mode 100644
index 0000000000000..b517150eec840
--- /dev/null
+++ b/dev/tests/acceptance/tests/_data/import_updated.csv
@@ -0,0 +1,4 @@
+product_websites,store_view_code,attribute_set_code,product_type,categories,sku,price,name,url_key
+base,,Default,simple,Default Category/category-admin,productformagetwo68980,123,productformagetwo68980,productformagetwo68980
+,en,Default,simple,,productformagetwo68980,,productformagetwo68980-english,productformagetwo68980-english
+,nl,Default,simple,,productformagetwo68980,,productformagetwo68980-dutch,productformagetwo68980-dutch
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml
index f5cd41bda74d7..404bebfb719dc 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml
index 3e386a034eecc..9fe70c8b4dd3b 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml
index d3b009eecf877..cb3d9edbc1cbb 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductWishlist/Test/EndToEndB2CLoggedInUserTest.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php
index 5458b5cfbb731..add0510c6b40c 100644
--- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php
+++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php
@@ -57,8 +57,8 @@ public function postQuery(string $query, array $variables = [], string $operatio
$headers = array_merge($headers, ['Accept: application/json', 'Content-Type: application/json']);
$requestArray = [
'query' => $query,
- 'variables' => empty($variables) ? $variables : null,
- 'operationName' => empty($operationName) ? $operationName : null
+ 'variables' => !empty($variables) ? $variables : null,
+ 'operationName' => !empty($operationName) ? $operationName : null
];
$postData = $this->json->jsonEncode($requestArray);
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
new file mode 100644
index 0000000000000..7342800379d13
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php
@@ -0,0 +1,240 @@
+customerRegistry = Bootstrap::getObjectManager()->get(CustomerRegistry::class);
+ $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
+ }
+
+ /**
+ * @throws \Exception
+ */
+ public function testCreateCustomerAccountWithPassword()
+ {
+ $newFirstname = 'Richard';
+ $newLastname = 'Rowe';
+ $currentPassword = 'test123#';
+ $newEmail = 'customer_created' . rand(1, 2000000) . '@example.com';
+
+ $query = <<graphQlQuery($query);
+
+ $this->assertEquals($newFirstname, $response['createCustomer']['customer']['firstname']);
+ $this->assertEquals($newLastname, $response['createCustomer']['customer']['lastname']);
+ $this->assertEquals($newEmail, $response['createCustomer']['customer']['email']);
+ $this->assertEquals(true, $response['createCustomer']['customer']['is_subscribed']);
+ }
+
+ /**
+ * @throws \Exception
+ */
+ public function testCreateCustomerAccountWithoutPassword()
+ {
+ $newFirstname = 'Richard';
+ $newLastname = 'Rowe';
+ $newEmail = 'customer_created' . rand(1, 2000000) . '@example.com';
+
+ $query = <<graphQlQuery($query);
+
+ $this->assertEquals($newFirstname, $response['createCustomer']['customer']['firstname']);
+ $this->assertEquals($newLastname, $response['createCustomer']['customer']['lastname']);
+ $this->assertEquals($newEmail, $response['createCustomer']['customer']['email']);
+ $this->assertEquals(true, $response['createCustomer']['customer']['is_subscribed']);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage "input" value should be specified
+ */
+ public function testCreateCustomerIfInputDataIsEmpty()
+ {
+ $query = <<graphQlQuery($query);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The customer email is missing. Enter and try again.
+ */
+ public function testCreateCustomerIfEmailMissed()
+ {
+ $newFirstname = 'Richard';
+ $newLastname = 'Rowe';
+ $currentPassword = 'test123#';
+
+ $query = <<graphQlQuery($query);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage "Email" is not a valid email address.
+ */
+ public function testCreateCustomerIfEmailIsNotValid()
+ {
+ $newFirstname = 'Richard';
+ $newLastname = 'Rowe';
+ $currentPassword = 'test123#';
+ $newEmail = 'email';
+
+ $query = <<graphQlQuery($query);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Field "test123" is not defined by type CustomerInput.
+ */
+ public function testCreateCustomerIfPassedAttributeDosNotExistsInCustomerInput()
+ {
+ $newFirstname = 'Richard';
+ $newLastname = 'Rowe';
+ $currentPassword = 'test123#';
+ $newEmail = 'customer_created' . rand(1, 2000000) . '@example.com';
+
+ $query = <<graphQlQuery($query);
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
index 519fe2b1405a0..6a9708b4f86a2 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php
@@ -218,13 +218,12 @@ private function assertCustomerAddressesFields(AddressInterface $address, $actua
];
$this->assertResponseFields($actualResponse, $assertionMap);
$this->assertTrue(is_array([$actualResponse['region']]), "region field must be of an array type.");
- // https://github.com/magento/graphql-ce/issues/270
-// $assertionRegionMap = [
-// ['response_field' => 'region', 'expected_value' => $address->getRegion()->getRegion()],
-// ['response_field' => 'region_code', 'expected_value' => $address->getRegion()->getRegionCode()],
-// ['response_field' => 'region_id', 'expected_value' => $address->getRegion()->getRegionId()]
-// ];
-// $this->assertResponseFields($actualResponse['region'], $assertionRegionMap);
+ $assertionRegionMap = [
+ ['response_field' => 'region', 'expected_value' => $address->getRegion()->getRegion()],
+ ['response_field' => 'region_code', 'expected_value' => $address->getRegion()->getRegionCode()],
+ ['response_field' => 'region_id', 'expected_value' => $address->getRegion()->getRegionId()]
+ ];
+ $this->assertResponseFields($actualResponse['region'], $assertionRegionMap);
}
/**
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php
index 85b4c62c41945..60acb3a7a4d44 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php
@@ -12,10 +12,10 @@
class IntrospectionQueryTest extends GraphQlAbstract
{
/**
- * Tests that Introspection is disabled when not in developer mode
+ * Tests that Introspection is allowed by default
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
- public function testIntrospectionQueryWithFieldArgs()
+ public function testIntrospectionQuery()
{
$query
= <<expectException(\Exception::class);
- $this->expectExceptionMessage(
- 'GraphQL response contains errors: GraphQL introspection is not allowed, but ' .
- 'the query contained __schema or __type'
- );
- $this->graphQlQuery($query);
+ $this->assertArrayHasKey('__schema', $this->graphQlQuery($query));
}
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php
new file mode 100644
index 0000000000000..78204aaa567b0
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php
@@ -0,0 +1,170 @@
+quoteResource = $objectManager->create(QuoteResource::class);
+ $this->quote = $objectManager->create(Quote::class);
+ $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class);
+ $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php
+ */
+ public function testGetOwnCartForRegisteredCustomer()
+ {
+ $reservedOrderId = 'test_order_item_with_items';
+ $this->quoteResource->load(
+ $this->quote,
+ $reservedOrderId,
+ 'reserved_order_id'
+ );
+
+ $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId());
+ $query = $this->prepareGetCartQuery($maskedQuoteId);
+
+ $response = $this->sendRequestWithToken($query);
+
+ self::assertArrayHasKey('Cart', $response);
+ self::assertNotEmpty($response['Cart']['items']);
+ self::assertNotEmpty($response['Cart']['addresses']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php
+ */
+ public function testGetCartFromAnotherCustomer()
+ {
+ $reservedOrderId = 'test_order_item_with_items';
+ $this->quoteResource->load(
+ $this->quote,
+ $reservedOrderId,
+ 'reserved_order_id'
+ );
+
+ $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId());
+ $query = $this->prepareGetCartQuery($maskedQuoteId);
+
+ self::expectExceptionMessage("The current user cannot perform operations on cart \"$maskedQuoteId\"");
+
+ $this->graphQlQuery($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
+ */
+ public function testGetCartForGuest()
+ {
+ $reservedOrderId = 'test_order_1';
+ $this->quoteResource->load(
+ $this->quote,
+ $reservedOrderId,
+ 'reserved_order_id'
+ );
+
+ $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId());
+ $query = $this->prepareGetCartQuery($maskedQuoteId);
+
+ $response = $this->graphQlQuery($query);
+
+ self::assertArrayHasKey('Cart', $response);
+ }
+
+ public function testGetNonExistentCart()
+ {
+ $maskedQuoteId = 'non_existent_masked_id';
+ $query = $this->prepareGetCartQuery($maskedQuoteId);
+
+ self::expectExceptionMessage("Could not find a cart with ID \"$maskedQuoteId\"");
+
+ $this->graphQlQuery($query);
+ }
+
+ /**
+ * Generates query for setting the specified shipping method on cart
+ *
+ * @param string $maskedQuoteId
+ * @return string
+ */
+ private function prepareGetCartQuery(
+ string $maskedQuoteId
+ ) : string {
+ return <<customerTokenService->createCustomerAccessToken('customer@example.com', 'password');
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+
+ return $this->graphQlQuery($query, [], '', $headerMap);
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php
new file mode 100644
index 0000000000000..7448b165fc234
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php
@@ -0,0 +1,81 @@
+productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Catalog/_files/products_list.php
+ */
+ public function testQueryObjectVariablesSupport()
+ {
+ $productSku = 'simple-249';
+ $minPrice = 153;
+
+ $query
+ = <<<'QUERY'
+query GetProductsQuery($pageSize: Int, $filterInput: ProductFilterInput, $priceSort: SortEnum) {
+ products(
+ pageSize: $pageSize
+ filter: $filterInput
+ sort: {price: $priceSort}
+ ) {
+ items {
+ sku
+ price {
+ minimalPrice {
+ amount {
+ value
+ currency
+ }
+ }
+ }
+ }
+ }
+}
+QUERY;
+
+ $variables = [
+ 'pageSize' => 1,
+ 'priceSort' => 'ASC',
+ 'filterInput' => [
+ 'min_price' => [
+ 'gt' => 150,
+ ],
+ ],
+ ];
+
+ $response = $this->graphQlQuery($query, $variables);
+ /** @var \Magento\Catalog\Model\Product $product */
+ $product = $this->productRepository->get($productSku, false, null, true);
+
+ self::assertArrayHasKey('products', $response);
+ self::assertArrayHasKey('items', $response['products']);
+ self::assertEquals(1, count($response['products']['items']));
+ self::assertArrayHasKey(0, $response['products']['items']);
+ self::assertEquals($product->getSku(), $response['products']['items'][0]['sku']);
+ self::assertEquals(
+ $minPrice,
+ $response['products']['items'][0]['price']['minimalPrice']['amount']['value']
+ );
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderStatusHistoryAddTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderStatusHistoryAddTest.php
index c5ecead00ce29..9e3bd4ca48478 100644
--- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderStatusHistoryAddTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderStatusHistoryAddTest.php
@@ -11,6 +11,7 @@
/**
* Class OrderCommentAddTest
+ *
* @package Magento\Sales\Service\V1
*/
class OrderStatusHistoryAddTest extends WebapiAbstract
@@ -48,7 +49,7 @@ public function testOrderCommentAdd()
OrderStatusHistoryInterface::CREATED_AT => null,
OrderStatusHistoryInterface::PARENT_ID => $order->getId(),
OrderStatusHistoryInterface::ENTITY_NAME => null,
- OrderStatusHistoryInterface::STATUS => null,
+ OrderStatusHistoryInterface::STATUS => $order->getStatus(),
OrderStatusHistoryInterface::IS_VISIBLE_ON_FRONT => 1,
];
@@ -69,25 +70,27 @@ public function testOrderCommentAdd()
//Verification
$comments = $order->load($order->getId())->getAllStatusHistory();
+ $comment = reset($comments);
- $commentData = reset($comments);
- foreach ($commentData as $key => $value) {
- $this->assertEquals(
- $commentData[OrderStatusHistoryInterface::COMMENT],
- $statusHistoryComment->getComment()
- );
- $this->assertEquals(
- $commentData[OrderStatusHistoryInterface::PARENT_ID],
- $statusHistoryComment->getParentId()
- );
- $this->assertEquals(
- $commentData[OrderStatusHistoryInterface::IS_CUSTOMER_NOTIFIED],
- $statusHistoryComment->getIsCustomerNotified()
- );
- $this->assertEquals(
- $commentData[OrderStatusHistoryInterface::IS_VISIBLE_ON_FRONT],
- $statusHistoryComment->getIsVisibleOnFront()
- );
- }
+ $this->assertEquals(
+ $commentData[OrderStatusHistoryInterface::COMMENT],
+ $comment->getComment()
+ );
+ $this->assertEquals(
+ $commentData[OrderStatusHistoryInterface::PARENT_ID],
+ $comment->getParentId()
+ );
+ $this->assertEquals(
+ $commentData[OrderStatusHistoryInterface::IS_CUSTOMER_NOTIFIED],
+ $comment->getIsCustomerNotified()
+ );
+ $this->assertEquals(
+ $commentData[OrderStatusHistoryInterface::IS_VISIBLE_ON_FRONT],
+ $comment->getIsVisibleOnFront()
+ );
+ $this->assertEquals(
+ $commentData[OrderStatusHistoryInterface::STATUS],
+ $comment->getStatus()
+ );
}
}
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.php b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.php
index 1c3d018af077a..4a6202f815b92 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.php
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.php
@@ -9,6 +9,7 @@
use Magento\Backend\Test\Page\Adminhtml\Dashboard;
use Magento\Mtf\Util\Command\Cli\DeployMode;
use Magento\Mtf\TestStep\TestStepFactory;
+use Magento\User\Test\TestStep\LoginUserOnBackendStep;
/**
* Verify visibility of form elements on Configuration page.
@@ -53,9 +54,11 @@ public function __inject(
}
/**
- * Admin login test after JS minification is turned on in production mode
+ * Admin login test after JS minification is turned on in production mode.
+ *
* @param DeployMode $cli
* @param null $configData
+ *
* @return void
*/
public function test(
@@ -64,15 +67,26 @@ public function test(
) {
$this->configData = $configData;
- //Pre-conditions
+ //Pre-conditions
$cli->setDeployModeToDeveloper();
- $this->objectManager->create(
+ $this->stepFactory->create(
\Magento\Config\Test\TestStep\SetupConfigurationStep::class,
['configData' => $this->configData]
)->run();
// Steps
$cli->setDeployModeToProduction();
- $this->adminDashboardPage->open();
+ $this->stepFactory->create(LoginUserOnBackendStep::class)->run();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->stepFactory->create(
+ \Magento\Config\Test\TestStep\SetupConfigurationStep::class,
+ ['configData' => $this->configData]
+ )->cleanup();
}
}
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php
index 7ca5bfd2be140..a34b97b4ce228 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php
@@ -10,7 +10,6 @@
use Magento\Mtf\Client\Locator;
/**
- * Class Search
* Block for "Search" section
*/
class Search extends Block
@@ -77,6 +76,7 @@ public function search($keyword, $length = null)
$keyword = substr($keyword, 0, $length);
}
$this->fillSearch($keyword);
+ $this->waitForElementEnabled($this->searchButton);
$this->_rootElement->find($this->searchButton)->click();
}
@@ -157,4 +157,24 @@ public function clickSuggestedText($text)
$searchAutocomplete = sprintf($this->searchAutocomplete, $text);
$this->_rootElement->find($searchAutocomplete, Locator::SELECTOR_XPATH)->click();
}
+
+ /**
+ * Wait for element is enabled.
+ *
+ * @param string $selector
+ * @param string $strategy
+ * @return bool|null
+ */
+ public function waitForElementEnabled($selector, $strategy = Locator::SELECTOR_CSS)
+ {
+ $browser = $this->browser;
+
+ return $browser->waitUntil(
+ function () use ($browser, $selector, $strategy) {
+ $element = $browser->find($selector, $strategy);
+
+ return !$element->isDisabled() ? true : null;
+ }
+ );
+ }
}
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/DeleteCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/DeleteCategoryEntityTest.xml
index 6951194308bc9..77ed04d40b77a 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/DeleteCategoryEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/DeleteCategoryEntityTest.xml
@@ -8,11 +8,13 @@
+ mftf_migrated:yes
root_category
+ mftf_migrated:yes
root_subcategory
@@ -20,6 +22,7 @@
+ mftf_migrated:yes
default_category
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml
index 76d5a532271ef..1cda62997e189 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
default_category
Name%isolation%
No
@@ -22,6 +23,7 @@
+ mftf_migrated:yes
default_category
Yes
No
@@ -36,6 +38,7 @@
+ mftf_migrated:yes
default_category
No
Name%isolation%
@@ -44,6 +47,7 @@
+ mftf_migrated:yes
custom
No
Category %isolation%
@@ -51,6 +55,7 @@
+ mftf_migrated:yes
default_category
custom
No
@@ -59,6 +64,7 @@
+ mftf_migrated:yes
default_with_custom_url
default_category
custom
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProduct.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProductTest.php
similarity index 98%
rename from dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProduct.php
rename to dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProductTest.php
index cb5ad93ee429b..8f11f31a6dff7 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProduct.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProductTest.php
@@ -26,7 +26,7 @@
*
* @ZephyrId MAGETWO-67570
*/
-class CreateFlatCatalogProduct extends Injectable
+class CreateFlatCatalogProductTest extends Injectable
{
/* tags */
const MVP = 'yes';
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProduct.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProductTest.xml
similarity index 82%
rename from dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProduct.xml
rename to dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProductTest.xml
index 17d362f35ec57..45161e1471f66 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProduct.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateFlatCatalogProductTest.xml
@@ -6,8 +6,9 @@
*/
-->
-
+
+ mftf_migrated:yes
category_flat,product_flat
19
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateStatusTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateStatusTest.xml
index fa82fd90268fd..6936315a12818 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateStatusTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateStatusTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
- catalogProductSimple::simple_10_dollar
- catalogProductSimple::simple_10_dollar
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml
index 6f3803d832c6d..d2fe51ecd810d 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
product_flat
catalogProductSimple::simple_10_dollar
catalogProductSimple::simple_10_dollar
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
index 43741393e7968..90cd6bdb76328 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php
@@ -143,5 +143,6 @@ protected function clearDownloadableData()
/** @var Downloadable $downloadableInfoTab */
$downloadableInfoTab = $this->catalogProductEdit->getProductForm()->getSection('downloadable_information');
$downloadableInfoTab->getDownloadableBlock('Links')->clearDownloadableData();
+ $downloadableInfoTab->setIsDownloadable('No');
}
}
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
index f3df374a8bac8..5fa1cfe5e5911 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml
@@ -11,7 +11,6 @@
catalogProductSimple::default
configurableProduct::default
-
- to_maintain:yes
@@ -21,7 +20,6 @@
- to_maintain:yes
catalogProductSimple::default
catalogProductVirtual::default
-
@@ -29,7 +27,6 @@
- stable:no
configurableProduct::default
catalogProductSimple::product_without_category
deleteVariations
@@ -40,12 +37,10 @@
configurableProduct::default
catalogProductVirtual::required_fields
deleteVariations
- to_maintain:yes
- to_maintain:yes
catalogProductVirtual::default
catalogProductSimple::default
-
@@ -56,7 +51,6 @@
catalogProductVirtual::default
configurableProduct::not_virtual_for_type_switching
-
- to_maintain:yes
@@ -69,7 +63,6 @@
catalogProductVirtual::default
downloadableProduct::default
-
- to_maintain:yes
@@ -81,15 +74,13 @@
downloadableProduct::default
catalogProductSimple::default
-
- to_maintain:yes
downloadableProduct::default
configurableProduct::not_virtual_for_type_switching
- -
- to_maintain:yes
+ clearDownloadableData
@@ -99,7 +90,6 @@
- stable:no
downloadableProduct::default
catalogProductVirtual::default
clearDownloadableData
@@ -110,7 +100,6 @@
catalogProductSimple::default
downloadableProduct::default
-
- to_maintain:yes
diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Grouped/AssociatedProducts/Search/Grid.php b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Grouped/AssociatedProducts/Search/Grid.php
index 45481d6ee0758..6c19291222a97 100644
--- a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Grouped/AssociatedProducts/Search/Grid.php
+++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Grouped/AssociatedProducts/Search/Grid.php
@@ -20,6 +20,13 @@ class Grid extends DataGrid
*/
protected $addProducts = '.action-primary[data-role="action"]';
+ /**
+ * Grid selector.
+ *
+ * @var string
+ */
+ private $gridSelector = '[data-role="grid-wrapper"]';
+
/**
* Filters array mapping
*
@@ -40,4 +47,59 @@ public function addProducts()
{
$this->_rootElement->find($this->addProducts)->click();
}
+
+ /**
+ * @inheritdoc
+ */
+ public function searchAndSelect(array $filter)
+ {
+ $this->waitGridVisible();
+ $this->waitLoader();
+ parent::searchAndSelect($filter);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function waitLoader()
+ {
+ parent::waitLoader();
+ $this->waitGridLoaderInvisible();
+ }
+
+ /**
+ * Wait for grid to appear.
+ *
+ * @return void
+ */
+ private function waitGridVisible()
+ {
+ $browser = $this->_rootElement;
+ $selector = $this->gridSelector;
+
+ return $browser->waitUntil(
+ function () use ($browser, $selector) {
+ $element = $browser->find($selector);
+ return $element->isVisible() ? true : null;
+ }
+ );
+ }
+
+ /**
+ * Wait for grid spinner disappear.
+ *
+ * @return void
+ */
+ private function waitGridLoaderInvisible()
+ {
+ $browser = $this->_rootElement;
+ $selector = $this->loader;
+
+ return $browser->waitUntil(
+ function () use ($browser, $selector) {
+ $element = $browser->find($selector);
+ return $element->isVisible() === false ? true : null;
+ }
+ );
+ }
}
diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.xml
index 38ef02ff49441..39f4fd08bb922 100644
--- a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.xml
@@ -8,7 +8,7 @@
- test_type:acceptance_test, test_type:extended_acceptance_test, stable:no
+ test_type:acceptance_test, test_type:extended_acceptance_test
test-grouped-product-%isolation%
GroupedProduct %isolation%
GroupedProduct_sku%isolation%
diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/GridSortingTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/GridSortingTest.xml
index 6ae9d19a898bc..28894ed6cc158 100644
--- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/GridSortingTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/GridSortingTest.xml
@@ -9,7 +9,6 @@
severity:S2
- to_maintain:yes
Verify sales order grid storting
- -
diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml
index 6f568df8f21ca..8bb4ef56361fb 100644
--- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml
@@ -8,13 +8,11 @@
- stable:no
default
catalogProductSimple::default
- MAGETWO-58762: Customer grid does not open in MoveLastOrderedProductsOnOrderPageTestVariation2 on Jenkins
default
configurableProduct::configurable_with_qty_1
diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php
index 2627f99d4c8c2..54cec6cf279f6 100644
--- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php
+++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php
@@ -28,6 +28,13 @@ class PromoQuoteForm extends FormSections
*/
protected $waitForSelectorVisible = false;
+ /**
+ * Selector of name element on the form.
+ *
+ * @var string
+ */
+ private $nameElementSelector = 'input[name=name]';
+
/**
* Fill form with sections.
*
@@ -38,6 +45,8 @@ class PromoQuoteForm extends FormSections
*/
public function fill(FixtureInterface $fixture, SimpleElement $element = null, array $replace = null)
{
+ $this->waitForElementNotVisible($this->waitForSelector);
+ $this->waitForElementVisible($this->nameElementSelector);
$sections = $this->getFixtureFieldsByContainers($fixture);
if ($replace) {
$sections = $this->prepareData($sections, $replace);
diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml
index 521d7d68ac4a6..5cb5b4db72769 100644
--- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml
+++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml
@@ -271,7 +271,7 @@
No Coupon
1
Yes
- [Total Items Quantity|equals or greater than|3]{Product attribute combination|FOUND|ALL|:[[Category|is|2]]}
+ [Total Items Quantity|equals or greater than|3]
Percent of product price discount
25
No
diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.xml
index e160fef609545..3dfe4cf118552 100644
--- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.xml
@@ -31,7 +31,6 @@
- stable:no
active_sales_rule_product_attribute
active_sales_total_items
250.00
@@ -44,7 +43,6 @@
- to_maintain:yes
active_sales_rule_row_total
active_sales_total_items
simple_for_salesrule_1
diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php
index 235b0d096533f..03476add669be 100644
--- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php
+++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php
@@ -181,7 +181,7 @@ public function resetFilter()
*
* @return void
*/
- protected function waitFilterToLoad()
+ public function waitFilterToLoad()
{
$this->getTemplateBlock()->waitLoader();
$browser = $this->_rootElement;
diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/TestCase/GridSortingTest.php b/dev/tests/functional/tests/app/Magento/Ui/Test/TestCase/GridSortingTest.php
index af4ccfdac9d30..0574fc8dc55fc 100644
--- a/dev/tests/functional/tests/app/Magento/Ui/Test/TestCase/GridSortingTest.php
+++ b/dev/tests/functional/tests/app/Magento/Ui/Test/TestCase/GridSortingTest.php
@@ -89,6 +89,7 @@ public function test(
$page->open();
/** @var DataGrid $gridBlock */
$gridBlock = $page->$gridRetriever();
+ $gridBlock->waitFilterToLoad();
$gridBlock->resetFilter();
$sortingResults = [];
diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Block/Customer/Wishlist/Items/TopToolbar.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Block/Customer/Wishlist/Items/TopToolbar.php
new file mode 100644
index 0000000000000..9d2d0fee46b53
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Block/Customer/Wishlist/Items/TopToolbar.php
@@ -0,0 +1,98 @@
+.page';
+
+ /**
+ * Selector option element
+ *
+ * @var string
+ */
+ private $optionSelector = './/option';
+
+ /**
+ * Go to the next page
+ *
+ * @return bool
+ */
+ public function nextPage()
+ {
+ $nextPageItem = $this->_rootElement->find($this->nextPageSelector);
+ if ($nextPageItem->isVisible()) {
+ $nextPageItem->click();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Go to the first page
+ *
+ * @return bool
+ */
+ public function firstPage()
+ {
+ $firstPageItem = $this->_rootElement->find($this->firstPageSelector);
+ if ($firstPageItem->isVisible()) {
+ $firstPageItem->click();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set value for limiter element by index
+ *
+ * @param int $index
+ * @return $this
+ */
+ public function setLimiterValueByIndex($index)
+ {
+ $options = $this->_rootElement->getElements($this->optionSelector, Locator::SELECTOR_XPATH);
+ if (isset($options[$index])) {
+ $options[$index]->click();
+ }
+ return $this;
+ }
+
+ /**
+ * Get value for limiter element by index
+ *
+ * @param int $index
+ * @return int|null
+ */
+ public function getLimitedValueByIndex($index)
+ {
+ $options = $this->_rootElement->getElements($this->optionSelector, Locator::SELECTOR_XPATH);
+ if (isset($options[$index])) {
+ return $options[$index]->getValue();
+ }
+ return null;
+ }
+}
diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductIsPresentInWishlist.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductIsPresentInWishlist.php
index ae994f84c47f7..058c764be16a4 100644
--- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductIsPresentInWishlist.php
+++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductIsPresentInWishlist.php
@@ -36,6 +36,13 @@ public function processAssert(
$cmsIndex->getLinksBlock()->openLink('My Account');
$customerAccountIndex->getAccountMenuBlock()->openMenuItem('My Wish List');
+ $isProductVisible = $wishlistIndex->getWishlistBlock()->getProductItemsBlock()->getItemProduct($product)
+ ->isVisible();
+ while (!$isProductVisible && $wishlistIndex->getTopToolbar()->nextPage()) {
+ $isProductVisible = $wishlistIndex->getWishlistBlock()->getProductItemsBlock()->getItemProduct($product)
+ ->isVisible();
+ }
+
\PHPUnit\Framework\Assert::assertTrue(
$wishlistIndex->getWishlistBlock()->getProductItemsBlock()->getItemProduct($product)->isVisible(),
$product->getName() . ' is not visible on Wish List page.'
diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductRegularPriceOnStorefront.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductRegularPriceOnStorefront.php
index 68e30e13558ca..dc71939d4790d 100644
--- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductRegularPriceOnStorefront.php
+++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AssertProductRegularPriceOnStorefront.php
@@ -44,44 +44,40 @@ public function processAssert(
$cmsIndex->getLinksBlock()->openLink('My Account');
$customerAccountIndex->getAccountMenuBlock()->openMenuItem('My Wish List');
- $productRegularPrice = 0;
- if ($product instanceof GroupedProduct) {
- $associatedProducts = $product->getAssociated();
+ $isProductVisible = $wishlistIndex->getWishlistBlock()
+ ->getProductItemsBlock()
+ ->getItemProduct($product)
+ ->isVisible();
+ while (!$isProductVisible && $wishlistIndex->getTopToolbar()->nextPage()) {
+ $isProductVisible = $wishlistIndex->getWishlistBlock()
+ ->getProductItemsBlock()
+ ->getItemProduct($product)
+ ->isVisible();
+ }
- /** @var \Magento\Catalog\Test\Fixture\CatalogProductSimple $associatedProduct */
- foreach ($associatedProducts['products'] as $key => $associatedProduct) {
- $qty = $associatedProducts['assigned_products'][$key]['qty'];
- $price = $associatedProduct->getPrice();
- $productRegularPrice += $qty * $price;
- }
+ if ($product instanceof GroupedProduct) {
+ $productRegularPrice = $this->getGroupedProductRegularPrice($product);
} elseif ($product instanceof BundleProduct) {
- $bundleSelection = (array)$product->getBundleSelections();
- foreach ($bundleSelection['products'] as $bundleOption) {
- $regularBundleProductPrice = 0;
- /** @var \Magento\Catalog\Test\Fixture\CatalogProductSimple $bundleProduct */
- foreach ($bundleOption as $bundleProduct) {
- $checkoutData = $bundleProduct->getCheckoutData();
- $bundleProductPrice = $checkoutData['qty'] * $checkoutData['cartItem']['price'];
- if (0 === $regularBundleProductPrice) {
- $regularBundleProductPrice = $bundleProductPrice;
- } else {
- $regularBundleProductPrice = max([$bundleProductPrice, $regularBundleProductPrice]);
- }
- }
- $productRegularPrice += $regularBundleProductPrice;
- }
+ $productRegularPrice = $this->getBundleProductRegularPrice($product);
} else {
- $productRegularPrice = (float)$product->getPrice();
+ $productRegularPrice = (float) $product->getPrice();
}
- $productItem = $wishlistIndex->getWishlistBlock()->getProductItemsBlock()->getItemProduct($product);
- $wishListProductRegularPrice = (float)$productItem->getRegularPrice();
+ $productItem = $wishlistIndex->getWishlistBlock()
+ ->getProductItemsBlock()
+ ->getItemProduct($product);
- \PHPUnit\Framework\Assert::assertEquals(
- $this->regularPriceLabel,
- $productItem->getPriceLabel(),
- 'Wrong product regular price is displayed.'
- );
+ $wishListProductRegularPrice = $product instanceof BundleProduct
+ ? (float)$productItem->getPrice()
+ : (float)$productItem->getRegularPrice();
+
+ if (!$product instanceof BundleProduct) {
+ \PHPUnit\Framework\Assert::assertEquals(
+ $this->regularPriceLabel,
+ $productItem->getPriceLabel(),
+ 'Wrong product regular price is displayed.'
+ );
+ }
\PHPUnit\Framework\Assert::assertNotEmpty(
$wishListProductRegularPrice,
@@ -95,6 +91,52 @@ public function processAssert(
);
}
+ /**
+ * Retrieve grouped product regular price
+ *
+ * @param GroupedProduct $product
+ * @return float
+ */
+ private function getGroupedProductRegularPrice(GroupedProduct $product)
+ {
+ $productRegularPrice = 0;
+ $associatedProducts = $product->getAssociated();
+ /** @var \Magento\Catalog\Test\Fixture\CatalogProductSimple $associatedProduct */
+ foreach ($associatedProducts['products'] as $key => $associatedProduct) {
+ $qty = $associatedProducts['assigned_products'][$key]['qty'];
+ $price = $associatedProduct->getPrice();
+ $productRegularPrice += $qty * $price;
+ }
+ return $productRegularPrice;
+ }
+
+ /**
+ * Retrieve bundle product regular price
+ *
+ * @param BundleProduct $product
+ * @return float
+ */
+ private function getBundleProductRegularPrice(BundleProduct $product)
+ {
+ $productRegularPrice = 0;
+ $bundleSelection = (array) $product->getBundleSelections();
+ foreach ($bundleSelection['products'] as $bundleOption) {
+ $regularBundleProductPrice = 0;
+ /** @var \Magento\Catalog\Test\Fixture\CatalogProductSimple $bundleProduct */
+ foreach ($bundleOption as $bundleProduct) {
+ $checkoutData = $bundleProduct->getCheckoutData();
+ $bundleProductPrice = $checkoutData['qty'] * $checkoutData['cartItem']['price'];
+ if (0 === $regularBundleProductPrice) {
+ $regularBundleProductPrice = $bundleProductPrice;
+ } else {
+ $regularBundleProductPrice = max([$bundleProductPrice, $regularBundleProductPrice]);
+ }
+ }
+ $productRegularPrice += $regularBundleProductPrice;
+ }
+ return $productRegularPrice;
+ }
+
/**
* Returns a string representation of the object.
*
diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Page/WishlistIndex.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Page/WishlistIndex.xml
index 141bc8c5898c2..4e67c8d4e1dd4 100644
--- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Page/WishlistIndex.xml
+++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Page/WishlistIndex.xml
@@ -9,5 +9,6 @@
+
diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml
index 06b1a6078d5c7..e5fa4b6fc11ee 100644
--- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.xml
@@ -108,7 +108,7 @@
- - bundleProduct::with_special_price_and_custom_options
+ - bundleProduct::default_with_one_simple_product
diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php
index b871fe1905910..f14fad963c625 100644
--- a/dev/tests/integration/etc/di/preferences/ce.php
+++ b/dev/tests/integration/etc/di/preferences/ce.php
@@ -27,4 +27,5 @@
\Magento\Framework\App\Config\ScopeConfigInterface::class => \Magento\TestFramework\App\Config::class,
\Magento\Framework\App\ResourceConnection\ConfigInterface::class =>
\Magento\Framework\App\ResourceConnection\Config::class,
+ \Magento\Framework\Session\SessionStartChecker::class => \Magento\TestFramework\Session\SessionStartChecker::class
];
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 @@
+resource === null) {
+ if ($this->resource === null || $this->uri === null) {
$this->markTestIncomplete('Acl test is not complete');
}
if ($this->httpMethod) {
diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php
index 305c3550269da..c0cc1763b2654 100644
--- a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php
+++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php
@@ -128,7 +128,10 @@ public function sendBulk($products)
}
$this->clearProducts();
- $result = $this->massSchedule->publishMass('async.V1.products.POST', $products);
+ $result = $this->massSchedule->publishMass(
+ 'async.magento.catalog.api.productrepositoryinterface.save.post',
+ $products
+ );
//assert bulk accepted with no errors
$this->assertFalse($result->isErrors());
@@ -206,7 +209,7 @@ public function testScheduleMassOneEntityFailure($products)
$expectedErrorMessage = "Data item corresponding to \"product\" " .
"must be specified in the message with topic " .
- "\"async.V1.products.POST\".";
+ "\"async.magento.catalog.api.productrepositoryinterface.save.post\".";
$this->assertEquals(
$expectedErrorMessage,
$reasonException->getMessage()
diff --git a/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/BundleTest.php b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/BundleTest.php
index e15f8d47a7bfc..864bdaa2a1331 100644
--- a/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/BundleTest.php
+++ b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/BundleTest.php
@@ -9,7 +9,10 @@
class BundleTest extends AbstractProductExportImportTestCase
{
- public function exportImportDataProvider()
+ /**
+ * @return array
+ */
+ public function exportImportDataProvider(): array
{
return [
// @todo uncomment after MAGETWO-49677 resolved
@@ -45,17 +48,13 @@ public function exportImportDataProvider()
];
}
- public function importReplaceDataProvider()
- {
- return $this->exportImportDataProvider();
- }
-
/**
- * @param \Magento\Catalog\Model\Product $expectedProduct
- * @param \Magento\Catalog\Model\Product $actualProduct
+ * @inheritdoc
*/
- protected function assertEqualsSpecificAttributes($expectedProduct, $actualProduct)
- {
+ protected function assertEqualsSpecificAttributes(
+ \Magento\Catalog\Model\Product $expectedProduct,
+ \Magento\Catalog\Model\Product $actualProduct
+ ): void {
$expectedBundleProductOptions = $expectedProduct->getExtensionAttributes()->getBundleProductOptions();
$actualBundleProductOptions = $actualProduct->getExtensionAttributes()->getBundleProductOptions();
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 7d6e2e6f97800..2cd0dd2c77560 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, 'manage_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/AbstractProductExportImportTestCase.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/AbstractProductExportImportTestCase.php
index b562879b319d6..d3a2e4c53f246 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/AbstractProductExportImportTestCase.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/AbstractProductExportImportTestCase.php
@@ -55,6 +55,16 @@ abstract class AbstractProductExportImportTestCase extends \PHPUnit\Framework\Te
'is_salable', // stock indexation is not performed during import
];
+ /**
+ * @var array
+ */
+ private static $attributesToRefresh = [
+ 'tax_class_id',
+ ];
+
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
@@ -65,12 +75,17 @@ protected function setUp()
\Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType::$commonAttributesCache = [];
}
+ /**
+ * @inheritdoc
+ */
protected function tearDown()
{
- $this->executeRollbackFixtures($this->fixtures);
+ $this->executeFixtures($this->fixtures, true);
}
/**
+ * Run import/export tests.
+ *
* @magentoAppArea adminhtml
* @magentoDbIsolation disabled
* @magentoAppIsolation enabled
@@ -78,36 +93,60 @@ protected function tearDown()
* @param array $fixtures
* @param string[] $skus
* @param string[] $skippedAttributes
+ * @return void
* @dataProvider exportImportDataProvider
*/
- public function testExport($fixtures, $skus, $skippedAttributes = [])
+ public function testImportExport(array $fixtures, array $skus, array $skippedAttributes = []): void
{
$this->fixtures = $fixtures;
- $this->executeFixtures($fixtures, $skus);
+ $this->executeFixtures($fixtures);
$this->modifyData($skus);
$skippedAttributes = array_merge(self::$skippedAttributes, $skippedAttributes);
- $this->executeExportTest($skus, $skippedAttributes);
+ $csvFile = $this->executeExportTest($skus, $skippedAttributes);
+
+ $this->executeImportReplaceTest($skus, $skippedAttributes, false, $csvFile);
+ $this->executeImportReplaceTest($skus, $skippedAttributes, true, $csvFile);
+ $this->executeImportDeleteTest($skus, $csvFile);
}
- abstract public function exportImportDataProvider();
+ /**
+ * Provide data for import/export.
+ *
+ * @return array
+ */
+ abstract public function exportImportDataProvider(): array;
/**
+ * Modify data.
+ *
* @param array $skus
+ * @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- protected function modifyData($skus)
+ protected function modifyData(array $skus): void
{
}
/**
+ * Prepare product.
+ *
* @param \Magento\Catalog\Model\Product $product
+ * @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- public function prepareProduct($product)
+ public function prepareProduct(\Magento\Catalog\Model\Product $product): void
{
}
- protected function executeExportTest($skus, $skippedAttributes)
+ /**
+ * Execute export test.
+ *
+ * @param array $skus
+ * @param array $skippedAttributes
+ * @return string
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ protected function executeExportTest(array $skus, array $skippedAttributes): string
{
$index = 0;
$ids = [];
@@ -140,10 +179,23 @@ protected function executeExportTest($skus, $skippedAttributes)
$this->assertEqualsSpecificAttributes($origProducts[$index], $newProduct);
}
+
+ return $csvfile;
}
- private function assertEqualsOtherThanSkippedAttributes($expected, $actual, $skippedAttributes)
- {
+ /**
+ * Assert data equals (ignore skipped attributes).
+ *
+ * @param array $expected
+ * @param array $actual
+ * @param array $skippedAttributes
+ * @return void
+ */
+ private function assertEqualsOtherThanSkippedAttributes(
+ array $expected,
+ array $actual,
+ array $skippedAttributes
+ ): void {
foreach ($expected as $key => $value) {
if (is_object($value) || in_array($key, $skippedAttributes)) {
continue;
@@ -158,134 +210,93 @@ private function assertEqualsOtherThanSkippedAttributes($expected, $actual, $ski
}
/**
- * @magentoAppArea adminhtml
- * @magentoDbIsolation disabled
- * @magentoAppIsolation enabled
+ * Execute import test with delete behavior.
*
- * @param array $fixtures
- * @param string[] $skus
- * @dataProvider exportImportDataProvider
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @param array $skus
+ * @param string|null $csvFile
+ * @return void
*/
- public function testImportDelete($fixtures, $skus, $skippedAttributes = [])
- {
- $this->fixtures = $fixtures;
- $this->executeFixtures($fixtures, $skus);
- $this->modifyData($skus);
- $this->executeImportDeleteTest($skus);
- }
-
- protected function executeImportDeleteTest($skus)
+ protected function executeImportDeleteTest(array $skus, string $csvFile = null): void
{
- $csvfile = $this->exportProducts();
- $this->importProducts($csvfile, \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE);
- /** @var \Magento\Catalog\Model\Product $product */
- $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class);
+ $csvFile = $csvFile ?? $this->exportProducts();
+ $this->importProducts($csvFile, \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE);
foreach ($skus as $sku) {
$productId = $this->productResource->getIdBySku($sku);
- $product->load($productId);
- $this->assertNull($product->getId());
+ $this->assertFalse($productId);
}
}
/**
- * Execute fixtures
+ * Execute fixtures.
*
- * @param array $skus
* @param array $fixtures
+ * @param bool $rollback
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- protected function executeFixtures($fixtures, $skus = [])
+ protected function executeFixtures(array $fixtures, bool $rollback = false)
{
foreach ($fixtures as $fixture) {
- $fixturePath = $this->fileSystem->getDirectoryRead(DirectoryList::ROOT)
- ->getAbsolutePath('/dev/tests/integration/testsuite/' . $fixture);
+ $fixturePath = $this->resolveFixturePath($fixture, $rollback);
include $fixturePath;
}
}
/**
- * Execute rollback fixtures
+ * Resolve fixture path.
*
- * @param array $fixtures
- * @return void
+ * @param string $fixture
+ * @param bool $rollback
+ * @return string
*/
- private function executeRollbackFixtures($fixtures)
+ private function resolveFixturePath(string $fixture, bool $rollback = false)
{
- foreach ($fixtures as $fixture) {
- $fixturePath = $this->fileSystem->getDirectoryRead(DirectoryList::ROOT)
- ->getAbsolutePath('/dev/tests/integration/testsuite/' . $fixture);
+ $fixturePath = $this->fileSystem->getDirectoryRead(DirectoryList::ROOT)
+ ->getAbsolutePath('/dev/tests/integration/testsuite/' . $fixture);
+ if ($rollback) {
$fileInfo = pathinfo($fixturePath);
$extension = '';
if (isset($fileInfo['extension'])) {
$extension = '.' . $fileInfo['extension'];
}
- $rollbackfixturePath = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '_rollback' . $extension;
- if (file_exists($rollbackfixturePath)) {
- include $rollbackfixturePath;
- }
+ $fixturePath = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '_rollback' . $extension;
}
+
+ return $fixturePath;
}
/**
+ * Assert that specific attributes equal.
+ *
* @param \Magento\Catalog\Model\Product $expectedProduct
* @param \Magento\Catalog\Model\Product $actualProduct
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- protected function assertEqualsSpecificAttributes($expectedProduct, $actualProduct)
- {
+ protected function assertEqualsSpecificAttributes(
+ \Magento\Catalog\Model\Product $expectedProduct,
+ \Magento\Catalog\Model\Product $actualProduct
+ ): void {
// check custom options
}
/**
- * @magentoAppArea adminhtml
- * @magentoDbIsolation disabled
- * @magentoAppIsolation enabled
+ * Execute import test with replace behavior.
*
- * @param array $fixtures
- * @param string[] $skus
- * @param string[] $skippedAttributes
- * @dataProvider importReplaceDataProvider
- */
- public function testImportReplace($fixtures, $skus, $skippedAttributes = [])
- {
- $this->fixtures = $fixtures;
- $this->executeFixtures($fixtures, $skus);
- $this->modifyData($skus);
- $skippedAttributes = array_merge(self::$skippedAttributes, $skippedAttributes);
- $this->executeImportReplaceTest($skus, $skippedAttributes);
- }
-
- /**
- * @magentoAppArea adminhtml
- * @magentoDbIsolation disabled
- * @magentoAppIsolation enabled
- *
- * @param array $fixtures
- * @param string[] $skus
- * @param string[] $skippedAttributes
- * @dataProvider importReplaceDataProvider
- */
- public function testImportReplaceWithPagination($fixtures, $skus, $skippedAttributes = [])
- {
- $this->fixtures = $fixtures;
- $this->executeFixtures($fixtures, $skus);
- $this->modifyData($skus);
- $skippedAttributes = array_merge(self::$skippedAttributes, $skippedAttributes);
- $this->executeImportReplaceTest($skus, $skippedAttributes, true);
- }
-
- /**
* @param string[] $skus
* @param string[] $skippedAttributes
* @param bool $usePagination
- *
+ * @param string|null $csvfile
+ * @return void
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
- protected function executeImportReplaceTest($skus, $skippedAttributes, $usePagination = false)
- {
+ protected function executeImportReplaceTest(
+ $skus,
+ $skippedAttributes,
+ $usePagination = false,
+ string $csvfile = null
+ ) {
$replacedAttributes = [
'row_id',
'entity_id',
@@ -293,6 +304,7 @@ protected function executeImportReplaceTest($skus, $skippedAttributes, $usePagin
'media_gallery'
];
$skippedAttributes = array_merge($replacedAttributes, $skippedAttributes);
+ $this->cleanAttributesCache();
$index = 0;
$ids = [];
@@ -316,15 +328,15 @@ protected function executeImportReplaceTest($skus, $skippedAttributes, $usePagin
$itemsPerPageProperty->setValue($exportProduct, 1);
}
- $csvfile = $this->exportProducts($exportProduct);
+ $csvfile = $csvfile ?? $this->exportProducts($exportProduct);
$this->importProducts($csvfile, \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE);
while ($index > 0) {
$index--;
$newProduct = $productRepository->get($skus[$index], false, Store::DEFAULT_STORE_ID, true);
// check original product is deleted
- $origProduct = $this->objectManager->create(\Magento\Catalog\Model\Product::class)->load($ids[$index]);
- $this->assertNull($origProduct->getId());
+ $productId = $this->productResource->getIdBySku($ids[$index]);
+ $this->assertFalse($productId);
// check new product data
// @todo uncomment or remove after MAGETWO-49806 resolved
@@ -342,7 +354,7 @@ protected function executeImportReplaceTest($skus, $skippedAttributes, $usePagin
array_filter($origProductData[$attribute]) :
$origProductData[$attribute];
if (!empty($expected)) {
- $actual = isset($newProductData[$attribute]) ? $newProductData[$attribute] : null;
+ $actual = $newProductData[$attribute] ?? null;
$actual = is_array($actual) ? array_filter($actual) : $actual;
$this->assertNotEquals($expected, $actual, $attribute . ' is expected to be changed');
}
@@ -352,7 +364,7 @@ protected function executeImportReplaceTest($skus, $skippedAttributes, $usePagin
}
/**
- * Export products in the system
+ * Export products in the system.
*
* @param \Magento\CatalogImportExport\Model\Export\Product|null $exportProduct
* @return string Return exported file name
@@ -371,17 +383,18 @@ private function exportProducts(\Magento\CatalogImportExport\Model\Export\Produc
)
);
$this->assertNotEmpty($exportProduct->export());
+
return $csvfile;
}
/**
- * Import products from the given file
+ * Import products from the given file.
*
* @param string $csvfile
* @param string $behavior
* @return void
*/
- private function importProducts($csvfile, $behavior)
+ private function importProducts(string $csvfile, string $behavior): void
{
/** @var \Magento\CatalogImportExport\Model\Import\Product $importModel */
$importModel = $this->objectManager->create(
@@ -437,15 +450,33 @@ private function importProducts($csvfile, $behavior)
}
/**
+ * Extract error message.
+ *
* @param \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError[] $errors
* @return string
*/
- private function extractErrorMessage($errors)
+ private function extractErrorMessage(array $errors): string
{
$errorMessage = '';
foreach ($errors as $error) {
$errorMessage = "\n" . $error->getErrorMessage();
}
+
return $errorMessage;
}
+
+ /**
+ * Clean import attribute cache.
+ *
+ * @return void
+ */
+ private function cleanAttributesCache(): void
+ {
+ foreach (self::$attributesToRefresh as $attributeCode) {
+ $attributeId = Import\Product\Type\AbstractType::$attributeCodeToId[$attributeCode] ?? null;
+ if ($attributeId !== null) {
+ unset(Import\Product\Type\AbstractType::$commonAttributesCache[$attributeId]);
+ }
+ }
+ }
}
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 e5d97ac0e6844..c4c6d3ba2d1d2 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -2272,6 +2272,20 @@ public function testImportWithBackordersEnabled(): void
$this->assertFalse($product->getDataByKey('quantity_and_stock_status')['is_in_stock']);
}
+ /**
+ * Test that imported product stock status with stock quantity > 0 and backorders functionality disabled
+ * can be set to 'out of stock'.
+ *
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ */
+ public function testImportWithBackordersDisabled(): void
+ {
+ $this->importFile('products_to_import_with_backorders_disabled_and_not_0_qty.csv');
+ $product = $this->getProductBySku('simple_new');
+ $this->assertFalse($product->getDataByKey('quantity_and_stock_status')['is_in_stock']);
+ }
+
/**
* Import file by providing import filename in parameters.
*
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_disabled_and_not_0_qty.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_disabled_and_not_0_qty.csv
new file mode 100644
index 0000000000000..b22427a8af120
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_disabled_and_not_0_qty.csv
@@ -0,0 +1,2 @@
+sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus
+simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,,,,,,,10/20/2015 7:05,10/20/2015 7:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,0,1,1,10000,1,0,1,1,1,0,1,1,0,0,0,1,,,,,,,,,,,,,
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php
index 11cc73e2cf944..c39acbc338727 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php
@@ -10,18 +10,10 @@
*/
class ProductTest extends AbstractProductExportImportTestCase
{
- /**
- * Set up
- */
- protected function setUp()
- {
- $this->markTestSkipped('MAGETWO-97378');
- }
-
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
- public function exportImportDataProvider()
+ public function exportImportDataProvider(): array
{
return [
'product_export_data' => [
@@ -144,11 +136,6 @@ public function exportImportDataProvider()
];
}
- public function importReplaceDataProvider()
- {
- return $this->exportImportDataProvider();
- }
-
/**
* Fixing https://github.com/magento-engcom/import-export-improvements/issues/50 means that during import images
* can now get renamed for this we need to skip the attribute checking and instead check that the images contain
@@ -158,8 +145,10 @@ public function importReplaceDataProvider()
* @param \Magento\Catalog\Model\Product $expectedProduct
* @param \Magento\Catalog\Model\Product $actualProduct
*/
- protected function assertEqualsSpecificAttributes($expectedProduct, $actualProduct)
- {
+ protected function assertEqualsSpecificAttributes(
+ \Magento\Catalog\Model\Product $expectedProduct,
+ \Magento\Catalog\Model\Product $actualProduct
+ ): void {
if (!empty($actualProduct->getImage())
&& !empty($expectedProduct->getImage())
) {
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote.php
index 2d948ebeb0128..52437ef828afd 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote.php
@@ -9,3 +9,11 @@
->setIsMultiShipping(false)
->setReservedOrderId('test_order_1')
->save();
+
+/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
+$quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
+ ->create();
+$quoteIdMask->setQuoteId($quote->getId());
+$quoteIdMask->setDataChanges(true);
+$quoteIdMask->save();
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/ConfigurableTest.php
index 5184a37563317..338daa56450d4 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/ConfigurableTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableImportExport/Model/ConfigurableTest.php
@@ -9,7 +9,10 @@
class ConfigurableTest extends AbstractProductExportImportTestCase
{
- public function exportImportDataProvider()
+ /**
+ * @return array
+ */
+ public function exportImportDataProvider(): array
{
return [
'configurable-product' => [
@@ -34,11 +37,12 @@ public function exportImportDataProvider()
}
/**
- * @param \Magento\Catalog\Model\Product $expectedProduct
- * @param \Magento\Catalog\Model\Product $actualProduct
+ * @inheritdoc
*/
- protected function assertEqualsSpecificAttributes($expectedProduct, $actualProduct)
- {
+ protected function assertEqualsSpecificAttributes(
+ \Magento\Catalog\Model\Product $expectedProduct,
+ \Magento\Catalog\Model\Product $actualProduct
+ ): void {
/** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $productType */
$productType = $expectedProduct->getTypeInstance();
$expectedAssociatedProducts = $productType->getUsedProductCollection($expectedProduct);
@@ -95,12 +99,16 @@ protected function assertEqualsSpecificAttributes($expectedProduct, $actualProdu
}
}
- public function importReplaceDataProvider()
- {
- $data = $this->exportImportDataProvider();
- foreach ($data as $key => $value) {
- $data[$key][2] = array_merge($value[2], ['_cache_instance_product_set_attributes']);
- }
- return $data;
+ /**
+ * @inheritdoc
+ */
+ protected function executeImportReplaceTest(
+ $skus,
+ $skippedAttributes,
+ $usePagination = false,
+ string $csvfile = null
+ ) {
+ $skippedAttributes = array_merge($skippedAttributes, ['_cache_instance_product_set_attributes']);
+ parent::executeImportReplaceTest($skus, $skippedAttributes, $usePagination, $csvfile);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/GenderTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/GenderTest.php
index 2cc2c2a426d12..3883f3f88ee5e 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/GenderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/GenderTest.php
@@ -15,6 +15,9 @@ class GenderTest extends \PHPUnit\Framework\TestCase
/** @var Gender */
protected $_block;
+ /** @var \Magento\Customer\Model\Attribute */
+ private $_model;
+
/**
* Test initialization and set up. Create the Gender block.
* @return void
@@ -28,6 +31,8 @@ protected function setUp()
)->createBlock(
\Magento\Customer\Block\Widget\Gender::class
);
+ $this->_model = $objectManager->create(\Magento\Customer\Model\Attribute::class);
+ $this->_model->loadByCode('customer', 'gender');
}
/**
@@ -49,7 +54,8 @@ public function testGetGenderOptions()
public function testToHtml()
{
$html = $this->_block->toHtml();
- $this->assertContains('Gender ', $html);
+ $attributeLabel = $this->_model->getStoreLabel();
+ $this->assertContains('' . $attributeLabel . ' ', $html);
$this->assertContains('Male ', $html);
$this->assertContains('Female ', $html);
$this->assertContains('Not Specified ', $html);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/TaxvatTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/TaxvatTest.php
index 3650a7e95a36c..3bc9fea5db381 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/TaxvatTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Widget/TaxvatTest.php
@@ -22,7 +22,13 @@ public function testToHtml()
\Magento\Customer\Block\Widget\Taxvat::class
);
- $this->assertContains('title="Tax/VAT number"', $block->toHtml());
+ $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+ \Magento\Customer\Model\Attribute::class
+ );
+ $model->loadByCode('customer', 'taxvat');
+ $attributeLabel = $model->getStoreLabel();
+
+ $this->assertContains('title="' . $block->escapeHtmlAttr($attributeLabel) . '"', $block->toHtml());
$this->assertNotContains('required', $block->toHtml());
}
@@ -38,13 +44,14 @@ public function testToHtmlRequired()
);
$model->loadByCode('customer', 'taxvat')->setIsRequired(true);
$model->save();
+ $attributeLabel = $model->getStoreLabel();
/** @var \Magento\Customer\Block\Widget\Taxvat $block */
$block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
\Magento\Customer\Block\Widget\Taxvat::class
);
- $this->assertContains('title="Tax/VAT number"', $block->toHtml());
+ $this->assertContains('title="' . $block->escapeHtmlAttr($attributeLabel) . '"', $block->toHtml());
$this->assertContains('required', $block->toHtml());
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
index c94948e23ab4d..ea7a7710acbc3 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
@@ -17,18 +17,35 @@
use Magento\Framework\App\Http;
use Magento\Framework\Data\Form\FormKey;
use Magento\Framework\Message\MessageInterface;
-use Magento\Store\Model\ScopeInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Request;
use Magento\TestFramework\Response;
use Zend\Stdlib\Parameters;
use Magento\Framework\App\Request\Http as HttpRequest;
+use Magento\TestFramework\Mail\Template\TransportBuilderMock;
+use Magento\Framework\Serialize\Serializer\Json;
+use Magento\Framework\Stdlib\CookieManagerInterface;
+use Magento\Theme\Controller\Result\MessagePlugin;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class AccountTest extends \Magento\TestFramework\TestCase\AbstractController
{
+ /**
+ * @var TransportBuilderMock
+ */
+ private $transportBuilderMock;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->transportBuilderMock = $this->_objectManager->get(TransportBuilderMock::class);
+ }
+
/**
* Login the user
*
@@ -133,11 +150,7 @@ public function testForgotPasswordEmailMessageWithSpecialCharacters()
$this->dispatch('customer/account/forgotPasswordPost');
$this->assertRedirect($this->stringContains('customer/account/'));
- /** @var \Magento\TestFramework\Mail\Template\TransportBuilderMock $transportBuilder */
- $transportBuilder = $this->_objectManager->get(
- \Magento\TestFramework\Mail\Template\TransportBuilderMock::class
- );
- $subject = $transportBuilder->getSentMessage()->getSubject();
+ $subject = $this->transportBuilderMock->getSentMessage()->getSubject();
$this->assertContains(
'Test special\' characters',
$subject
@@ -260,26 +273,10 @@ public function testNoFormKeyCreatePostAction()
/**
* @magentoDbIsolation enabled
* @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_disable.php
*/
public function testNoConfirmCreatePostAction()
{
- /** @var \Magento\Framework\App\Config\MutableScopeConfigInterface $mutableScopeConfig */
- $mutableScopeConfig = Bootstrap::getObjectManager()
- ->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class);
-
- $scopeValue = $mutableScopeConfig->getValue(
- 'customer/create_account/confirm',
- ScopeInterface::SCOPE_WEBSITES,
- null
- );
-
- $mutableScopeConfig->setValue(
- 'customer/create_account/confirm',
- 0,
- ScopeInterface::SCOPE_WEBSITES,
- null
- );
-
$this->fillRequestWithAccountDataAndFormKey('test1@email.com');
$this->dispatch('customer/account/createPost');
$this->assertRedirect($this->stringEndsWith('customer/account/'));
@@ -287,38 +284,15 @@ public function testNoConfirmCreatePostAction()
$this->equalTo(['Thank you for registering with Main Website Store.']),
MessageInterface::TYPE_SUCCESS
);
-
- $mutableScopeConfig->setValue(
- 'customer/create_account/confirm',
- $scopeValue,
- ScopeInterface::SCOPE_WEBSITES,
- null
- );
}
/**
* @magentoDbIsolation enabled
* @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php
*/
public function testWithConfirmCreatePostAction()
{
- /** @var \Magento\Framework\App\Config\MutableScopeConfigInterface $mutableScopeConfig */
- $mutableScopeConfig = Bootstrap::getObjectManager()
- ->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class);
-
- $scopeValue = $mutableScopeConfig->getValue(
- 'customer/create_account/confirm',
- ScopeInterface::SCOPE_WEBSITES,
- null
- );
-
- $mutableScopeConfig->setValue(
- 'customer/create_account/confirm',
- 1,
- ScopeInterface::SCOPE_WEBSITES,
- null
- );
-
$this->fillRequestWithAccountDataAndFormKey('test2@email.com');
$this->dispatch('customer/account/createPost');
$this->assertRedirect($this->stringContains('customer/account/index/'));
@@ -330,13 +304,6 @@ public function testWithConfirmCreatePostAction()
]),
MessageInterface::TYPE_SUCCESS
);
-
- $mutableScopeConfig->setValue(
- 'customer/create_account/confirm',
- $scopeValue,
- ScopeInterface::SCOPE_WEBSITES,
- null
- );
}
/**
@@ -730,6 +697,46 @@ public function testLoginPostRedirect($redirectDashboard, string $redirectUrl)
$this->assertTrue($this->_objectManager->get(Session::class)->isLoggedIn());
}
+ /**
+ * Test that confirmation email address displays special characters correctly.
+ *
+ * @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php
+ *
+ * @return void
+ */
+ public function testConfirmationEmailWithSpecialCharacters(): void
+ {
+ $email = 'customer+confirmation@example.com';
+ $this->dispatch('customer/account/confirmation/email/customer%2Bconfirmation%40email.com');
+ $this->getRequest()->setPostValue('email', $email);
+ $this->dispatch('customer/account/confirmation/email/customer%2Bconfirmation%40email.com');
+
+ $this->assertRedirect($this->stringContains('customer/account/index'));
+ $this->assertSessionMessages(
+ $this->equalTo(['Please check your email for confirmation key.']),
+ MessageInterface::TYPE_SUCCESS
+ );
+
+ /** @var $message \Magento\Framework\Mail\Message */
+ $message = $this->transportBuilderMock->getSentMessage();
+ $rawMessage = $message->getRawMessage();
+
+ $this->assertContains('To: ' . $email, $rawMessage);
+
+ $content = $message->getBody()->getPartContent(0);
+ $confirmationUrl = $this->getConfirmationUrlFromMessageContent($content);
+ $this->setRequestInfo($confirmationUrl, 'confirm');
+ $this->clearCookieMessagesList();
+ $this->dispatch($confirmationUrl);
+
+ $this->assertRedirect($this->stringContains('customer/account/index'));
+ $this->assertSessionMessages(
+ $this->equalTo(['Thank you for registering with Main Website Store.']),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
/**
* Data provider for testLoginPostRedirect.
*
@@ -847,4 +854,53 @@ private function assertResponseRedirect(Response $response, string $redirectUrl)
$this->assertTrue($response->isRedirect());
$this->assertSame($redirectUrl, $response->getHeader('Location')->getUri());
}
+
+ /**
+ * Add new request info (request uri, path info, action name).
+ *
+ * @param string $uri
+ * @param string $actionName
+ * @return void
+ */
+ private function setRequestInfo(string $uri, string $actionName): void
+ {
+ $this->getRequest()
+ ->setRequestUri($uri)
+ ->setPathInfo()
+ ->setActionName($actionName);
+ }
+
+ /**
+ * Clear cookie messages list.
+ *
+ * @return void
+ */
+ private function clearCookieMessagesList(): void
+ {
+ $cookieManager = $this->_objectManager->get(CookieManagerInterface::class);
+ $jsonSerializer = $this->_objectManager->get(Json::class);
+ $cookieManager->setPublicCookie(
+ MessagePlugin::MESSAGES_COOKIES_NAME,
+ $jsonSerializer->serialize([])
+ );
+ }
+
+ /**
+ * Get confirmation URL from message content.
+ *
+ * @param string $content
+ * @return string
+ */
+ private function getConfirmationUrlFromMessageContent(string $content): string
+ {
+ $confirmationUrl = '';
+
+ if (preg_match('.*?)".*>', $content, $matches)) {
+ $confirmationUrl = $matches['url'];
+ $confirmationUrl = str_replace('http://localhost/index.php/', '', $confirmationUrl);
+ $confirmationUrl = html_entity_decode($confirmationUrl);
+ }
+
+ return $confirmationUrl;
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AddressTest.php
index 017532fb392b5..b6e8cba82adae 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/AddressTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AddressTest.php
@@ -67,4 +67,28 @@ public function testUpdateDataOverrideExistingData()
$this->assertEquals('CompanyZ', $updatedAddressData->getCompany());
$this->assertEquals('99999', $updatedAddressData->getPostcode());
}
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer_sample.php
+ */
+ public function testUpdateDataForExistingCustomer()
+ {
+ /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */
+ $customerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(CustomerRegistry::class);
+ /** @var \Magento\Customer\Model\Data\Address $addressData */
+ $updatedAddressData = $this->addressFactory->create()
+ ->setId(1)
+ ->setCustomerId($customerRegistry->retrieveByEmail('customer@example.com')->getId())
+ ->setCity('CityZ')
+ ->setCompany('CompanyZ')
+ ->setPostcode('99999');
+ $updatedAddressData = $this->addressModel->updateData($updatedAddressData)->getDataModel();
+
+ $this->assertEquals(1, $updatedAddressData->getId());
+ $this->assertEquals('CityZ', $updatedAddressData->getCity());
+ $this->assertEquals('CompanyZ', $updatedAddressData->getCompany());
+ $this->assertEquals('99999', $updatedAddressData->getPostcode());
+ $this->assertEquals(true, $updatedAddressData->isDefaultBilling());
+ $this->assertEquals(true, $updatedAddressData->isDefaultShipping());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php
index 794fce17480fa..a5c69bcd3239e 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php
@@ -239,10 +239,10 @@ public function testGetCustomerAttributeMetadata()
$this->assertNotEmpty($attributes);
// remove odd extension attributes
- $allAtrributes = $expectAttrsWithVals;
- $allAtrributes['created_at'] = $attributes['created_at'];
- $allAtrributes['updated_at'] = $attributes['updated_at'];
- $attributes = array_intersect_key($attributes, $allAtrributes);
+ $allAttributes = $expectAttrsWithVals;
+ $allAttributes['created_at'] = $attributes['created_at'];
+ $allAttributes['updated_at'] = $attributes['updated_at'];
+ $attributes = array_intersect_key($attributes, $allAttributes);
foreach ($attributes as $attributeCode => $attributeValue) {
$this->assertNotNull($attributeCode);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php
new file mode 100644
index 0000000000000..7d4e451db514b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php
@@ -0,0 +1,21 @@
+create(MutableScopeConfigInterface::class);
+
+$mutableScopeConfig->setValue(
+ 'customer/create_account/confirm',
+ 0,
+ ScopeInterface::SCOPE_WEBSITES,
+ null
+);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php
new file mode 100644
index 0000000000000..36743b4a20e9a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php
@@ -0,0 +1,23 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var Config $config */
+$config = Bootstrap::getObjectManager()->create(Config::class);
+$config->deleteConfig('customer/create_account/confirm');
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php
new file mode 100644
index 0000000000000..c8deb7ec2a536
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php
@@ -0,0 +1,21 @@
+create(MutableScopeConfigInterface::class);
+
+$mutableScopeConfig->setValue(
+ 'customer/create_account/confirm',
+ 1,
+ ScopeInterface::SCOPE_WEBSITES,
+ null
+);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php
new file mode 100644
index 0000000000000..36743b4a20e9a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php
@@ -0,0 +1,23 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var Config $config */
+$config = Bootstrap::getObjectManager()->create(Config::class);
+$config->deleteConfig('customer/create_account/confirm');
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php
new file mode 100644
index 0000000000000..c4f046bac57a6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php
@@ -0,0 +1,37 @@
+create(Customer::class);
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = $objectManager->create(CustomerRepositoryInterface::class);
+/** @var CustomerInterface $customerInterface */
+$customerInterface = $objectManager->create(CustomerInterface::class);
+
+$customerInterface->setWebsiteId(1)
+ ->setEmail('customer+confirmation@example.com')
+ ->setConfirmation($customer->getRandomConfirmationKey())
+ ->setGroupId(1)
+ ->setStoreId(1)
+ ->setFirstname('John')
+ ->setLastname('Smith')
+ ->setDefaultBilling(1)
+ ->setDefaultShipping(1)
+ ->setTaxvat('12')
+ ->setGender(0);
+
+$customerRepository->save($customerInterface, 'password');
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php
new file mode 100644
index 0000000000000..7a0ebf74ed8a0
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php
@@ -0,0 +1,31 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = Bootstrap::getObjectManager()->create(CustomerRepositoryInterface::class);
+
+try {
+ $customer = $customerRepository->get('customer+confirmation@example.com');
+ $customerRepository->delete($customer);
+} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+ // Customer with the specified email does not exist
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_sample.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_sample.php
index e12eec293f2ad..1af6489870559 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_sample.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_sample.php
@@ -19,6 +19,7 @@
'lastname' => 'test lastname',
'email' => 'customer@example.com',
'default_billing' => 1,
+ 'default_shipping' => 1,
'password' => '123123q',
'attribute_set_id' => 1,
];
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..8874d880a4dd1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php
@@ -0,0 +1,241 @@
+dhlCarrier = $objectManager->create(
+ \Magento\Dhl\Model\Carrier::class,
+ ['httpClientFactory' => $this->getHttpClientFactory()]
+ );
+ }
+
+ /**
+ * @magentoConfigFixture default_store carriers/dhl/id CustomerSiteID
+ * @magentoConfigFixture default_store carriers/dhl/password CustomerPassword
+ * @param string[] $trackingNumbers
+ * @param string $responseXml
+ * @param $expectedTrackingData
+ * @param string $expectedRequestXml
+ * @dataProvider getTrackingDataProvider
+ */
+ public function testGetTracking(
+ $trackingNumbers,
+ string $responseXml,
+ $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
+ */
+ private function assertRequest(string $expectedRequestXml, string $requestXml): void
+ {
+ $expectedRequestElement = new Element($expectedRequestXml);
+ $requestElement = new Element($requestXml);
+ $requestMessageTime = $requestElement->Request->ServiceHeader->MessageTime->__toString();
+ $this->assertEquals(
+ 1,
+ preg_match("/\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): void
+ {
+ if (null === $expectedTrackingData) {
+ $this->assertNull($trackingResults);
+ } else {
+ $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 100755
index 0000000000000..9887cecbd2d4e
--- /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 100755
index 0000000000000..c2abd68d3c4ae
--- /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 100755
index 0000000000000..c0a18fcc4e2f6
--- /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 100755
index 0000000000000..dac69a0d68c57
--- /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
+
\ No newline at end of file
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 100755
index 0000000000000..369236d80c614
--- /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 100755
index 0000000000000..ef303eaab64f7
--- /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/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/LinksTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/LinksTest.php
index c743fcec1dd89..b07a6506c1b78 100644
--- a/dev/tests/integration/testsuite/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/LinksTest.php
+++ b/dev/tests/integration/testsuite/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/LinksTest.php
@@ -5,6 +5,13 @@
*/
namespace Magento\Downloadable\Block\Adminhtml\Catalog\Product\Edit\Tab\Downloadable;
+/**
+ * Class LinksTest
+ *
+ * @package Magento\Downloadable\Block\Adminhtml\Catalog\Product\Edit\Tab\Downloadable
+ * @deprecated
+ * @see \Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Links
+ */
class LinksTest extends \PHPUnit\Framework\TestCase
{
/**
diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/SamplesTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/SamplesTest.php
index 28d3680358329..3f3b3bd621953 100644
--- a/dev/tests/integration/testsuite/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/SamplesTest.php
+++ b/dev/tests/integration/testsuite/Magento/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/SamplesTest.php
@@ -5,6 +5,13 @@
*/
namespace Magento\Downloadable\Block\Adminhtml\Catalog\Product\Edit\Tab\Downloadable;
+/**
+ * Class SamplesTest
+ *
+ * @package Magento\Downloadable\Block\Adminhtml\Catalog\Product\Edit\Tab\Downloadable
+ * @deprecated
+ * @see \Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Samples
+ */
class SamplesTest extends \PHPUnit\Framework\TestCase
{
public function testGetUploadButtonsHtml()
diff --git a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php
index c80cd13a1683b..d0e4471e2ea68 100644
--- a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php
+++ b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/DownloadableTest.php
@@ -9,7 +9,10 @@
class DownloadableTest extends AbstractProductExportImportTestCase
{
- public function exportImportDataProvider()
+ /**
+ * @return array
+ */
+ public function exportImportDataProvider(): array
{
return [
'downloadable-product' => [
@@ -31,79 +34,32 @@ public function exportImportDataProvider()
];
}
- public function importReplaceDataProvider()
- {
- return $this->exportImportDataProvider();
- }
-
- /**
- * @param array $fixtures
- * @param string[] $skus
- * @param string[] $skippedAttributes
- * @dataProvider exportImportDataProvider
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- *
- * @todo remove after MAGETWO-38240 resolved
- */
- public function testExport($fixtures, $skus, $skippedAttributes = [], $rollbackFixtures = [])
- {
- $this->markTestSkipped('Uncomment after MAGETWO-38240 resolved');
- }
-
- /**
- * @param array $fixtures
- * @param string[] $skus
- * @dataProvider exportImportDataProvider
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- *
- * @todo remove after MAGETWO-38240 resolved
- */
- public function testImportDelete($fixtures, $skus, $skippedAttributes = [], $rollbackFixtures = [])
- {
- $this->markTestSkipped('Uncomment after MAGETWO-38240 resolved');
- }
-
/**
- * @magentoAppArea adminhtml
- * @magentoDbIsolation enabled
- * @magentoAppIsolation enabled
- *
- * @param array $fixtures
- * @param string[] $skus
- * @param string[] $skippedAttributes
- * @dataProvider importReplaceDataProvider
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * Run import/export tests.
*
- * @todo remove after MAGETWO-38240 resolved
- */
- public function testImportReplace($fixtures, $skus, $skippedAttributes = [], $rollbackFixtures = [])
- {
- $this->markTestSkipped('Uncomment after MAGETWO-38240 resolved');
- }
-
- /**
* @magentoAppArea adminhtml
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
* @magentoAppIsolation enabled
*
* @param array $fixtures
* @param string[] $skus
* @param string[] $skippedAttributes
- * @dataProvider importReplaceDataProvider
- *
+ * @return void
+ * @dataProvider exportImportDataProvider
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- public function testImportReplaceWithPagination($fixtures, $skus, $skippedAttributes = [])
+ public function testImportExport(array $fixtures, array $skus, array $skippedAttributes = []): void
{
$this->markTestSkipped('Uncomment after MAGETWO-38240 resolved');
}
/**
- * @param \Magento\Catalog\Model\Product $expectedProduct
- * @param \Magento\Catalog\Model\Product $actualProduct
+ * @inheritdoc
*/
- protected function assertEqualsSpecificAttributes($expectedProduct, $actualProduct)
- {
+ protected function assertEqualsSpecificAttributes(
+ \Magento\Catalog\Model\Product $expectedProduct,
+ \Magento\Catalog\Model\Product $actualProduct
+ ): void {
$expectedProductLinks = $expectedProduct->getExtensionAttributes()->getDownloadableProductLinks();
$expectedProductSamples = $expectedProduct->getExtensionAttributes()->getDownloadableProductSamples();
diff --git a/dev/tests/integration/testsuite/Magento/GroupedImportExport/Model/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedImportExport/Model/GroupedTest.php
index 67817b068ff09..afd515757ae4b 100644
--- a/dev/tests/integration/testsuite/Magento/GroupedImportExport/Model/GroupedTest.php
+++ b/dev/tests/integration/testsuite/Magento/GroupedImportExport/Model/GroupedTest.php
@@ -9,7 +9,10 @@
class GroupedTest extends AbstractProductExportImportTestCase
{
- public function exportImportDataProvider()
+ /**
+ * @return array
+ */
+ public function exportImportDataProvider(): array
{
return [
'grouped-product' => [
@@ -23,17 +26,13 @@ public function exportImportDataProvider()
];
}
- public function importReplaceDataProvider()
- {
- return $this->exportImportDataProvider();
- }
-
/**
- * @param \Magento\Catalog\Model\Product $expectedProduct
- * @param \Magento\Catalog\Model\Product $actualProduct
+ * @inheritdoc
*/
- protected function assertEqualsSpecificAttributes($expectedProduct, $actualProduct)
- {
+ protected function assertEqualsSpecificAttributes(
+ \Magento\Catalog\Model\Product $expectedProduct,
+ \Magento\Catalog\Model\Product $actualProduct
+ ): void {
$expectedAssociatedProducts = $expectedProduct->getTypeInstance()->getAssociatedProducts($expectedProduct);
$actualAssociatedProducts = $actualProduct->getTypeInstance()->getAssociatedProducts($actualProduct);
diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php
index 9afce0ed10bcd..a3cf42b48489f 100644
--- a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php
+++ b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php
@@ -3,9 +3,11 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\ImportExport\Controller\Adminhtml\Import;
use Magento\Framework\Filesystem\DirectoryList;
+use Magento\Framework\HTTP\Adapter\FileTransferFactory;
use Magento\ImportExport\Model\Import;
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
@@ -25,7 +27,7 @@ class ValidateTest extends \Magento\TestFramework\TestCase\AbstractBackendContro
* @magentoDbIsolation enabled
* @SuppressWarnings(PHPMD.Superglobals)
*/
- public function testValidationReturn(string $fileName, string $mimeType, string $message, string $delimiter)
+ public function testValidationReturn(string $fileName, string $mimeType, string $message, string $delimiter): void
{
$validationStrategy = ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_STOP_ON_ERROR;
@@ -62,10 +64,7 @@ public function testValidationReturn(string $fileName, string $mimeType, string
$this->_objectManager->configure(
[
- 'preferences' => [
- \Magento\Framework\HTTP\Adapter\FileTransferFactory::class =>
- \Magento\ImportExport\Controller\Adminhtml\Import\HttpFactoryMock::class
- ]
+ 'preferences' => [FileTransferFactory::class => HttpFactoryMock::class]
]
);
@@ -82,7 +81,7 @@ public function testValidationReturn(string $fileName, string $mimeType, string
/**
* @return array
*/
- public function validationDataProvider()
+ public function validationDataProvider(): array
{
return [
[
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php
index 999522a49e006..b289e9b94558e 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php
@@ -11,26 +11,37 @@
namespace Magento\Sales\Block\Adminhtml\Order\Create\Form;
use Magento\Backend\Model\Session\Quote as SessionQuote;
+use Magento\Customer\Api\Data\AttributeMetadataInterface;
use Magento\Customer\Api\Data\AttributeMetadataInterfaceFactory;
+use Magento\Customer\Model\Data\Option;
use Magento\Customer\Model\Metadata\Form;
use Magento\Customer\Model\Metadata\FormFactory;
use Magento\Framework\View\LayoutInterface;
use Magento\Quote\Model\Quote;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\ObjectManager;
+use PHPUnit\Framework\MockObject\MockObject;
/**
* @magentoAppArea adminhtml
*/
class AccountTest extends \PHPUnit\Framework\TestCase
{
- /** @var Account */
+ /**
+ * @var Account
+ */
private $accountBlock;
/**
- * @var Bootstrap
+ * @var ObjectManager
*/
private $objectManager;
+ /**
+ * @var SessionQuote|MockObject
+ */
+ private $session;
+
/**
* @magentoDataFixture Magento/Sales/_files/quote.php
*/
@@ -38,19 +49,23 @@ protected function setUp()
{
$this->objectManager = Bootstrap::getObjectManager();
$quote = $this->objectManager->create(Quote::class)->load(1);
- $sessionQuoteMock = $this->getMockBuilder(
- SessionQuote::class
- )->disableOriginalConstructor()->setMethods(
- ['getCustomerId', 'getStore', 'getStoreId', 'getQuote']
- )->getMock();
- $sessionQuoteMock->expects($this->any())->method('getCustomerId')->will($this->returnValue(1));
- $sessionQuoteMock->expects($this->any())->method('getQuote')->will($this->returnValue($quote));
+
+ $this->session = $this->getMockBuilder(SessionQuote::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getCustomerId', 'getStore', 'getStoreId', 'getQuote', 'getQuoteId'])
+ ->getMock();
+ $this->session->method('getCustomerId')
+ ->willReturn(1);
+ $this->session->method('getQuote')
+ ->willReturn($quote);
+ $this->session->method('getQuoteId')
+ ->willReturn($quote->getId());
/** @var LayoutInterface $layout */
$layout = $this->objectManager->get(LayoutInterface::class);
$this->accountBlock = $layout->createBlock(
Account::class,
'address_block' . rand(),
- ['sessionQuote' => $sessionQuoteMock]
+ ['sessionQuote' => $this->session]
);
parent::setUp();
}
@@ -62,13 +77,13 @@ public function testGetForm()
{
$expectedFields = ['group_id', 'email'];
$form = $this->accountBlock->getForm();
- $this->assertEquals(1, $form->getElements()->count(), "Form has invalid number of fieldsets");
+ self::assertEquals(1, $form->getElements()->count(), "Form has invalid number of fieldsets");
$fieldset = $form->getElements()[0];
- $this->assertEquals(count($expectedFields), $fieldset->getElements()->count());
+ self::assertEquals(count($expectedFields), $fieldset->getElements()->count());
foreach ($fieldset->getElements() as $element) {
- $this->assertTrue(
+ self::assertTrue(
in_array($element->getId(), $expectedFields),
sprintf('Unexpected field "%s" in form.', $element->getId())
);
@@ -79,6 +94,7 @@ public function testGetForm()
* Tests a case when user defined custom attribute has default value.
*
* @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoConfigFixture current_store customer/create_account/default_group 3
*/
public function testGetFormWithUserDefinedAttribute()
{
@@ -91,18 +107,27 @@ public function testGetFormWithUserDefinedAttribute()
$form = $accountBlock->getForm();
$form->setUseContainer(true);
+ $content = $form->toHtml();
- $this->assertContains(
+ self::assertContains(
'Yes ',
- $form->toHtml(),
- 'Default value for user defined custom attribute should be selected'
+ $content,
+ 'Default value for user defined custom attribute should be selected.'
+ );
+
+ self::assertContains(
+ 'Customer Group 1 ',
+ $content,
+ 'The Customer Group specified for the chosen store should be selected.'
);
}
/**
- * @return \PHPUnit_Framework_MockObject_MockObject
+ * Creates a mock for Form object.
+ *
+ * @return MockObject
*/
- private function getFormFactoryMock(): \PHPUnit_Framework_MockObject_MockObject
+ private function getFormFactoryMock(): MockObject
{
/** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */
$attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class);
@@ -113,11 +138,12 @@ private function getFormFactoryMock(): \PHPUnit_Framework_MockObject_MockObject
->setDefaultValue('1')
->setFrontendLabel('Yes/No');
+ /** @var Form|MockObject $form */
$form = $this->getMockBuilder(Form::class)
->disableOriginalConstructor()
->getMock();
$form->method('getUserAttributes')->willReturn([$booleanAttribute]);
- $form->method('getSystemAttributes')->willReturn([]);
+ $form->method('getSystemAttributes')->willReturn([$this->createCustomerGroupAttribute()]);
$formFactory = $this->getMockBuilder(FormFactory::class)
->disableOriginalConstructor()
@@ -126,4 +152,33 @@ private function getFormFactoryMock(): \PHPUnit_Framework_MockObject_MockObject
return $formFactory;
}
+
+ /**
+ * Creates a customer group attribute object.
+ *
+ * @return AttributeMetadataInterface
+ */
+ private function createCustomerGroupAttribute(): AttributeMetadataInterface
+ {
+ /** @var Option $option1 */
+ $option1 = $this->objectManager->create(Option::class);
+ $option1->setValue(3);
+ $option1->setLabel('Customer Group 1');
+
+ /** @var Option $option2 */
+ $option2 = $this->objectManager->create(Option::class);
+ $option2->setValue(4);
+ $option2->setLabel('Customer Group 2');
+
+ /** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */
+ $attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class);
+ $attribute = $attributeMetadataFactory->create()
+ ->setAttributeCode('group_id')
+ ->setBackendType('static')
+ ->setFrontendInput('select')
+ ->setOptions([$option1, $option2])
+ ->setIsRequired(true);
+
+ return $attribute;
+ }
}
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 e2638b5df1f88..f863edd049258 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
@@ -9,21 +9,61 @@
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Request\Http;
+use Magento\Framework\Data\Form\FormKey;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Message\MessageInterface;
use Magento\Quote\Api\CartRepositoryInterface;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Model\OrderRepository;
use Magento\Sales\Model\Service\OrderService;
+use Magento\TestFramework\Mail\Template\TransportBuilderMock;
use Magento\TestFramework\TestCase\AbstractBackendController;
+use PHPUnit\Framework\Constraint\StringContains;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
+/**
+ * Class test backend order save.
+ *
+ * @magentoAppArea adminhtml
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class SaveTest extends AbstractBackendController
{
+ /**
+ * @var TransportBuilderMock
+ */
+ private $transportBuilder;
+
+ /**
+ * @var FormKey
+ */
+ private $formKey;
+
+ /**
+ * @var string
+ */
+ protected $resource = 'Magento_Sales::create';
+
+ /**
+ * @var string
+ */
+ protected $uri = 'backend/sales/order_create/save';
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class);
+ $this->formKey = $this->_objectManager->get(FormKey::class);
+ }
+
/**
* Checks a case when order creation is failed on payment method processing but new customer already created
* in the database and after new controller dispatching the customer should be already loaded in session
* to prevent invalid validation.
*
- * @magentoAppArea adminhtml
* @magentoDataFixture Magento/Sales/_files/quote_with_new_customer.php
*/
public function testExecuteWithPaymentOperation()
@@ -36,7 +76,7 @@ public function testExecuteWithPaymentOperation()
$email = 'john.doe001@test.com';
$data = [
'account' => [
- 'email' => $email
+ 'email' => $email,
]
];
$this->getRequest()->setMethod(Http::METHOD_POST);
@@ -66,13 +106,52 @@ public function testExecuteWithPaymentOperation()
$this->_objectManager->removeSharedInstance(OrderService::class);
}
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php
+ *
+ * @return void
+ */
+ public function testSendEmailOnOrderSave(): void
+ {
+ $this->prepareRequest(['send_confirmation' => true]);
+ $this->dispatch('backend/sales/order_create/save');
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('You created the order.')]),
+ MessageInterface::TYPE_SUCCESS
+ );
+
+ $this->assertRedirect($this->stringContains('sales/order/view/'));
+
+ $orderId = $this->getOrderId();
+ if ($orderId === false) {
+ $this->fail('Order is not created.');
+ }
+ $order = $this->getOrder($orderId);
+
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Your %1 order confirmation', $order->getStore()->getFrontendName())->render();
+ $assert = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new StringContains(
+ 'Thank you for your order from ' . $order->getStore()->getFrontendName()
+ ),
+ new StringContains(
+ "Your Order #{$order->getIncrementId()} "
+ )
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $assert);
+ }
+
/**
* Gets quote by reserved order id.
*
* @param string $reservedOrderId
* @return \Magento\Quote\Api\Data\CartInterface
*/
- private function getQuote($reservedOrderId)
+ private function getQuote(string $reservedOrderId): \Magento\Quote\Api\Data\CartInterface
{
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
$searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class);
@@ -82,6 +161,81 @@ private function getQuote($reservedOrderId)
/** @var CartRepositoryInterface $quoteRepository */
$quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
$items = $quoteRepository->getList($searchCriteria)->getItems();
+
return array_pop($items);
}
+
+ /**
+ * @inheritdoc
+ * @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ * @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param int $orderId
+ * @return OrderInterface
+ */
+ private function getOrder(int $orderId): OrderInterface
+ {
+ return $this->_objectManager->get(OrderRepository::class)->get($orderId);
+ }
+
+ /**
+ * @param array $params
+ * @return void
+ */
+ private function prepareRequest(array $params = []): void
+ {
+ $quote = $this->getQuote('guest_quote');
+ $session = $this->_objectManager->get(Quote::class);
+ $session->setQuoteId($quote->getId());
+ $session->setCustomerId(0);
+
+ $email = 'john.doe001@test.com';
+ $data = [
+ 'account' => [
+ 'email' => $email,
+ ],
+ ];
+
+ $data = array_replace_recursive($data, $params);
+
+ $this->getRequest()
+ ->setMethod('POST')
+ ->setParams(['form_key' => $this->formKey->getFormKey()])
+ ->setPostValue(['order' => $data]);
+ }
+
+ /**
+ * @return string|bool
+ */
+ protected function getOrderId()
+ {
+ $currentUrl = $this->getResponse()->getHeader('Location');
+ $orderId = false;
+
+ if (preg_match('/order_id\/(?\d+)/', $currentUrl, $matches)) {
+ $orderId = $matches['order_id'] ?? '';
+ }
+
+ return $orderId;
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AbstractCreditmemoControllerTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AbstractCreditmemoControllerTest.php
new file mode 100644
index 0000000000000..2a7731715021b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AbstractCreditmemoControllerTest.php
@@ -0,0 +1,92 @@
+transportBuilder = $this->_objectManager->get(TransportBuilderMock::class);
+ $this->orderRepository = $this->_objectManager->get(OrderRepository::class);
+ $this->formKey = $this->_objectManager->get(FormKey::class);
+ }
+
+ /**
+ * @param string $incrementalId
+ * @return OrderInterface|null
+ */
+ protected function getOrder(string $incrementalId)
+ {
+ /** @var SearchCriteria $searchCriteria */
+ $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class)
+ ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId)
+ ->create();
+
+ $orders = $this->orderRepository->getList($searchCriteria)->getItems();
+ /** @var OrderInterface|null $order */
+ $order = reset($orders);
+
+ return $order;
+ }
+
+ /**
+ * @param OrderInterface $order
+ * @return CreditmemoInterface
+ */
+ protected function getCreditMemo(OrderInterface $order): CreditmemoInterface
+ {
+ /** @var \Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection $creditMemoCollection */
+ $creditMemoCollection = $this->_objectManager->create(
+ \Magento\Sales\Model\ResourceModel\Order\Creditmemo\CollectionFactory::class
+ )->create();
+
+ /** @var CreditmemoInterface $creditMemo */
+ $creditMemo = $creditMemoCollection
+ ->setOrderFilter($order)
+ ->setPageSize(1)
+ ->getFirstItem();
+
+ return $creditMemo;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AddCommentTest.php
new file mode 100644
index 0000000000000..2f23da8b3db87
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AddCommentTest.php
@@ -0,0 +1,102 @@
+prepareRequest(
+ [
+ 'comment' => ['comment' => $comment, 'is_customer_notified' => true],
+ ]
+ );
+ $this->dispatch('backend/sales/order_creditmemo/addComment');
+ $html = $this->getResponse()->getBody();
+ $this->assertContains($comment, $html);
+
+ $message = $this->transportBuilder->getSentMessage();
+ $subject =__('Update to your %1 credit memo', $order->getStore()->getFrontendName())->render();
+ $messageConstraint = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new RegularExpression(
+ sprintf(
+ "/Your order #%s has been updated with a status of.*%s/",
+ $order->getIncrementId(),
+ $order->getFrontendStatusLabel()
+ )
+ ),
+ new StringContains($comment)
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $messageConstraint);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest(['comment' => ['comment' => 'Comment']]);
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest(['comment' => ['comment' => 'Comment']]);
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param array $params
+ * @return \Magento\Sales\Api\Data\OrderInterface|null
+ */
+ private function prepareRequest(array $params = [])
+ {
+ $order = $this->getOrder('100000001');
+ $creditmemo = $this->getCreditMemo($order);
+
+ $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams(
+ [
+ 'id' => $creditmemo->getEntityId(),
+ 'form_key' => $this->formKey->getFormKey(),
+ ]
+ );
+
+ $data = $params ?? [];
+ $this->getRequest()->setPostValue($data);
+
+ return $order;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php
new file mode 100644
index 0000000000000..fa5da2e0e50d1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php
@@ -0,0 +1,99 @@
+prepareRequest(['creditmemo' => ['send_email' => true]]);
+ $this->dispatch('backend/sales/order_creditmemo/save');
+
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('You created the credit memo.')]),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId()));
+
+ $creditMemo = $this->getCreditMemo($order);
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Credit memo for your %1 order', $order->getStore()->getFrontendName())->render();
+ $messageConstraint = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new StringContains(
+ 'Thank you for your order from ' . $creditMemo->getStore()->getFrontendName()
+ ),
+ new StringContains(
+ "Your Credit Memo #{$creditMemo->getIncrementId()} for Order #{$order->getIncrementId()}"
+ )
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $messageConstraint);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param array $params
+ * @return \Magento\Sales\Api\Data\OrderInterface|null
+ */
+ private function prepareRequest(array $params = [])
+ {
+ $order = $this->getOrder('100000001');
+ $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams(
+ [
+ 'order_id' => $order->getEntityId(),
+ 'form_key' => $this->formKey->getFormKey(),
+ ]
+ );
+
+ $data = ['creditmemo' => ['do_offline' => true]];
+ $data = array_replace_recursive($data, $params);
+
+ $this->getRequest()->setPostValue($data);
+
+ return $order;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php
new file mode 100644
index 0000000000000..4d19106ad8e51
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php
@@ -0,0 +1,136 @@
+orderRepository = $this->_objectManager->get(OrderRepository::class);
+ $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testSendOrderEmail(): void
+ {
+ $order = $this->prepareRequest();
+ $this->dispatch('backend/sales/order/email');
+
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('You sent the order email.')]),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+
+ $redirectUrl = 'sales/order/view/order_id/' . $order->getEntityId();
+ $this->assertRedirect($this->stringContains($redirectUrl));
+
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Your %1 order confirmation', $order->getStore()->getFrontendName())->render();
+ $assert = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new StringContains(
+ 'Thank you for your order from ' . $order->getStore()->getFrontendName()
+ ),
+ new StringContains(
+ "Your Order #{$order->getIncrementId()} "
+ )
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $assert);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param string $incrementalId
+ * @return OrderInterface|null
+ */
+ private function getOrder(string $incrementalId)
+ {
+ /** @var SearchCriteria $searchCriteria */
+ $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class)
+ ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId)
+ ->create();
+
+ $orders = $this->orderRepository->getList($searchCriteria)->getItems();
+ /** @var OrderInterface|null $order */
+ $order = reset($orders);
+
+ return $order;
+ }
+
+ /**
+ * @return OrderInterface|null
+ */
+ private function prepareRequest()
+ {
+ $order = $this->getOrder('100000001');
+ $this->getRequest()->setParams(['order_id' => $order->getEntityId()]);
+
+ return $order;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php
new file mode 100644
index 0000000000000..3ba54418b6c26
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php
@@ -0,0 +1,92 @@
+transportBuilder = $this->_objectManager->get(TransportBuilderMock::class);
+ $this->orderRepository = $this->_objectManager->get(OrderRepository::class);
+ $this->formKey = $this->_objectManager->get(FormKey::class);
+ }
+
+ /**
+ * @param string $incrementalId
+ * @return OrderInterface|null
+ */
+ protected function getOrder(string $incrementalId)
+ {
+ /** @var SearchCriteria $searchCriteria */
+ $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class)
+ ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId)
+ ->create();
+
+ $orders = $this->orderRepository->getList($searchCriteria)->getItems();
+ /** @var OrderInterface $order */
+ $order = reset($orders);
+
+ return $order;
+ }
+
+ /**
+ * @param OrderInterface $order
+ * @return InvoiceInterface
+ */
+ protected function getInvoiceByOrder(OrderInterface $order): InvoiceInterface
+ {
+ /** @var \Magento\Sales\Model\ResourceModel\Order\Invoice\Collection $invoiceCollection */
+ $invoiceCollection = $this->_objectManager->create(
+ \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory::class
+ )->create();
+
+ /** @var InvoiceInterface $invoice */
+ $invoice = $invoiceCollection
+ ->setOrderFilter($order)
+ ->setPageSize(1)
+ ->getFirstItem();
+
+ return $invoice;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php
new file mode 100644
index 0000000000000..81e1dd7afc496
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php
@@ -0,0 +1,103 @@
+prepareRequest(
+ [
+ 'comment' => ['comment' => $comment, 'is_customer_notified' => true],
+ ]
+ );
+ $this->dispatch('backend/sales/order_invoice/addComment');
+
+ $html = $this->getResponse()->getBody();
+ $this->assertContains($comment, $html);
+
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Update to your %1 invoice', $order->getStore()->getFrontendName())->render();
+ $messageConstraint = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new RegularExpression(
+ sprintf(
+ "/Your order #%s has been updated with a status of.*%s/",
+ $order->getIncrementId(),
+ $order->getFrontendStatusLabel()
+ )
+ ),
+ new StringContains($comment)
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $messageConstraint);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest(['comment' => ['comment' => 'Comment']]);
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest(['comment' => ['comment' => 'Comment']]);
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param array $params
+ * @return \Magento\Sales\Api\Data\OrderInterface|null
+ */
+ private function prepareRequest(array $params = [])
+ {
+ $order = $this->getOrder('100000001');
+ $invoice = $this->getInvoiceByOrder($order);
+
+ $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams(
+ [
+ 'id' => $invoice->getEntityId(),
+ 'form_key' => $this->formKey->getFormKey(),
+ ]
+ );
+
+ $data = $params ?? [];
+ $this->getRequest()->setPostValue($data);
+
+ return $order;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/EmailTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/EmailTest.php
new file mode 100644
index 0000000000000..85223528ec82a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/EmailTest.php
@@ -0,0 +1,88 @@
+getOrder('100000001');
+ $invoice = $this->getInvoiceByOrder($order);
+
+ $this->getRequest()->setParams(['invoice_id' => $invoice->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/email');
+
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('You sent the message.')]),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+
+ $redirectUrl = sprintf(
+ 'sales/invoice/view/order_id/%s/invoice_id/%s',
+ $order->getEntityId(),
+ $invoice->getEntityId()
+ );
+ $this->assertRedirect($this->stringContains($redirectUrl));
+
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Invoice for your %1 order', $order->getStore()->getFrontendName())->render();
+ $messageConstraint = $this->logicalAnd(
+ new StringContains($invoice->getBillingAddress()->getName()),
+ new StringContains(
+ 'Thank you for your order from ' . $invoice->getStore()->getFrontendName()
+ ),
+ new StringContains(
+ "Your Invoice #{$invoice->getIncrementId()} for Order #{$order->getIncrementId()}"
+ )
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $messageConstraint);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $order = $this->getOrder('100000001');
+ $invoice = $this->getInvoiceByOrder($order);
+ $this->uri .= '/invoice_id/' . $invoice->getEntityId();
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $order = $this->getOrder('100000001');
+ $invoice = $this->getInvoiceByOrder($order);
+ $this->uri .= '/invoice_id/' . $invoice->getEntityId();
+
+ parent::testAclNoAccess();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php
new file mode 100644
index 0000000000000..68074e38d9a39
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php
@@ -0,0 +1,97 @@
+prepareRequest(['invoice' => ['send_email' => true]]);
+ $this->dispatch('backend/sales/order_invoice/save');
+
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('The invoice has been created.')]),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId()));
+
+ $invoice = $this->getInvoiceByOrder($order);
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Invoice for your %1 order', $order->getStore()->getFrontendName())->render();
+ $messageConstraint = $this->logicalAnd(
+ new StringContains($invoice->getBillingAddress()->getName()),
+ new StringContains(
+ 'Thank you for your order from ' . $invoice->getStore()->getFrontendName()
+ ),
+ new StringContains(
+ "Your Invoice #{$invoice->getIncrementId()} for Order #{$order->getIncrementId()}"
+ )
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $messageConstraint);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param array $params
+ * @return \Magento\Sales\Api\Data\OrderInterface|null
+ */
+ private function prepareRequest(array $params = [])
+ {
+ $order = $this->getOrder('100000001');
+ $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams(
+ [
+ 'order_id' => $order->getEntityId(),
+ 'form_key' => $this->formKey->getFormKey(),
+ ]
+ );
+
+ $data = $params ?? [];
+ $this->getRequest()->setPostValue($data);
+
+ return $order;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php
new file mode 100644
index 0000000000000..1035ce1592314
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php
@@ -0,0 +1,100 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->transportBuilder = $this->objectManager->get(TransportBuilderMock::class);
+ $this->quoteIdMaskFactory = $this->objectManager->get(QuoteIdMaskFactory::class);
+ $this->formKey = $this->objectManager->get(FormKey::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php
+ * @return void
+ */
+ public function testSendEmailOnOrderPlace(): void
+ {
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
+ $quote->load('guest_quote', 'reserved_order_id');
+
+ $checkoutSession = $this->objectManager->get(CheckoutSession::class);
+ $checkoutSession->setQuoteId($quote->getId());
+
+ /** @var QuoteIdMask $quoteIdMask */
+ $quoteIdMask = $this->quoteIdMaskFactory->create();
+ $quoteIdMask->load($quote->getId(), 'quote_id');
+ $cartId = $quoteIdMask->getMaskedId();
+
+ /** @var GuestCartManagementInterface $cartManagement */
+ $cartManagement = $this->objectManager->get(GuestCartManagementInterface::class);
+ $orderId = $cartManagement->placeOrder($cartId);
+ $order = $this->objectManager->get(OrderRepository::class)->get($orderId);
+
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Your %1 order confirmation', $order->getStore()->getFrontendName())->render();
+ $assert = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new StringContains(
+ 'Thank you for your order from ' . $order->getStore()->getFrontendName()
+ ),
+ new StringContains(
+ "Your Order #{$order->getIncrementId()} "
+ )
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $assert);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php
new file mode 100644
index 0000000000000..b8f2ca38e2489
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php
@@ -0,0 +1,68 @@
+loadArea(\Magento\Framework\App\Area::AREA_FRONTEND);
+
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+/** @var \Magento\Catalog\Model\Product $product */
+$product = $objectManager->create(\Magento\Catalog\Model\Product::class);
+$product->setTypeId('simple')
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setName('Simple Product')
+ ->setSku('simple-product-guest-quote')
+ ->setPrice(10)
+ ->setTaxClassId(0)
+ ->setMetaTitle('meta title')
+ ->setMetaKeyword('meta keyword')
+ ->setMetaDescription('meta description')
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setStockData(
+ [
+ 'qty' => 100,
+ 'is_in_stock' => 1,
+ ]
+ )->save();
+
+$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+$product = $productRepository->get('simple-product-guest-quote');
+
+$addressData = reset($addresses);
+
+$billingAddress = $objectManager->create(
+ \Magento\Quote\Model\Quote\Address::class,
+ ['data' => $addressData]
+);
+$billingAddress->setAddressType('billing');
+
+$shippingAddress = clone $billingAddress;
+$shippingAddress->setId(null)->setAddressType('shipping');
+
+$store = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore();
+
+/** @var \Magento\Quote\Model\Quote $quote */
+$quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
+$quote->setCustomerIsGuest(true)
+ ->setStoreId($store->getId())
+ ->setReservedOrderId('guest_quote')
+ ->setBillingAddress($billingAddress)
+ ->setShippingAddress($shippingAddress)
+ ->addProduct($product);
+$quote->getPayment()->setMethod('checkmo');
+$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate')->setCollectShippingRates(true);
+$quote->collectTotals();
+
+$quoteRepository = $objectManager->create(\Magento\Quote\Api\CartRepositoryInterface::class);
+$quoteRepository->save($quote);
+
+/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
+$quoteIdMask = $objectManager->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)->create();
+$quoteIdMask->setQuoteId($quote->getId());
+$quoteIdMask->setDataChanges(true);
+$quoteIdMask->save();
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses_rollback.php
new file mode 100644
index 0000000000000..02c42153b72c3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses_rollback.php
@@ -0,0 +1,32 @@
+get(\Magento\Framework\Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var $quote \Magento\Quote\Model\Quote */
+$quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
+$quote->load('guest_quote', 'reserved_order_id');
+if ($quote->getId()) {
+ $quote->delete();
+}
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('simple-product-guest-quote', false, null, true);
+ $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/Sales/_files/order.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php
index a1c5f8277762c..6b9cf3bc613ce 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php
@@ -44,7 +44,8 @@
->setBasePrice($product->getPrice())
->setPrice($product->getPrice())
->setRowTotal($product->getPrice())
- ->setProductType('simple');
+ ->setProductType('simple')
+ ->setName($product->getName());
/** @var Order $order */
$order = $objectManager->create(Order::class);
diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AbstractShipmentControllerTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AbstractShipmentControllerTest.php
new file mode 100644
index 0000000000000..0a1926d58624c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AbstractShipmentControllerTest.php
@@ -0,0 +1,92 @@
+transportBuilder = $this->_objectManager->get(TransportBuilderMock::class);
+ $this->orderRepository = $this->_objectManager->get(OrderRepository::class);
+ $this->formKey = $this->_objectManager->get(FormKey::class);
+ }
+
+ /**
+ * @param string $incrementalId
+ * @return OrderInterface|null
+ */
+ protected function getOrder(string $incrementalId)
+ {
+ /** @var SearchCriteria $searchCriteria */
+ $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class)
+ ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId)
+ ->create();
+
+ $orders = $this->orderRepository->getList($searchCriteria)->getItems();
+ /** @var OrderInterface|null $order */
+ $order = reset($orders);
+
+ return $order;
+ }
+
+ /**
+ * @param OrderInterface $order
+ * @return ShipmentInterface
+ */
+ protected function getShipment(OrderInterface $order): ShipmentInterface
+ {
+ /** @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Collection $shipmentCollection */
+ $shipmentCollection = $this->_objectManager->create(
+ \Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory::class
+ )->create();
+
+ /** @var ShipmentInterface $shipment */
+ $shipment = $shipmentCollection
+ ->setOrderFilter($order)
+ ->setPageSize(1)
+ ->getFirstItem();
+
+ return $shipment;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddCommentTest.php
new file mode 100644
index 0000000000000..25a44bab62994
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddCommentTest.php
@@ -0,0 +1,102 @@
+prepareRequest(
+ [
+ 'comment' => ['comment' => $comment, 'is_customer_notified' => true],
+ ]
+ );
+ $this->dispatch('backend/admin/order_shipment/addComment');
+ $html = $this->getResponse()->getBody();
+ $this->assertContains($comment, $html);
+
+ $message = $this->transportBuilder->getSentMessage();
+ $subject =__('Update to your %1 shipment', $order->getStore()->getFrontendName())->render();
+ $messageConstraint = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new RegularExpression(
+ sprintf(
+ "/Your order #%s has been updated with a status of.*%s/",
+ $order->getIncrementId(),
+ $order->getFrontendStatusLabel()
+ )
+ ),
+ new StringContains($comment)
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $messageConstraint);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest(['comment', ['comment' => 'Comment']]);
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest(['comment', ['comment' => 'Comment']]);
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param array $params
+ * @return \Magento\Sales\Api\Data\OrderInterface|null
+ */
+ private function prepareRequest(array $params = [])
+ {
+ $order = $this->getOrder('100000001');
+ $shipment = $this->getShipment($order);
+
+ $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams(
+ [
+ 'id' => $shipment->getEntityId(),
+ 'form_key' => $this->formKey->getFormKey(),
+ ]
+ );
+
+ $data = $params ?? [];
+ $this->getRequest()->setPostValue($data);
+
+ return $order;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php
new file mode 100644
index 0000000000000..27b5bb02d4b22
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php
@@ -0,0 +1,97 @@
+prepareRequest(['shipment' => ['send_email' => true]]);
+ $this->dispatch('backend/admin/order_shipment/save');
+
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('The shipment has been created.')]),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId()));
+
+ $shipment = $this->getShipment($order);
+ $message = $this->transportBuilder->getSentMessage();
+ $subject = __('Your %1 order has shipped', $order->getStore()->getFrontendName())->render();
+ $messageConstraint = $this->logicalAnd(
+ new StringContains($order->getBillingAddress()->getName()),
+ new StringContains(
+ 'Thank you for your order from ' . $shipment->getStore()->getFrontendName()
+ ),
+ new StringContains(
+ "Your Shipment #{$shipment->getIncrementId()} for Order #{$order->getIncrementId()}"
+ )
+ );
+
+ $this->assertEquals($message->getSubject(), $subject);
+ $this->assertThat($message->getRawMessage(), $messageConstraint);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclHasAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclHasAccess();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function testAclNoAccess()
+ {
+ $this->prepareRequest();
+
+ parent::testAclNoAccess();
+ }
+
+ /**
+ * @param array $params
+ * @return \Magento\Sales\Api\Data\OrderInterface|null
+ */
+ private function prepareRequest(array $params = [])
+ {
+ $order = $this->getOrder('100000001');
+ $this->getRequest()->setMethod('POST');
+ $this->getRequest()->setParams(
+ [
+ 'order_id' => $order->getEntityId(),
+ 'form_key' => $this->formKey->getFormKey(),
+ ]
+ );
+
+ $data = $params ?? [];
+ $this->getRequest()->setPostValue($data);
+
+ return $order;
+ }
+}
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.test.js
index c98c459d98819..6b68978380ea4 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.test.js
@@ -46,8 +46,8 @@ define([
variation.serializeData();
- expect(variation.source.data['configurable-matrix']).toBeUndefined();
- expect(variation.source.data['associated_product_ids']).toBeUndefined();
+ expect(variation.source.data['configurable-matrix']).toEqual(matrix);
+ expect(variation.source.data['associated_product_ids']).toEqual(ids);
expect(variation.source.data['configurable-matrix-serialized']).toEqual(resultMatrix);
expect(variation.source.data['associated_product_ids_serialized']).toEqual(resultIds);
});
@@ -112,8 +112,8 @@ define([
variation.source.data['associated_product_ids_serialized'] = JSON.stringify(['some old data']);
variation.serializeData();
- expect(variation.source.data['configurable-matrix']).toBeUndefined();
- expect(variation.source.data['associated_product_ids']).toBeUndefined();
+ expect(variation.source.data['configurable-matrix']).toEqual(matrix);
+ expect(variation.source.data['associated_product_ids']).toEqual(ids);
expect(variation.source.data['configurable-matrix-serialized']).toEqual(resultMatrix);
expect(variation.source.data['associated_product_ids_serialized']).toEqual(resultIds);
});
@@ -164,8 +164,8 @@ define([
variation.serializeData();
- expect(variation.source.data['configurable-matrix']).toBeUndefined();
- expect(variation.source.data['associated_product_ids']).toBeUndefined();
+ expect(variation.source.data['configurable-matrix']).toEqual(matrix);
+ expect(variation.source.data['associated_product_ids']).toEqual(ids);
expect(variation.source.data['configurable-matrix-serialized']).toEqual(resultMatrix);
expect(variation.source.data['associated_product_ids_serialized']).toEqual(resultIds);
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php
index 6d627574a3a18..b5a4e41b63279 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php
@@ -194,7 +194,7 @@ private function assertClassesExist(array $classes, string $path): void
foreach ($classes as $class) {
$class = trim($class, '\\');
try {
- if (strrchr($class, '\\') === false and !Classes::isVirtual($class)) {
+ if (strrchr($class, '\\') === false && !Classes::isVirtual($class)) {
$badUsages[] = $class;
continue;
} else {
diff --git a/lib/internal/LinLibertineFont/ChangeLog.txt b/lib/internal/LinLibertineFont/ChangeLog.txt
index 8dc2c56567a4b..83b8792e71eda 100644
--- a/lib/internal/LinLibertineFont/ChangeLog.txt
+++ b/lib/internal/LinLibertineFont/ChangeLog.txt
@@ -952,7 +952,7 @@ Changes to version 0.5.8 regular(|) & italic(/) (20040315)
Changes to version 0.5.7 regular(|) & italic(/) (20040315)
-N is now 66pt wider
-^ {Ascicircum} is now better
-- {exclamdown} is now availible
+- {exclamdown} is now available
- {currency} has been added
| "-" hyphen is the same as softhyphen. length is now 510pt
-bars have been made
diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php
index 615c295675adc..40b03b068d6ab 100644
--- a/lib/internal/Magento/Framework/App/DeploymentConfig.php
+++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php
@@ -70,6 +70,11 @@ public function get($key = null, $defaultValue = null)
if ($key === null) {
return $this->flatData;
}
+
+ if (array_key_exists($key, $this->flatData) && $this->flatData[$key] === null) {
+ return '';
+ }
+
return $this->flatData[$key] ?? $defaultValue;
}
@@ -146,6 +151,8 @@ private function load()
}
/**
+ * Array keys conversion
+ *
* Convert associative array of arbitrary depth to a flat associative array with concatenated key path as keys
* each level of array is accessible by path key
*
diff --git a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php b/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php
index 8fcbd74c884d9..b79ba49a24ddd 100644
--- a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php
+++ b/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php
@@ -124,20 +124,22 @@ protected function extractTopics($config)
$requestSchema,
$responseSchema
);
+ $isSynchronous = $this->extractTopicIsSynchronous($topicNode);
if ($serviceMethod) {
$output[$topicName] = $this->reflectionGenerator->generateTopicConfigForServiceMethod(
$topicName,
$serviceMethod[ConfigParser::TYPE_NAME],
$serviceMethod[ConfigParser::METHOD_NAME],
- $handlers
+ $handlers,
+ $isSynchronous
);
} elseif ($requestSchema && $responseSchema) {
$output[$topicName] = [
Config::TOPIC_NAME => $topicName,
- Config::TOPIC_IS_SYNCHRONOUS => true,
+ Config::TOPIC_IS_SYNCHRONOUS => $isSynchronous,
Config::TOPIC_REQUEST => $requestSchema,
Config::TOPIC_REQUEST_TYPE => Config::TOPIC_REQUEST_TYPE_CLASS,
- Config::TOPIC_RESPONSE => $responseSchema,
+ Config::TOPIC_RESPONSE => ($isSynchronous) ? $responseSchema: null,
Config::TOPIC_HANDLERS => $handlers
];
} elseif ($requestSchema) {
@@ -258,4 +260,20 @@ protected function parseServiceMethod($serviceMethod, $topicName)
);
return $parsedServiceMethod;
}
+
+ /**
+ * Extract is_synchronous topic value.
+ *
+ * @param \DOMNode $topicNode
+ * @return bool
+ */
+ private function extractTopicIsSynchronous($topicNode): bool
+ {
+ $attributeName = Config::TOPIC_IS_SYNCHRONOUS;
+ $topicAttributes = $topicNode->attributes;
+ if (!$topicAttributes->getNamedItem($attributeName)) {
+ return true;
+ }
+ return $this->booleanUtils->toBoolean($topicAttributes->getNamedItem($attributeName)->nodeValue);
+ }
}
diff --git a/lib/internal/Magento/Framework/Communication/Config/ReflectionGenerator.php b/lib/internal/Magento/Framework/Communication/Config/ReflectionGenerator.php
index d1bc62464f212..7ef84f1c43b10 100644
--- a/lib/internal/Magento/Framework/Communication/Config/ReflectionGenerator.php
+++ b/lib/internal/Magento/Framework/Communication/Config/ReflectionGenerator.php
@@ -42,7 +42,10 @@ public function extractMethodMetadata($className, $methodName)
$result = [
Config::SCHEMA_METHOD_PARAMS => [],
Config::SCHEMA_METHOD_RETURN_TYPE => $this->methodsMap->getMethodReturnType($className, $methodName),
- Config::SCHEMA_METHOD_HANDLER => [Config::HANDLER_TYPE => $className, Config::HANDLER_METHOD => $methodName]
+ Config::SCHEMA_METHOD_HANDLER => [
+ Config::HANDLER_TYPE => $className,
+ Config::HANDLER_METHOD => $methodName
+ ]
];
$paramsMeta = $this->methodsMap->getMethodParams($className, $methodName);
foreach ($paramsMeta as $paramPosition => $paramMeta) {
@@ -63,16 +66,27 @@ public function extractMethodMetadata($className, $methodName)
* @param string $serviceType
* @param string $serviceMethod
* @param array|null $handlers
+ * @param bool|null $isSynchronous
* @return array
*/
- public function generateTopicConfigForServiceMethod($topicName, $serviceType, $serviceMethod, $handlers = [])
- {
+ public function generateTopicConfigForServiceMethod(
+ $topicName,
+ $serviceType,
+ $serviceMethod,
+ $handlers = [],
+ $isSynchronous = null
+ ) {
$methodMetadata = $this->extractMethodMetadata($serviceType, $serviceMethod);
$returnType = $methodMetadata[Config::SCHEMA_METHOD_RETURN_TYPE];
$returnType = ($returnType != 'void' && $returnType != 'null') ? $returnType : null;
+ if (!isset($isSynchronous)) {
+ $isSynchronous = $returnType ? true : false;
+ } else {
+ $returnType = ($isSynchronous) ? $returnType : null;
+ }
return [
Config::TOPIC_NAME => $topicName,
- Config::TOPIC_IS_SYNCHRONOUS => $returnType ? true : false,
+ Config::TOPIC_IS_SYNCHRONOUS => $isSynchronous,
Config::TOPIC_REQUEST => $methodMetadata[Config::SCHEMA_METHOD_PARAMS],
Config::TOPIC_REQUEST_TYPE => Config::TOPIC_REQUEST_TYPE_METHOD,
Config::TOPIC_RESPONSE => $returnType,
@@ -85,7 +99,8 @@ public function generateTopicConfigForServiceMethod($topicName, $serviceType, $s
* Generate topic name based on service type and method name.
*
* Perform the following conversion:
- * \Magento\Customer\Api\RepositoryInterface + getById => magento.customer.api.repositoryInterface.getById
+ * \Magento\Customer\Api\RepositoryInterface + getById =>
+ * magento.customer.api.repositoryInterface.getById
*
* @param string $typeName
* @param string $methodName
diff --git a/lib/internal/Magento/Framework/Communication/etc/communication.xsd b/lib/internal/Magento/Framework/Communication/etc/communication.xsd
index 12ee56371ce77..678d89f30c531 100644
--- a/lib/internal/Magento/Framework/Communication/etc/communication.xsd
+++ b/lib/internal/Magento/Framework/Communication/etc/communication.xsd
@@ -40,6 +40,7 @@
+
diff --git a/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php b/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php
index 5c9bc9c2fb2d7..f654fd263f605 100644
--- a/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php
+++ b/lib/internal/Magento/Framework/DB/Adapter/AdapterInterface.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\DB\Adapter;
use Magento\Framework\DB\Ddl\Table;
@@ -365,6 +366,7 @@ public function getIndexList($tableName, $schemaName = null);
/**
* Add new Foreign Key to table
+ *
* If Foreign Key with same name is exist - it will be deleted
*
* @param string $fkName
@@ -373,7 +375,6 @@ public function getIndexList($tableName, $schemaName = null);
* @param string $refTableName
* @param string $refColumnName
* @param string $onDelete
- * @param string $onUpdate
* @param boolean $purge trying remove invalid data
* @param string $schemaName
* @param string $refSchemaName
@@ -484,6 +485,7 @@ public function insert($table, array $bind);
/**
* Inserts a table row with specified data
+ *
* Special for Zero values to identity column
*
* @param string $table
@@ -502,9 +504,9 @@ public function insertForce($table, array $bind);
* If the $where parameter is an array of multiple clauses, they will be joined by AND, with each clause wrapped in
* parenthesis. If you wish to use an OR, you must give a single clause that is an instance of {@see Zend_Db_Expr}
*
- * @param mixed $table The table to update.
- * @param array $bind Column-value pairs.
- * @param mixed $where UPDATE WHERE clause(s).
+ * @param mixed $table The table to update.
+ * @param array $bind Column-value pairs.
+ * @param mixed $where UPDATE WHERE clause(s).
* @return int The number of affected rows.
*/
public function update($table, array $bind, $where = '');
@@ -512,8 +514,8 @@ public function update($table, array $bind, $where = '');
/**
* Deletes table rows based on a WHERE clause.
*
- * @param mixed $table The table to update.
- * @param mixed $where DELETE WHERE clause(s).
+ * @param mixed $table The table to update.
+ * @param mixed $where DELETE WHERE clause(s).
* @return int The number of affected rows.
*/
public function delete($table, $where = '');
@@ -521,31 +523,33 @@ public function delete($table, $where = '');
/**
* Prepares and executes an SQL statement with bound data.
*
- * @param mixed $sql The SQL statement with placeholders.
+ * @param mixed $sql The SQL statement with placeholders.
* May be a string or \Magento\Framework\DB\Select.
- * @param mixed $bind An array of data or data itself to bind to the placeholders.
+ * @param mixed $bind An array of data or data itself to bind to the placeholders.
* @return \Zend_Db_Statement_Interface
*/
public function query($sql, $bind = []);
/**
* Fetches all SQL result rows as a sequential array.
+ *
* Uses the current fetchMode for the adapter.
*
- * @param string|\Magento\Framework\DB\Select $sql An SQL SELECT statement.
- * @param mixed $bind Data to bind into SELECT placeholders.
- * @param mixed $fetchMode Override current fetch mode.
+ * @param string|\Magento\Framework\DB\Select $sql An SQL SELECT statement.
+ * @param mixed $bind Data to bind into SELECT placeholders.
+ * @param mixed $fetchMode Override current fetch mode.
* @return array
*/
public function fetchAll($sql, $bind = [], $fetchMode = null);
/**
* Fetches the first row of the SQL result.
+ *
* Uses the current fetchMode for the adapter.
*
* @param string|\Magento\Framework\DB\Select $sql An SQL SELECT statement.
* @param mixed $bind Data to bind into SELECT placeholders.
- * @param mixed $fetchMode Override current fetch mode.
+ * @param mixed $fetchMode Override current fetch mode.
* @return array
*/
public function fetchRow($sql, $bind = [], $fetchMode = null);
@@ -622,9 +626,9 @@ public function quote($value, $type = null);
* // $safe = "WHERE date < '2005-01-02'"
*
*
- * @param string $text The text with a placeholder.
- * @param mixed $value The value to quote.
- * @param string $type OPTIONAL SQL datatype
+ * @param string $text The text with a placeholder.
+ * @param mixed $value The value to quote.
+ * @param string $type OPTIONAL SQL datatype
* @param integer $count OPTIONAL count of placeholders to replace
* @return string An SQL-safe quoted value placed into the original text.
*/
@@ -633,7 +637,7 @@ public function quoteInto($text, $value, $type = null, $count = null);
/**
* Quotes an identifier.
*
- * Accepts a string representing a qualified indentifier. For Example:
+ * Accepts a string representing a qualified identifier. For Example:
*
* $adapter->quoteIdentifier('myschema.mytable')
*
@@ -721,7 +725,8 @@ public function disallowDdlCache();
/**
* Reset cached DDL data from cache
- * if table name is null - reset all cached DDL data
+ *
+ * If table name is null - reset all cached DDL data
*
* @param string $tableName
* @param string $schemaName OPTIONAL
@@ -741,6 +746,7 @@ public function saveDdlCache($tableCacheKey, $ddlType, $data);
/**
* Load DDL data from cache
+ *
* Return false if cache does not exists
*
* @param string $tableCacheKey the table cache key
@@ -784,6 +790,7 @@ public function prepareSqlCondition($fieldName, $condition);
/**
* Prepare value for save in column
+ *
* Return converted to column data type value
*
* @param array $column the column describe array
@@ -813,6 +820,7 @@ public function getIfNullSql($expression, $value = 0);
/**
* Generate fragment of SQL, that combine together (concatenate) the results from data array
+ *
* All arguments in data must be quoted
*
* @param array $data
@@ -823,6 +831,7 @@ public function getConcatSql(array $data, $separator = null);
/**
* Generate fragment of SQL that returns length of character string
+ *
* The string argument must be quoted
*
* @param string $string
@@ -931,6 +940,7 @@ public function getDateExtractSql($date, $unit);
/**
* Retrieve valid table name
+ *
* Check table name length and allowed symbols
*
* @param string $tableName
@@ -950,6 +960,7 @@ public function getTriggerName($tableName, $time, $event);
/**
* Retrieve valid index name
+ *
* Check index name length and allowed symbols
*
* @param string $tableName
@@ -961,6 +972,7 @@ public function getIndexName($tableName, $fields, $indexType = '');
/**
* Retrieve valid foreign key name
+ *
* Check foreign key name length and allowed symbols
*
* @param string $priTableName
@@ -1047,6 +1059,7 @@ public function supportStraightJoin();
/**
* Adds order by random to select object
+ *
* Possible using integer field for optimization
*
* @param \Magento\Framework\DB\Select $select
@@ -1074,6 +1087,7 @@ public function getPrimaryKeyName($tableName, $schemaName = null);
/**
* Converts fetched blob into raw binary PHP data.
+ *
* Some DB drivers return blobs as hex-coded strings, so we need to process them.
*
* @param mixed $value
@@ -1114,6 +1128,8 @@ public function dropTrigger($triggerName, $schemaName = null);
public function getTables($likeCondition = null);
/**
+ * Generates case SQL fragment
+ *
* Generate fragment of SQL, that check value against multiple condition cases
* and return different result depends on them
*
diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/ReadInterface.php b/lib/internal/Magento/Framework/Filesystem/Directory/ReadInterface.php
index 61108c64dda44..85d41b6932629 100644
--- a/lib/internal/Magento/Framework/Filesystem/Directory/ReadInterface.php
+++ b/lib/internal/Magento/Framework/Filesystem/Directory/ReadInterface.php
@@ -89,6 +89,7 @@ public function isDirectory($path = null);
*
* @param string $path
* @return \Magento\Framework\Filesystem\File\ReadInterface
+ * @throws \Magento\Framework\Exception\FileSystemException
*/
public function openFile($path);
diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php
index 3e5f9bcf0bd27..a56a4a3edf1fe 100644
--- a/lib/internal/Magento/Framework/Filter/Template.php
+++ b/lib/internal/Magento/Framework/Filter/Template.php
@@ -293,7 +293,7 @@ public function templateDirective($construction)
{
// Processing of {template config_path=... [...]} statement
$templateParameters = $this->getParameters($construction[2]);
- if (!isset($templateParameters['config_path']) or !$this->getTemplateProcessor()) {
+ if (!isset($templateParameters['config_path']) || !$this->getTemplateProcessor()) {
// Not specified template or not set include processor
$replacedValue = '{Error in template processing}';
} else {
diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php
index 75b6c64e9d24f..ec22b742b1d6c 100644
--- a/lib/internal/Magento/Framework/GraphQl/Config.php
+++ b/lib/internal/Magento/Framework/GraphQl/Config.php
@@ -48,12 +48,7 @@ public function __construct(
}
/**
- * Get a data object with data pertaining to a GraphQL type's structural makeup.
- *
- * @param string $configElementName
- * @return ConfigElementInterface
- * @throws \LogicException
- * @SuppressWarnings(PHPMD.UnusedLocalVariable)
+ * @inheritdoc
*/
public function getConfigElement(string $configElementName) : ConfigElementInterface
{
@@ -67,7 +62,7 @@ public function getConfigElement(string $configElementName) : ConfigElementInter
$fieldsInQuery = $this->queryFields->getFieldsUsedInQuery();
if (isset($data['fields'])) {
if (!empty($fieldsInQuery)) {
- foreach ($data['fields'] as $fieldName => $fieldConfig) {
+ foreach (array_keys($data['fields']) as $fieldName) {
if (!isset($fieldsInQuery[$fieldName])) {
unset($data['fields'][$fieldName]);
}
@@ -81,18 +76,20 @@ public function getConfigElement(string $configElementName) : ConfigElementInter
}
/**
- * Return all type names declared in a GraphQL schema's configuration.
- *
- * @return string[]
+ * @inheritdoc
*/
- public function getDeclaredTypeNames() : array
+ public function getDeclaredTypes() : array
{
$types = [];
foreach ($this->configData->get(null) as $item) {
- if (isset($item['type']) && $item['type'] == 'graphql_type') {
- $types[] = $item['name'];
+ if (isset($item['type'])) {
+ $types[] = [
+ 'name' => $item['name'],
+ 'type' => $item['type'],
+ ];
}
}
+
return $types;
}
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php
index b1210e986b772..994ae489af128 100644
--- a/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php
+++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php
@@ -37,7 +37,7 @@ class Enum implements ConfigElementInterface
public function __construct(
string $name,
array $values,
- string $description = ""
+ string $description
) {
$this->name = $name;
$this->values = $values;
diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/FieldsFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/FieldsFactory.php
new file mode 100644
index 0000000000000..ca6b67eac3d83
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/FieldsFactory.php
@@ -0,0 +1,62 @@
+argumentFactory = $argumentFactory;
+ $this->fieldFactory = $fieldFactory;
+ }
+
+ /**
+ * Create a fields object from a configured array with optional arguments.
+ *
+ * Field data must contain name and type. Other values are optional and include required, itemType, description,
+ * and resolver. Arguments array must be in the format of [$argumentData['name'] => $argumentData].
+ *
+ * @param array $fieldsData
+ * @return Field[]
+ */
+ public function createFromConfigData(
+ array $fieldsData
+ ) : array {
+ $fields = [];
+ foreach ($fieldsData as $fieldData) {
+ $arguments = [];
+ foreach ($fieldData['arguments'] as $argumentData) {
+ $arguments[$argumentData['name']] = $this->argumentFactory->createFromConfigData($argumentData);
+ }
+ $fields[$fieldData['name']] = $this->fieldFactory->createFromConfigData(
+ $fieldData,
+ $arguments
+ );
+ }
+ return $fields;
+ }
+}
diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php
new file mode 100644
index 0000000000000..8e86f701672c6
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php
@@ -0,0 +1,74 @@
+name = $name;
+ $this->fields = $fields;
+ $this->description = $description;
+ }
+
+ /**
+ * Get the type name.
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * Get a list of fields that make up the possible return or input values of a type.
+ *
+ * @return Field[]
+ */
+ public function getFields(): array
+ {
+ return $this->fields;
+ }
+
+ /**
+ * Get a human-readable description of the type.
+ *
+ * @return string
+ */
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+}
diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php
new file mode 100644
index 0000000000000..0e7ccb831a5a4
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php
@@ -0,0 +1,79 @@
+objectManager = $objectManager;
+ $this->fieldsFactory = $fieldsFactory;
+ }
+
+ /**
+ * Instantiate an object representing 'input' GraphQL config element.
+ *
+ * @param array $data
+ * @return ConfigElementInterface
+ */
+ public function createFromConfigData(array $data): ConfigElementInterface
+ {
+ $fields = isset($data['fields']) ? $this->fieldsFactory->createFromConfigData($data['fields']) : [];
+
+ return $this->create(
+ $data,
+ $fields
+ );
+ }
+
+ /**
+ * Create input type object based off array of configured GraphQL InputType data.
+ *
+ * Type data must contain name and the type's fields. Optional data includes description.
+ *
+ * @param array $typeData
+ * @param array $fields
+ * @return Input
+ */
+ private function create(
+ array $typeData,
+ array $fields
+ ): Input {
+ return $this->objectManager->create(
+ Input::class,
+ [
+ 'name' => $typeData['name'],
+ 'fields' => $fields,
+ 'description' => isset($typeData['description']) ? $typeData['description'] : ''
+ ]
+ );
+ }
+}
diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php
index 320199c14a6d6..73ebd42acfb27 100644
--- a/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php
+++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php
@@ -8,7 +8,7 @@
namespace Magento\Framework\GraphQl\Config\Element;
/**
- * Describes the configured data for a GraphQL interface type.
+ * Class representing 'interface' GraphQL config element.
*/
class InterfaceType implements TypeInterface
{
@@ -42,7 +42,7 @@ public function __construct(
string $name,
string $typeResolver,
array $fields,
- string $description = ""
+ string $description
) {
$this->name = $name;
$this->fields = $fields;
diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php
index 24ff439db0347..20d017cc71062 100644
--- a/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php
+++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php
@@ -8,7 +8,7 @@
namespace Magento\Framework\GraphQl\Config\Element;
/**
- * Describes all the configured data of an Output or Input type in GraphQL.
+ * Class representing 'type' GraphQL config element.
*/
class Type implements TypeInterface
{
diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php
index c5f3187b04841..5dd477a050890 100644
--- a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php
+++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php
@@ -22,28 +22,20 @@ class TypeFactory implements ConfigElementFactoryInterface
private $objectManager;
/**
- * @var ArgumentFactory
+ * @var FieldsFactory
*/
- private $argumentFactory;
-
- /**
- * @var FieldFactory
- */
- private $fieldFactory;
+ private $fieldsFactory;
/**
* @param ObjectManagerInterface $objectManager
- * @param ArgumentFactory $argumentFactory
- * @param FieldFactory $fieldFactory
+ * @param FieldsFactory $fieldsFactory
*/
public function __construct(
ObjectManagerInterface $objectManager,
- ArgumentFactory $argumentFactory,
- FieldFactory $fieldFactory
+ FieldsFactory $fieldsFactory
) {
$this->objectManager = $objectManager;
- $this->argumentFactory = $argumentFactory;
- $this->fieldFactory = $fieldFactory;
+ $this->fieldsFactory = $fieldsFactory;
}
/**
@@ -54,18 +46,8 @@ public function __construct(
*/
public function createFromConfigData(array $data): ConfigElementInterface
{
- $fields = [];
- $data['fields'] = isset($data['fields']) ? $data['fields'] : [];
- foreach ($data['fields'] as $field) {
- $arguments = [];
- foreach ($field['arguments'] as $argument) {
- $arguments[$argument['name']] = $this->argumentFactory->createFromConfigData($argument);
- }
- $fields[$field['name']] = $this->fieldFactory->createFromConfigData(
- $field,
- $arguments
- );
- }
+ $fields = isset($data['fields']) ? $this->fieldsFactory->createFromConfigData($data['fields']) : [];
+
return $this->create(
$data,
$fields
@@ -73,10 +55,10 @@ public function createFromConfigData(array $data): ConfigElementInterface
}
/**
- * Create type object based off array of configured GraphQL Output/InputType data.
+ * Create type object based off array of configured GraphQL Type data.
*
* Type data must contain name and the type's fields. Optional data includes 'implements' (i.e. the interfaces
- * implemented by the types), and description. An InputType cannot implement an interface.
+ * implemented by the types), and description.
*
* @param array $typeData
* @param array $fields
diff --git a/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php b/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php
index c2670967f1db5..f7d6cf49e180c 100644
--- a/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php
+++ b/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php
@@ -25,9 +25,11 @@ interface ConfigInterface
public function getConfigElement(string $configElementName) : ConfigElementInterface;
/**
- * Return all type names from a GraphQL schema's configuration.
+ * Return all type names declared in a GraphQL schema's configuration and their type.
*
- * @return string[]
+ * Format is ['name' => 'example value', 'type' = 'example value']
+ *
+ * @return array $types
*/
- public function getDeclaredTypeNames() : array;
+ public function getDeclaredTypes() : array;
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php
index d0bc9591265eb..a34c0a9d42187 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php
@@ -24,9 +24,11 @@ class Fields
* Set Query for extracting list of fields.
*
* @param string $query
+ * @param array|null $variables
+ *
* @return void
*/
- public function setQuery($query)
+ public function setQuery($query, array $variables = null)
{
$queryFields = [];
try {
@@ -41,6 +43,9 @@ public function setQuery($query)
]
]
);
+ if (isset($variables)) {
+ $queryFields = array_merge($queryFields, $this->extractVariables($variables));
+ }
} catch (\Exception $e) {
// If a syntax error is encountered do not collect fields
}
@@ -62,4 +67,24 @@ public function getFieldsUsedInQuery()
{
return $this->fieldsUsedInQuery;
}
+
+ /**
+ * Extract and return list of all used fields in GraphQL query's variables
+ *
+ * @param array $variables
+ *
+ * @return string[]
+ */
+ private function extractVariables(array $variables): array
+ {
+ $fields = [];
+ foreach ($variables as $key => $value) {
+ if (is_array($value)) {
+ $fields = array_merge($fields, $this->extractVariables($value));
+ }
+ $fields[$key] = $key;
+ }
+
+ return $fields;
+ }
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/IntrospectionConfiguration.php b/lib/internal/Magento/Framework/GraphQl/Query/IntrospectionConfiguration.php
new file mode 100644
index 0000000000000..2fdb3df5f6d71
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Query/IntrospectionConfiguration.php
@@ -0,0 +1,42 @@
+deploymentConfig = $deploymentConfig;
+ }
+
+ /**
+ * Check the the environment config to determine if introspection should be disabled.
+ *
+ * @return bool
+ */
+ public function isIntrospectionDisabled(): bool
+ {
+ return (bool)$this->deploymentConfig->get(self::CONFIG_PATH_DISABLE_INTROSPECTION);
+ }
+}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php
index 5730156ca5b34..2b9ce9b01b5c4 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php
@@ -33,16 +33,24 @@ class QueryComplexityLimiter
*/
private $queryComplexity;
+ /**
+ * @var IntrospectionConfiguration
+ */
+ private $introspectionConfig;
+
/**
* @param int $queryDepth
* @param int $queryComplexity
+ * @param IntrospectionConfiguration $introspectionConfig
*/
public function __construct(
int $queryDepth,
- int $queryComplexity
+ int $queryComplexity,
+ IntrospectionConfiguration $introspectionConfig
) {
$this->queryDepth = $queryDepth;
$this->queryComplexity = $queryComplexity;
+ $this->introspectionConfig = $introspectionConfig;
}
/**
@@ -53,7 +61,9 @@ public function __construct(
public function execute(): void
{
DocumentValidator::addRule(new QueryComplexity($this->queryComplexity));
- DocumentValidator::addRule(new DisableIntrospection());
+ DocumentValidator::addRule(
+ new DisableIntrospection((int) $this->introspectionConfig->isIntrospectionDisabled())
+ );
DocumentValidator::addRule(new QueryDepth($this->queryDepth));
}
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php
index e7d14a81b9dee..bd9de206ccda1 100644
--- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php
+++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php
@@ -38,7 +38,7 @@ public function getEntityAttributesForEntityFromField(string $fieldName) : array
if (isset($this->attributesInstances[$fieldName])) {
return $this->attributesInstances[$fieldName]->getEntityAttributes();
} else {
- throw new \LogicException(sprintf('There is no attrribute class assigned to field %1', $fieldName));
+ throw new \LogicException(sprintf('There is no attribute class assigned to field %1', $fieldName));
}
}
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php
index 63fef73186b12..250b80defa6dd 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php
@@ -8,9 +8,8 @@
namespace Magento\Framework\GraphQl\Schema;
use Magento\Framework\GraphQl\ConfigInterface;
-use Magento\Framework\GraphQl\Schema\SchemaGeneratorInterface;
-use Magento\Framework\GraphQl\Schema\Type\Output\OutputMapper;
use Magento\Framework\GraphQl\Schema;
+use Magento\Framework\GraphQl\Schema\Type\TypeRegistry;
use Magento\Framework\GraphQl\SchemaFactory;
/**
@@ -24,47 +23,46 @@ class SchemaGenerator implements SchemaGeneratorInterface
private $schemaFactory;
/**
- * @var OutputMapper
+ * @var ConfigInterface
*/
- private $outputMapper;
+ private $config;
/**
- * @var ConfigInterface
+ * @var TypeRegistry
*/
- private $config;
+ private $typeRegistry;
/**
* @param SchemaFactory $schemaFactory
- * @param OutputMapper $outputMapper
* @param ConfigInterface $config
+ * @param TypeRegistry $typeRegistry
*/
public function __construct(
SchemaFactory $schemaFactory,
- OutputMapper $outputMapper,
- ConfigInterface $config
+ ConfigInterface $config,
+ TypeRegistry $typeRegistry
) {
$this->schemaFactory = $schemaFactory;
- $this->outputMapper = $outputMapper;
$this->config = $config;
+ $this->typeRegistry = $typeRegistry;
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function generate() : Schema
{
$schema = $this->schemaFactory->create(
[
- 'query' => $this->outputMapper->getOutputType('Query'),
- 'mutation' => $this->outputMapper->getOutputType('Mutation'),
+ 'query' => $this->typeRegistry->get('Query'),
+ 'mutation' => $this->typeRegistry->get('Mutation'),
'typeLoader' => function ($name) {
- return $this->outputMapper->getOutputType($name);
+ return $this->typeRegistry->get($name);
},
'types' => function () {
- //all types should be generated only on introspection
$typesImplementors = [];
- foreach ($this->config->getDeclaredTypeNames() as $name) {
- $typesImplementors [] = $this->outputMapper->getOutputType($name);
+ foreach ($this->config->getDeclaredTypes() as $type) {
+ $typesImplementors [] = $this->typeRegistry->get($type['name']);
}
return $typesImplementors;
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputFactory.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputFactory.php
deleted file mode 100644
index cbbd97cfdb8c7..0000000000000
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputFactory.php
+++ /dev/null
@@ -1,60 +0,0 @@
-objectManager = $objectManager;
- $this->prototypes = $prototypes;
- }
-
- /**
- * @param ConfigElementInterface $configElement
- * @return InputTypeInterface
- */
- public function create(ConfigElementInterface $configElement) : InputTypeInterface
- {
- if (!isset($this->typeRegistry[$configElement->getName()])) {
- $this->typeRegistry[$configElement->getName()] =
- $this->objectManager->create(
- $this->prototypes[get_class($configElement)],
- [
- 'configElement' => $configElement
- ]
- );
- }
- return $this->typeRegistry[$configElement->getName()];
- }
-}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php
index d806c0b3e68ab..d1f48dada2cbd 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php
@@ -9,27 +9,15 @@
use Magento\Framework\GraphQl\Config\Data\WrappedTypeProcessor;
use Magento\Framework\GraphQl\Config\Element\Argument;
-use Magento\Framework\GraphQl\ConfigInterface;
+use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Schema\Type\ScalarTypes;
-use Magento\Framework\GraphQl\Schema\TypeFactory;
+use Magento\Framework\GraphQl\Schema\Type\TypeRegistry;
+/**
+ * Prepare argument's metadata for GraphQL schema generation
+ */
class InputMapper
{
- /**
- * @var InputFactory
- */
- private $inputFactory;
-
- /**
- * @var ConfigInterface
- */
- private $config;
-
- /**
- * @var TypeFactory
- */
- private $typeFactory;
-
/**
* @var ScalarTypes
*/
@@ -41,24 +29,23 @@ class InputMapper
private $wrappedTypeProcessor;
/**
- * @param InputFactory $inputFactory
- * @param ConfigInterface $config
- * @param TypeFactory $typeFactory
+ * @var TypeRegistry
+ */
+ private $typeRegistry;
+
+ /**
* @param ScalarTypes $scalarTypes
* @param WrappedTypeProcessor $wrappedTypeProcessor
+ * @param TypeRegistry $typeRegistry
*/
public function __construct(
- InputFactory $inputFactory,
- ConfigInterface $config,
- TypeFactory $typeFactory,
ScalarTypes $scalarTypes,
- WrappedTypeProcessor $wrappedTypeProcessor
+ WrappedTypeProcessor $wrappedTypeProcessor,
+ TypeRegistry $typeRegistry
) {
- $this->inputFactory = $inputFactory;
- $this->config = $config;
- $this->typeFactory = $typeFactory;
$this->scalarTypes = $scalarTypes;
$this->wrappedTypeProcessor = $wrappedTypeProcessor;
+ $this->typeRegistry = $typeRegistry;
}
/**
@@ -66,6 +53,7 @@ public function __construct(
*
* @param Argument $argument
* @return array
+ * @throws GraphQlInputException
*/
public function getRepresentation(Argument $argument) : array
{
@@ -73,8 +61,7 @@ public function getRepresentation(Argument $argument) : array
if ($this->scalarTypes->isScalarType($typeName)) {
$instance = $this->wrappedTypeProcessor->processScalarWrappedType($argument);
} else {
- $configElement = $this->config->getConfigElement($typeName);
- $instance = $this->inputFactory->create($configElement);
+ $instance = $this->typeRegistry->get($typeName);
$instance = $this->wrappedTypeProcessor->processWrappedType($argument, $instance);
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php
index ae2d07ade2ad0..fa0327f79bc66 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php
@@ -8,21 +8,16 @@
namespace Magento\Framework\GraphQl\Schema\Type\Input;
use Magento\Framework\GraphQl\Config\Data\WrappedTypeProcessor;
-use Magento\Framework\GraphQl\Config\Element\Type as TypeConfigElement;
-use Magento\Framework\GraphQl\ConfigInterface;
+use Magento\Framework\GraphQl\Config\Element\Input as InputConfigElement;
+use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Schema\Type\ScalarTypes;
-use Magento\Framework\GraphQl\Schema\TypeFactory;
+use Magento\Framework\GraphQl\Schema\Type\TypeRegistry;
/**
* Class InputObjectType
*/
class InputObjectType extends \Magento\Framework\GraphQl\Schema\Type\InputObjectType
{
- /**
- * @var TypeFactory
- */
- private $typeFactory;
-
/**
* @var ScalarTypes
*/
@@ -34,36 +29,27 @@ class InputObjectType extends \Magento\Framework\GraphQl\Schema\Type\InputObject
private $wrappedTypeProcessor;
/**
- * @var InputFactory
+ * @var TypeRegistry
*/
- private $inputFactory;
+ private $typeRegistry;
/**
- * @var ConfigInterface
- */
- public $graphQlConfig;
-
- /**
- * @param TypeConfigElement $configElement
- * @param TypeFactory $typeFactory
+ * @param InputConfigElement $configElement
* @param ScalarTypes $scalarTypes
* @param WrappedTypeProcessor $wrappedTypeProcessor
- * @param InputFactory $inputFactory
- * @param ConfigInterface $graphQlConfig
+ * @param TypeRegistry $typeRegistry
+ * @throws GraphQlInputException
*/
public function __construct(
- TypeConfigElement $configElement,
- TypeFactory $typeFactory,
+ InputConfigElement $configElement,
ScalarTypes $scalarTypes,
WrappedTypeProcessor $wrappedTypeProcessor,
- InputFactory $inputFactory,
- ConfigInterface $graphQlConfig
+ TypeRegistry $typeRegistry
) {
- $this->typeFactory = $typeFactory;
$this->scalarTypes = $scalarTypes;
$this->wrappedTypeProcessor = $wrappedTypeProcessor;
- $this->inputFactory = $inputFactory;
- $this->graphQlConfig = $graphQlConfig;
+ $this->typeRegistry = $typeRegistry;
+
$config = [
'name' => $configElement->getName(),
'description' => $configElement->getDescription()
@@ -75,8 +61,7 @@ public function __construct(
if ($field->getTypeName() == $configElement->getName()) {
$type = $this;
} else {
- $fieldConfigElement = $this->graphQlConfig->getConfigElement($field->getTypeName());
- $type = $this->inputFactory->create($fieldConfigElement);
+ $type = $this->typeRegistry->get($field->getTypeName());
}
$type = $this->wrappedTypeProcessor->processWrappedType($field, $type);
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php
index b54cd4d8ca218..034a5702090d9 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php
@@ -16,7 +16,6 @@
use Magento\Framework\GraphQl\Schema\Type\Output\OutputMapper;
use Magento\Framework\GraphQl\Schema\Type\OutputTypeInterface;
use Magento\Framework\GraphQl\Schema\Type\ScalarTypes;
-use Magento\Framework\GraphQl\Schema\TypeFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfoFactory;
@@ -40,11 +39,6 @@ class Fields implements FormatterInterface
*/
private $inputMapper;
- /**
- * @var TypeFactory
- */
- private $typeFactory;
-
/**
* @var ScalarTypes
*/
@@ -64,7 +58,6 @@ class Fields implements FormatterInterface
* @param ObjectManagerInterface $objectManager
* @param OutputMapper $outputMapper
* @param InputMapper $inputMapper
- * @param TypeFactory $typeFactory
* @param ScalarTypes $scalarTypes
* @param WrappedTypeProcessor $wrappedTypeProcessor
* @param ResolveInfoFactory $resolveInfoFactory
@@ -73,7 +66,6 @@ public function __construct(
ObjectManagerInterface $objectManager,
OutputMapper $outputMapper,
InputMapper $inputMapper,
- TypeFactory $typeFactory,
ScalarTypes $scalarTypes,
WrappedTypeProcessor $wrappedTypeProcessor,
ResolveInfoFactory $resolveInfoFactory
@@ -81,14 +73,13 @@ public function __construct(
$this->objectManager = $objectManager;
$this->outputMapper = $outputMapper;
$this->inputMapper = $inputMapper;
- $this->typeFactory = $typeFactory;
$this->scalarTypes = $scalarTypes;
$this->wrappedTypeProcessor = $wrappedTypeProcessor;
$this->resolveInfoFactory = $resolveInfoFactory;
}
/**
- * {@inheritDoc}
+ * @inheritdoc
*/
public function format(TypeInterface $configElement, OutputTypeInterface $outputType): array
{
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputFactory.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputFactory.php
deleted file mode 100644
index 81dad11774b01..0000000000000
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputFactory.php
+++ /dev/null
@@ -1,65 +0,0 @@
-objectManager = $objectManager;
- $this->prototypes = $prototypes;
- }
-
- /**
- * Create output type.
- *
- * @param ConfigElementInterface $configElement
- * @return OutputTypeInterface
- */
- public function create(ConfigElementInterface $configElement) : OutputTypeInterface
- {
- if (!isset($this->typeRegistry[$configElement->getName()])) {
- $this->typeRegistry[$configElement->getName()] =
- $this->objectManager->create(
- $this->prototypes[get_class($configElement)],
- [
- 'configElement' => $configElement
- ]
- );
- }
- return $this->typeRegistry[$configElement->getName()];
- }
-}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php
index b7f4b8a1f60db..046eeb5b1f93d 100644
--- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php
@@ -7,50 +7,28 @@
namespace Magento\Framework\GraphQl\Schema\Type\Output;
-use Magento\Framework\GraphQl\ConfigInterface;
use Magento\Framework\GraphQl\Schema\Type\OutputTypeInterface;
-use Magento\Framework\GraphQl\Schema\TypeFactory;
+use Magento\Framework\GraphQl\Schema\Type\TypeRegistry;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\Phrase;
/**
- * Map type names to their output type/interface classes.
+ * Map type names to their output type/interface/enum classes.
*/
class OutputMapper
{
/**
- * @var OutputFactory
+ * @var TypeRegistry
*/
- private $outputFactory;
+ private $typeRegistry;
/**
- * @var OutputTypeInterface[]
- */
- private $outputTypes;
-
- /**
- * @var TypeFactory
- */
- private $typeFactory;
-
- /**
- * @var ConfigInterface
- */
- private $config;
-
- /**
- * @param OutputFactory $outputFactory
- * @param TypeFactory $typeFactory
- * @param ConfigInterface $config
+ * @param TypeRegistry $typeRegistry
*/
public function __construct(
- OutputFactory $outputFactory,
- TypeFactory $typeFactory,
- ConfigInterface $config
+ TypeRegistry $typeRegistry
) {
- $this->outputFactory = $outputFactory;
- $this->config = $config;
- $this->typeFactory = $typeFactory;
+ $this->typeRegistry = $typeRegistry;
}
/**
@@ -62,16 +40,13 @@ public function __construct(
*/
public function getOutputType($typeName)
{
- if (!isset($this->outputTypes[$typeName])) {
- $configElement = $this->config->getConfigElement($typeName);
- $this->outputTypes[$typeName] = $this->outputFactory->create($configElement);
- if (!($this->outputTypes[$typeName] instanceof OutputTypeInterface)) {
- throw new GraphQlInputException(
- new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.")
- );
- }
- }
+ $outputType = $this->typeRegistry->get($typeName);
- return $this->outputTypes[$typeName];
+ if (!$outputType instanceof OutputTypeInterface) {
+ throw new GraphQlInputException(
+ new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.")
+ );
+ }
+ return $outputType;
}
}
diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php
new file mode 100644
index 0000000000000..cde8b6b3e446b
--- /dev/null
+++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php
@@ -0,0 +1,95 @@
+objectManager = $objectManager;
+ $this->config = $config;
+ $this->configToTypeMap = $configToTypeMap;
+ }
+
+ /**
+ * Get GraphQL type object by type name
+ *
+ * @param string $typeName
+ * @return TypeInterface|InputTypeInterface|OutputTypeInterface
+ * @throws GraphQlInputException
+ */
+ public function get(string $typeName): TypeInterface
+ {
+ if (!isset($this->types[$typeName])) {
+ $configElement = $this->config->getConfigElement($typeName);
+
+ $configElementClass = get_class($configElement);
+ if (!isset($this->configToTypeMap[$configElementClass])) {
+ throw new GraphQlInputException(
+ new Phrase(
+ "No mapping to Webonyx type is declared for '%1' config element type.",
+ [$configElementClass]
+ )
+ );
+ }
+
+ $this->types[$typeName] = $this->objectManager->create(
+ $this->configToTypeMap[$configElementClass],
+ [
+ 'configElement' => $configElement,
+ ]
+ );
+
+ if (!($this->types[$typeName] instanceof TypeInterface)) {
+ throw new GraphQlInputException(
+ new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.")
+ );
+ }
+ }
+ return $this->types[$typeName];
+ }
+}
diff --git a/lib/internal/Magento/Framework/Message/Manager.php b/lib/internal/Magento/Framework/Message/Manager.php
index c3b5701057d73..4ef1754b7e586 100644
--- a/lib/internal/Magento/Framework/Message/Manager.php
+++ b/lib/internal/Magento/Framework/Message/Manager.php
@@ -11,6 +11,8 @@
/**
* Message manager model
+ *
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Manager implements ManagerInterface
@@ -226,7 +228,7 @@ public function addUniqueMessages(array $messages, $group = null)
$items = $this->getMessages(false, $group)->getItems();
foreach ($messages as $message) {
- if ($message instanceof MessageInterface and !in_array($message, $items, false)) {
+ if ($message instanceof MessageInterface && !in_array($message, $items, false)) {
$this->addMessage($message, $group);
}
}
diff --git a/lib/internal/Magento/Framework/MessageQueue/MessageProcessor.php b/lib/internal/Magento/Framework/MessageQueue/MessageProcessor.php
index d71a527c9cbb1..ad0201cf56e77 100644
--- a/lib/internal/Magento/Framework/MessageQueue/MessageProcessor.php
+++ b/lib/internal/Magento/Framework/MessageQueue/MessageProcessor.php
@@ -12,6 +12,11 @@
*/
class MessageProcessor implements MessageProcessorInterface
{
+ /**
+ * Maximum number of transaction retries
+ */
+ const MAX_TRANSACTION_RETRIES = 10;
+
/**
* @var \Magento\Framework\MessageQueue\MessageStatusProcessor
*/
@@ -22,6 +27,11 @@ class MessageProcessor implements MessageProcessorInterface
*/
private $resource;
+ /**
+ * @var int
+ */
+ private $retryCount = 0;
+
/**
* @param MessageStatusProcessor $messageStatusProcessor
* @param ResourceConnection $resource
@@ -53,8 +63,19 @@ public function process(
} catch (ConnectionLostException $e) {
$this->resource->getConnection()->rollBack();
} catch (\Exception $e) {
+ $retry = false;
$this->resource->getConnection()->rollBack();
- $this->messageStatusProcessor->rejectMessages($queue, $messages);
+ if (strpos($e->getMessage(), 'Error while sending QUERY packet') !== false
+ && $this->retryCount < self::MAX_TRANSACTION_RETRIES
+ ) {
+ $retry = true;
+ $this->retryCount++;
+ $this->resource->closeConnection();
+ $this->process($queue, $configuration, $messages, $messagesToAcknowledge, $mergedMessages);
+ }
+ if (!$retry) {
+ $this->messageStatusProcessor->rejectMessages($queue, $messages);
+ }
}
}
diff --git a/lib/internal/Magento/Framework/Option/ArrayPool.php b/lib/internal/Magento/Framework/Option/ArrayPool.php
index 5ac349d99b82e..11e1b46ff0363 100644
--- a/lib/internal/Magento/Framework/Option/ArrayPool.php
+++ b/lib/internal/Magento/Framework/Option/ArrayPool.php
@@ -28,13 +28,14 @@ public function __construct(\Magento\Framework\ObjectManagerInterface $objectMan
*
* @param string $model
* @throws \InvalidArgumentException
- * @return \Magento\Framework\Option\ArrayInterface
+ * @return \Magento\Framework\Data\OptionSourceInterface
*/
public function get($model)
{
$modelInstance = $this->_objectManager->get($model);
- if (false == $modelInstance instanceof \Magento\Framework\Option\ArrayInterface) {
- throw new \InvalidArgumentException($model . 'doesn\'t implement \Magento\Framework\Option\ArrayInterface');
+ if (false == $modelInstance instanceof \Magento\Framework\Data\OptionSourceInterface) {
+ throw new \InvalidArgumentException($model
+ . 'doesn\'t implement \Magento\Framework\Data\OptionSourceInterface');
}
return $modelInstance;
}
diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php
index e352d0c2d7124..7ce9756ff243d 100644
--- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php
+++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php
@@ -16,27 +16,27 @@
class Json implements SerializerInterface
{
/**
- * {@inheritDoc}
+ * @inheritDoc
* @since 100.2.0
*/
public function serialize($data)
{
$result = json_encode($data);
if (false === $result) {
- throw new \InvalidArgumentException('Unable to serialize value.');
+ throw new \InvalidArgumentException("Unable to serialize value. Error: " . json_last_error_msg());
}
return $result;
}
/**
- * {@inheritDoc}
+ * @inheritDoc
* @since 100.2.0
*/
public function unserialize($string)
{
$result = json_decode($string, true);
if (json_last_error() !== JSON_ERROR_NONE) {
- throw new \InvalidArgumentException('Unable to unserialize value.');
+ throw new \InvalidArgumentException("Unable to unserialize value. Error: " . json_last_error_msg());
}
return $result;
}
diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php
index b53c83acb48cf..c7d201676b228 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
{
@@ -36,7 +37,7 @@ class SessionManager implements SessionManagerInterface
/**
* Validator
*
- * @var \Magento\Framework\Session\ValidatorInterface
+ * @var ValidatorInterface
*/
protected $validator;
@@ -50,28 +51,28 @@ class SessionManager implements SessionManagerInterface
/**
* SID resolver
*
- * @var \Magento\Framework\Session\SidResolverInterface
+ * @var SidResolverInterface
*/
protected $sidResolver;
/**
* Session config
*
- * @var \Magento\Framework\Session\Config\ConfigInterface
+ * @var Config\ConfigInterface
*/
protected $sessionConfig;
/**
* Save handler
*
- * @var \Magento\Framework\Session\SaveHandlerInterface
+ * @var SaveHandlerInterface
*/
protected $saveHandler;
/**
* Storage
*
- * @var \Magento\Framework\Session\StorageInterface
+ * @var StorageInterface
*/
protected $storage;
@@ -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,11 +134,15 @@ public function __construct(
$this->cookieManager = $cookieManager;
$this->cookieMetadataFactory = $cookieMetadataFactory;
$this->appState = $appState;
+ $this->sessionStartChecker = $sessionStartChecker ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
+ SessionStartChecker::class
+ );
$this->start();
}
/**
- * This method needs to support sessions with APC enabled
+ * This method needs to support sessions with APC enabled.
+ *
* @return void
*/
public function writeClose()
@@ -163,47 +177,49 @@ 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
- );
- }
+ 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]);
- }
+ // 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);
+ $this->validator->validate($this);
+ $this->renewCookie($sid);
- register_shutdown_function([$this, 'writeClose']);
+ register_shutdown_function([$this, 'writeClose']);
- $this->_addHost();
- \Magento\Framework\Profiler::stop('session_start');
+ $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/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php
index e472a9c9effb1..fbb84712b2afd 100644
--- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php
+++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php
@@ -5,7 +5,9 @@
*/
namespace Magento\Framework\View\Element\UiComponent;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\RequestInterface;
+use Magento\Framework\AuthorizationInterface;
use Magento\Framework\UrlInterface;
use Magento\Framework\View\Element\UiComponent\ContentType\ContentTypeFactory;
use Magento\Framework\View\Element\UiComponent\Control\ActionPoolFactory;
@@ -94,6 +96,11 @@ class Context implements ContextInterface
*/
protected $uiComponentFactory;
+ /**
+ * @var AuthorizationInterface
+ */
+ private $authorization;
+
/**
* @param PageLayoutInterface $pageLayout
* @param RequestInterface $request
@@ -104,7 +111,8 @@ class Context implements ContextInterface
* @param Processor $processor
* @param UiComponentFactory $uiComponentFactory
* @param DataProviderInterface|null $dataProvider
- * @param string|null $namespace
+ * @param string $namespace
+ * @param AuthorizationInterface|null $authorization
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -117,7 +125,8 @@ public function __construct(
Processor $processor,
UiComponentFactory $uiComponentFactory,
DataProviderInterface $dataProvider = null,
- $namespace = null
+ $namespace = null,
+ AuthorizationInterface $authorization = null
) {
$this->namespace = $namespace;
$this->request = $request;
@@ -129,6 +138,9 @@ public function __construct(
$this->urlBuilder = $urlBuilder;
$this->processor = $processor;
$this->uiComponentFactory = $uiComponentFactory;
+ $this->authorization = $authorization ?: ObjectManager::getInstance()->get(
+ AuthorizationInterface::class
+ );
$this->setAcceptType();
}
@@ -280,6 +292,9 @@ public function addButtons(array $buttons, UiComponentInterface $component)
uasort($buttons, [$this, 'sortButtons']);
foreach ($buttons as $buttonId => $buttonData) {
+ if (isset($buttonData['aclResource']) && !$this->authorization->isAllowed($buttonData['aclResource'])) {
+ continue;
+ }
if (isset($buttonData['url'])) {
$buttonData['url'] = $this->getUrl($buttonData['url']);
}
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/UiComponent/ContextTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/UiComponent/ContextTest.php
index b7301c4cad5d4..75c7fc248541c 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Element/UiComponent/ContextTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/UiComponent/ContextTest.php
@@ -11,7 +11,11 @@
use Magento\Framework\View\Element\UiComponent\Context;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Framework\View\Element\UiComponent\Control\ActionPoolInterface;
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class ContextTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -19,6 +23,16 @@ class ContextTest extends \PHPUnit\Framework\TestCase
*/
protected $context;
+ /**
+ * @var ActionPoolInterface
+ */
+ private $actionPool;
+
+ /**
+ * @var \Magento\Framework\AuthorizationInterface
+ */
+ private $authorization;
+
protected function setUp()
{
$pageLayout = $this->getMockBuilder(\Magento\Framework\View\LayoutInterface::class)->getMock();
@@ -33,6 +47,10 @@ protected function setUp()
$this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Control\ActionPoolFactory::class)
->disableOriginalConstructor()
->getMock();
+ $this->actionPool = $this->getMockBuilder(ActionPoolInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $actionPoolFactory->method('create')->willReturn($this->actionPool);
$contentTypeFactory =
$this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContentType\ContentTypeFactory::class)
->disableOriginalConstructor()
@@ -43,6 +61,9 @@ protected function setUp()
$this->getMockBuilder(\Magento\Framework\View\Element\UiComponentFactory::class)
->disableOriginalConstructor()
->getMock();
+ $this->authorization = $this->getMockBuilder(\Magento\Framework\AuthorizationInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$objectManagerHelper = new ObjectManagerHelper($this);
$this->context = $objectManagerHelper->getObject(
@@ -55,11 +76,62 @@ protected function setUp()
'contentTypeFactory' => $contentTypeFactory,
'urlBuilder' => $urlBuilder,
'processor' => $processor,
- 'uiComponentFactory' => $uiComponentFactory
+ 'uiComponentFactory' => $uiComponentFactory,
+ 'authorization' => $this->authorization,
]
);
}
+ public function testAddButtonWithoutAclResource()
+ {
+ $component = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->actionPool->expects($this->once())->method('add');
+ $this->authorization->expects($this->never())->method('isAllowed');
+
+ $this->context->addButtons([
+ 'button_1' => [
+ 'name' => 'button_1',
+ ],
+ ], $component);
+ }
+
+ public function testAddButtonWithAclResourceAllowed()
+ {
+ $component = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->actionPool->expects($this->once())->method('add');
+ $this->authorization->expects($this->once())->method('isAllowed')->willReturn(true);
+
+ $this->context->addButtons([
+ 'button_1' => [
+ 'name' => 'button_1',
+ 'aclResource' => 'Magento_Framwork::acl',
+ ],
+ ], $component);
+ }
+
+ public function testAddButtonWithAclResourceDenied()
+ {
+ $component = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->actionPool->expects($this->never())->method('add');
+ $this->authorization->expects($this->once())->method('isAllowed')->willReturn(false);
+
+ $this->context->addButtons([
+ 'button_1' => [
+ 'name' => 'button_1',
+ 'aclResource' => 'Magento_Framwork::acl',
+ ],
+ ], $component);
+ }
+
/**
* @dataProvider addComponentDefinitionDataProvider
* @param array $components
diff --git a/lib/web/css/source/components/_modals.less b/lib/web/css/source/components/_modals.less
index a8e8cebde3a6c..396930cce6d86 100644
--- a/lib/web/css/source/components/_modals.less
+++ b/lib/web/css/source/components/_modals.less
@@ -102,7 +102,7 @@
&.confirm {
.modal-inner-wrap {
- .lib-css(width, @modal-popup-confirm__width);
+ .lib-css(max-width, @modal-popup-confirm__width);
.modal-content {
padding-right: 7rem;
diff --git a/phpserver/README.md b/phpserver/README.md
index 414ad77ae6b33..563d2ed7c9fc9 100644
--- a/phpserver/README.md
+++ b/phpserver/README.md
@@ -31,7 +31,7 @@ For more informations about the installation process using the CLI, you can cons
### How to run Magento
-Example usage: ```php -S 127.0.0.1:8082 -t ./pub/ ../phpserver/router.php```
+Example usage: ```php -S 127.0.0.1:8082 -t ./pub/ ./phpserver/router.php```
### What exactly the script does
diff --git a/setup/src/Magento/Setup/Console/Command/InstallCommand.php b/setup/src/Magento/Setup/Console/Command/InstallCommand.php
index 74c2e3b24234c..cc1cca74ed6df 100644
--- a/setup/src/Magento/Setup/Console/Command/InstallCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/InstallCommand.php
@@ -183,7 +183,7 @@ protected function configure()
self::INPUT_KEY_INTERACTIVE_SETUP,
self::INPUT_KEY_INTERACTIVE_SETUP_SHORTCUT,
InputOption::VALUE_NONE,
- 'Interactive Magento instalation'
+ 'Interactive Magento installation'
),
new InputOption(
OperationsExecutor::KEY_SAFE_MODE,
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php
index c0ec78f046e23..e864a81ffcc0e 100644
--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php
@@ -139,7 +139,7 @@ class Session implements ConfigOptionsListInterface
];
/**
- * {@inheritdoc}
+ * @inheritdoc
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function getOptions()
@@ -289,7 +289,7 @@ public function getOptions()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function createConfig(array $options, DeploymentConfig $deploymentConfig)
{
@@ -320,7 +320,7 @@ public function createConfig(array $options, DeploymentConfig $deploymentConfig)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function validate(array $options, DeploymentConfig $deploymentConfig)
{
@@ -340,7 +340,7 @@ public function validate(array $options, DeploymentConfig $deploymentConfig)
if (isset($options[self::INPUT_KEY_SESSION_REDIS_LOG_LEVEL])) {
$level = $options[self::INPUT_KEY_SESSION_REDIS_LOG_LEVEL];
- if (($level < 0) or ($level > 7)) {
+ if (($level < 0) || ($level > 7)) {
$errors[] = "Invalid Redis log level '{$level}'. Valid range is 0-7, inclusive.";
}
}