diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html
index 5e7fd11dbf693..7d7d4b6ae88cb 100644
--- a/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html
+++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html
@@ -27,7 +27,7 @@
+ data-role="subTitle">
<%= data.subTitle %>
<% } %>
diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html
index 53661ed8df87f..08376964eee63 100644
--- a/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html
+++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html
@@ -27,7 +27,7 @@
+ data-role="subTitle">
<%= data.subTitle %>
<% } %>
diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml
new file mode 100644
index 0000000000000..690b6b8bc2b59
--- /dev/null
+++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Assert "Add to Wish List" icon is clickable in category product listing
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml
index 6a718ebdfcf0f..bb0a3c40c21f1 100644
--- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml
+++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml
@@ -118,6 +118,9 @@
+
+
+
diff --git a/composer.json b/composer.json
index 9cbbf1689738f..8d38f1e0a151e 100644
--- a/composer.json
+++ b/composer.json
@@ -30,7 +30,7 @@
"lib-libxml": "*",
"braintree/braintree_php": "3.35.0",
"colinmollenhour/cache-backend-file": "~1.4.1",
- "colinmollenhour/cache-backend-redis": "1.10.6",
+ "colinmollenhour/cache-backend-redis": "1.11.0",
"colinmollenhour/credis": "1.10.0",
"colinmollenhour/php-redis-session-abstract": "~1.4.0",
"composer/composer": "^1.6",
@@ -88,7 +88,7 @@
"friendsofphp/php-cs-fixer": "~2.14.0",
"lusitanian/oauth": "~0.8.10",
"magento/magento-coding-standard": "*",
- "magento/magento2-functional-testing-framework": "2.5.4",
+ "magento/magento2-functional-testing-framework": "2.6.1",
"pdepend/pdepend": "2.5.2",
"phpcompatibility/php-compatibility": "^9.3",
"phpmd/phpmd": "@stable",
diff --git a/composer.lock b/composer.lock
index 5b94f60fa80a9..347a50bdf68e0 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": "988eebffd81167973e4a51d7efd5be46",
+ "content-hash": "3b292997ff7767b89b6e08b0c550db7d",
"packages": [
{
"name": "braintree/braintree_php",
@@ -88,16 +88,16 @@
},
{
"name": "colinmollenhour/cache-backend-redis",
- "version": "1.10.6",
+ "version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis.git",
- "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf"
+ "reference": "389fb68de15660e39b055d149d31f3708b5d6cbc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/cc941a5f4cc017e11d3eab9061811ba9583ed6bf",
- "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf",
+ "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/389fb68de15660e39b055d149d31f3708b5d6cbc",
+ "reference": "389fb68de15660e39b055d149d31f3708b5d6cbc",
"shasum": ""
},
"require": {
@@ -120,7 +120,7 @@
],
"description": "Zend_Cache backend using Redis with full support for tags.",
"homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis",
- "time": "2018-09-24T16:02:07+00:00"
+ "time": "2019-03-03T04:04:49+00:00"
},
{
"name": "colinmollenhour/credis",
@@ -830,6 +830,7 @@
}
],
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
+ "abandoned": true,
"time": "2018-07-31T13:22:33+00:00"
},
{
@@ -880,6 +881,7 @@
"Guzzle",
"stream"
],
+ "abandoned": true,
"time": "2014-10-12T19:18:40+00:00"
},
{
@@ -5197,6 +5199,90 @@
],
"time": "2017-11-03T13:08:21+00:00"
},
+ {
+ "name": "aws/aws-sdk-php",
+ "version": "3.133.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/aws/aws-sdk-php.git",
+ "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd",
+ "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-simplexml": "*",
+ "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0",
+ "guzzlehttp/promises": "^1.0",
+ "guzzlehttp/psr7": "^1.4.1",
+ "mtdowling/jmespath.php": "^2.5",
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "andrewsville/php-token-reflection": "^1.4",
+ "aws/aws-php-sns-message-validator": "~1.0",
+ "behat/behat": "~3.0",
+ "doctrine/cache": "~1.4",
+ "ext-dom": "*",
+ "ext-openssl": "*",
+ "ext-pcntl": "*",
+ "ext-sockets": "*",
+ "nette/neon": "^2.3",
+ "phpunit/phpunit": "^4.8.35|^5.4.3",
+ "psr/cache": "^1.0",
+ "psr/simple-cache": "^1.0",
+ "sebastian/comparator": "^1.2.3"
+ },
+ "suggest": {
+ "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
+ "doctrine/cache": "To use the DoctrineCacheAdapter",
+ "ext-curl": "To send requests using cURL",
+ "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
+ "ext-sockets": "To use client-side monitoring"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Aws\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Amazon Web Services",
+ "homepage": "http://aws.amazon.com"
+ }
+ ],
+ "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
+ "homepage": "http://aws.amazon.com/sdkforphp",
+ "keywords": [
+ "amazon",
+ "aws",
+ "cloud",
+ "dynamodb",
+ "ec2",
+ "glacier",
+ "s3",
+ "sdk"
+ ],
+ "time": "2020-02-05T19:12:47+00:00"
+ },
{
"name": "behat/gherkin",
"version": "v4.6.0",
@@ -6589,67 +6675,6 @@
],
"time": "2019-10-30T14:39:59+00:00"
},
- {
- "name": "facebook/webdriver",
- "version": "1.7.1",
- "source": {
- "type": "git",
- "url": "https://github.com/facebook/php-webdriver.git",
- "reference": "e43de70f3c7166169d0f14a374505392734160e5"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/e43de70f3c7166169d0f14a374505392734160e5",
- "reference": "e43de70f3c7166169d0f14a374505392734160e5",
- "shasum": ""
- },
- "require": {
- "ext-curl": "*",
- "ext-json": "*",
- "ext-mbstring": "*",
- "ext-zip": "*",
- "php": "^5.6 || ~7.0",
- "symfony/process": "^2.8 || ^3.1 || ^4.0"
- },
- "require-dev": {
- "friendsofphp/php-cs-fixer": "^2.0",
- "jakub-onderka/php-parallel-lint": "^0.9.2",
- "php-coveralls/php-coveralls": "^2.0",
- "php-mock/php-mock-phpunit": "^1.1",
- "phpunit/phpunit": "^5.7",
- "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0",
- "squizlabs/php_codesniffer": "^2.6",
- "symfony/var-dumper": "^3.3 || ^4.0"
- },
- "suggest": {
- "ext-SimpleXML": "For Firefox profile creation"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-community": "1.5-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Facebook\\WebDriver\\": "lib/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "Apache-2.0"
- ],
- "description": "A PHP client for Selenium WebDriver",
- "homepage": "https://github.com/facebook/php-webdriver",
- "keywords": [
- "facebook",
- "php",
- "selenium",
- "webdriver"
- ],
- "abandoned": "php-webdriver/webdriver",
- "time": "2019-06-13T08:02:18+00:00"
- },
{
"name": "flow/jsonpath",
"version": "0.5.0",
@@ -7356,34 +7381,41 @@
},
{
"name": "magento/magento2-functional-testing-framework",
- "version": "2.5.4",
+ "version": "2.6.1",
"source": {
"type": "git",
"url": "https://github.com/magento/magento2-functional-testing-framework.git",
- "reference": "4f482ce22a755a812b76f81020ae71d502f9d043"
+ "reference": "b00f5e195e1ed7f6335bce3052be9a0291f4d0db"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/4f482ce22a755a812b76f81020ae71d502f9d043",
- "reference": "4f482ce22a755a812b76f81020ae71d502f9d043",
+ "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/b00f5e195e1ed7f6335bce3052be9a0291f4d0db",
+ "reference": "b00f5e195e1ed7f6335bce3052be9a0291f4d0db",
"shasum": ""
},
"require": {
"allure-framework/allure-codeception": "~1.3.0",
+ "aws/aws-sdk-php": "^3.132",
"codeception/codeception": "~2.4.5",
"composer/composer": "^1.4",
"consolidation/robo": "^1.0.0",
"csharpru/vault-php": "~3.5.3",
"csharpru/vault-php-guzzle6-transport": "^2.0",
"ext-curl": "*",
+ "ext-json": "*",
+ "ext-openssl": "*",
"flow/jsonpath": ">0.2",
"fzaninotto/faker": "^1.6",
"monolog/monolog": "^1.0",
"mustache/mustache": "~2.5",
"php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0",
+ "php-webdriver/webdriver": "^1.8.0",
"symfony/process": "^2.8 || ^3.1 || ^4.0",
"vlucas/phpdotenv": "^2.4"
},
+ "replace": {
+ "facebook/webdriver": "^1.7.1"
+ },
"require-dev": {
"brainmaestro/composer-git-hooks": "^2.3.1",
"codacy/coverage": "^1.4",
@@ -7430,7 +7462,7 @@
"magento",
"testing"
],
- "time": "2019-12-12T20:14:00+00:00"
+ "time": "2020-02-11T22:23:54+00:00"
},
{
"name": "mikey179/vfsstream",
@@ -7478,6 +7510,63 @@
"homepage": "http://vfs.bovigo.org/",
"time": "2019-10-30T15:31:00+00:00"
},
+ {
+ "name": "mtdowling/jmespath.php",
+ "version": "2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/jmespath/jmespath.php.git",
+ "reference": "52168cb9472de06979613d365c7f1ab8798be895"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895",
+ "reference": "52168cb9472de06979613d365c7f1ab8798be895",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "symfony/polyfill-mbstring": "^1.4"
+ },
+ "require-dev": {
+ "composer/xdebug-handler": "^1.2",
+ "phpunit/phpunit": "^4.8.36|^7.5.15"
+ },
+ "bin": [
+ "bin/jp.php"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "JmesPath\\": "src/"
+ },
+ "files": [
+ "src/JmesPath.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Declaratively specify how to extract elements from a JSON document",
+ "keywords": [
+ "json",
+ "jsonpath"
+ ],
+ "time": "2019-12-30T18:03:34+00:00"
+ },
{
"name": "mustache/mustache",
"version": "v2.13.0",
@@ -7765,6 +7854,71 @@
],
"time": "2018-02-15T16:58:55+00:00"
},
+ {
+ "name": "php-webdriver/webdriver",
+ "version": "1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-webdriver/php-webdriver.git",
+ "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3e33ee3b8a688d719c55acdd7c6788e3006e1d3e",
+ "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php": "^5.6 || ~7.0",
+ "symfony/polyfill-mbstring": "^1.12",
+ "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.0",
+ "jakub-onderka/php-parallel-lint": "^1.0",
+ "php-coveralls/php-coveralls": "^2.0",
+ "php-mock/php-mock-phpunit": "^1.1",
+ "phpunit/phpunit": "^5.7",
+ "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0",
+ "sminnee/phpunit-mock-objects": "^3.4",
+ "squizlabs/php_codesniffer": "^3.5",
+ "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0"
+ },
+ "suggest": {
+ "ext-SimpleXML": "For Firefox profile creation"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.8.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "lib/Exception/TimeoutException.php"
+ ],
+ "psr-4": {
+ "Facebook\\WebDriver\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.",
+ "homepage": "https://github.com/php-webdriver/php-webdriver",
+ "keywords": [
+ "Chromedriver",
+ "geckodriver",
+ "php",
+ "selenium",
+ "webdriver"
+ ],
+ "time": "2020-02-10T15:04:25+00:00"
+ },
{
"name": "phpcollection/phpcollection",
"version": "0.5.0",
diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php
index 00c8bb85d9be7..e03a54f9463d7 100644
--- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php
@@ -6,6 +6,8 @@
*/
namespace Magento\Quote\Api;
+use Magento\CatalogInventory\Api\StockRegistryInterface;
+use Magento\CatalogInventory\Model\Stock;
use Magento\TestFramework\TestCase\WebapiAbstract;
class GuestCartItemRepositoryTest extends WebapiAbstract
@@ -167,9 +169,13 @@ public function testRemoveItem()
/**
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php
+ * @param array $stockData
+ * @param string|null $errorMessage
+ * @dataProvider updateItemDataProvider
*/
- public function testUpdateItem()
+ public function testUpdateItem(array $stockData, string $errorMessage = null)
{
+ $this->updateStockData('simple_one', $stockData);
/** @var \Magento\Quote\Model\Quote $quote */
$quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
@@ -215,6 +221,9 @@ public function testUpdateItem()
],
];
}
+ if ($errorMessage) {
+ $this->expectExceptionMessage($errorMessage);
+ }
$this->_webApiCall($serviceInfo, $requestData);
$quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
@@ -223,4 +232,66 @@ public function testUpdateItem()
$this->assertEquals(5, $item->getQty());
$this->assertEquals($itemId, $item->getItemId());
}
+
+ /**
+ * @return array
+ */
+ public function updateItemDataProvider(): array
+ {
+ return [
+ [
+ []
+ ],
+ [
+ [
+ 'qty' => 0,
+ 'is_in_stock' => 1,
+ 'use_config_manage_stock' => 0,
+ 'manage_stock' => 1,
+ 'use_config_backorders' => 0,
+ 'backorders' => Stock::BACKORDERS_YES_NOTIFY,
+ ]
+ ],
+ [
+ [
+ 'qty' => 0,
+ 'is_in_stock' => 1,
+ 'use_config_manage_stock' => 0,
+ 'manage_stock' => 1,
+ 'use_config_backorders' => 0,
+ 'backorders' => Stock::BACKORDERS_NO,
+ ],
+ 'This product is out of stock.'
+ ],
+ [
+ [
+ 'qty' => 2,
+ 'is_in_stock' => 1,
+ 'use_config_manage_stock' => 0,
+ 'manage_stock' => 1,
+ 'use_config_backorders' => 0,
+ 'backorders' => Stock::BACKORDERS_NO,
+ ],
+ 'The requested qty is not available'
+ ]
+ ];
+ }
+
+ /**
+ * Update product stock
+ *
+ * @param string $sku
+ * @param array $stockData
+ * @return void
+ */
+ private function updateStockData(string $sku, array $stockData): void
+ {
+ if ($stockData) {
+ /** @var $stockRegistry StockRegistryInterface */
+ $stockRegistry = $this->objectManager->create(StockRegistryInterface::class);
+ $stockItem = $stockRegistry->getStockItemBySku($sku);
+ $stockItem->addData($stockData);
+ $stockRegistry->updateStockItemBySku($sku, $stockItem);
+ }
+ }
}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php b/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php
new file mode 100644
index 0000000000000..6a7d034d5892f
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php
@@ -0,0 +1,89 @@
+linkFactory = $linkFactory;
+ $this->optionLinkFactory = $optionLinkFactory;
+ $this->extensionAttributesFactory = $extensionAttributesFactory;
+ }
+
+ /**
+ * Prepare bundle product links
+ *
+ * @param ProductInterface $product
+ * @param array $bundleOptionsData
+ * @param array $bundleSelectionsData
+ * @return ProductInterface
+ */
+ public function execute(
+ ProductInterface $product,
+ array $bundleOptionsData,
+ array $bundleSelectionsData
+ ): ProductInterface {
+ $product->setBundleOptionsData($bundleOptionsData)
+ ->setBundleSelectionsData($bundleSelectionsData);
+ $options = [];
+ foreach ($product->getBundleOptionsData() as $key => $optionData) {
+ $option = $this->optionLinkFactory->create(['data' => $optionData]);
+ $option->setSku($product->getSku());
+ $option->setOptionId(null);
+ $links = [];
+ $bundleLinks = $product->getBundleSelectionsData();
+ foreach ($bundleLinks[$key] as $linkData) {
+ $link = $this->linkFactory->create(['data' => $linkData]);
+ $link->setQty($linkData['selection_qty']);
+ $priceType = $price = null;
+ if ($product->getPriceType() === Price::PRICE_TYPE_FIXED) {
+ $priceType = $linkData['selection_price_type'] ?? null;
+ $price = $linkData['selection_price_value'] ?? null;
+ }
+ $link->setPriceType($priceType);
+ $link->setPrice($price);
+ $links[] = $link;
+ }
+ $option->setProductLinks($links);
+ $options[] = $option;
+ }
+ /** @var ProductExtensionFactory $extensionAttributesFactory */
+ $extensionAttributes = $product->getExtensionAttributes() ?? $this->extensionAttributesFactory->create();
+ $extensionAttributes->setBundleProductOptions($options);
+ $product->setExtensionAttributes($extensionAttributes);
+
+ return $product;
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php
new file mode 100644
index 0000000000000..f1e98cd4ea0bf
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php
@@ -0,0 +1,54 @@
+regionFactory = $regionFactory;
+ }
+
+ /**
+ * Get region ID from cache property if region id exist or load it.
+ *
+ * @param string $regionName
+ * @param string $countryId
+ * @return int|null
+ */
+ public function execute(string $regionName, string $countryId): ?int
+ {
+ $cacheKey = "{$regionName}_{$countryId}";
+
+ if (!isset($this->regionIdsCache[$cacheKey])) {
+ $region = $this->regionFactory->create()->loadByName($regionName, $countryId);
+ $this->regionIdsCache[$cacheKey] = $region->getRegionId() ? (int)$region->getRegionId() : null;
+ }
+
+ return $this->regionIdsCache[$cacheKey];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php
new file mode 100644
index 0000000000000..287a3f07d1964
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php
@@ -0,0 +1,194 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->serializer = $this->objectManager->get(SerializerInterface::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->pageFactory = $this->objectManager->get(PageFactory::class);
+ $this->productResource = $this->objectManager->get(ProductResource::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister('product');
+ $this->registry->unregister('current_product');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Process bundle options view with few selections
+ *
+ * @param string $sku
+ * @param string $optionsSelectLabel
+ * @param array $expectedSelectionsNames
+ * @param bool $requiredOption
+ * @return void
+ */
+ protected function processMultiSelectionsView(
+ string $sku,
+ string $optionsSelectLabel,
+ array $expectedSelectionsNames,
+ bool $requiredOption = false
+ ): void {
+ $product = $this->productRepository->get($sku);
+ $result = $this->renderProductOptionsBlock($product);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($this->backToProductDetailButtonXpath, $result),
+ "'Back to product details' button doesn't exist on the page"
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($this->selectLabelXpath, $optionsSelectLabel), $result),
+ 'Options select label does not exist on the page'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($this->titleXpath, $product->getName()), $result),
+ sprintf('Customize %s label does not exist on the page', $product->getName())
+ );
+ $selectPath = $requiredOption ? $this->getRequiredSelectXpath() : $this->getNotRequiredSelectXpath();
+ foreach ($expectedSelectionsNames as $selection) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($selectPath, $selection), $result),
+ sprintf('Option for product named %s does not exist on the page', $selection)
+ );
+ }
+ }
+
+ /**
+ * Process bundle options view with single selection
+ *
+ * @param string $sku
+ * @param string $optionsSelectLabel
+ * @return void
+ */
+ protected function processSingleSelectionView(string $sku, string $optionsSelectLabel): void
+ {
+ $product = $this->productRepository->get($sku);
+ $result = $this->renderProductOptionsBlock($product);
+ $this->assertEquals(1, Xpath::getElementsCountForXpath($this->backToProductDetailButtonXpath, $result));
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($this->selectLabelXpath, $optionsSelectLabel), $result),
+ 'Options select label does not exist on the page'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($this->singleOptionXpath, $result),
+ 'Bundle product options select with single option does not display correctly'
+ );
+ }
+
+ /**
+ * Register product
+ *
+ * @param ProductInterface $product
+ * @return void
+ */
+ private function registerProduct(ProductInterface $product): void
+ {
+ $this->registry->unregister('product');
+ $this->registry->unregister('current_product');
+ $this->registry->register('product', $product);
+ $this->registry->register('current_product', $product);
+ }
+
+ /**
+ * Render bundle product options block
+ *
+ * @param ProductInterface $product
+ * @return string
+ */
+ private function renderProductOptionsBlock(ProductInterface $product): string
+ {
+ $this->registerProduct($product);
+ $page = $this->pageFactory->create();
+ $page->addHandle(['default', 'catalog_product_view', 'catalog_product_view_type_bundle']);
+ $page->getLayout()->generateXml();
+ $block = $page->getLayout()->getBlock('product.info.bundle.options');
+
+ return $block->toHtml();
+ }
+
+ /**
+ * Get required select Xpath
+ *
+ * @return string
+ */
+ abstract protected function getRequiredSelectXpath(): string;
+
+ /**
+ * Get not required select Xpath
+ *
+ * @return string
+ */
+ abstract protected function getNotRequiredSelectXpath(): string;
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php
new file mode 100644
index 0000000000000..2a61a252e9313
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php
@@ -0,0 +1,172 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Bundle::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->json = $this->objectManager->get(SerializerInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister('product');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php
+ *
+ * @return void
+ */
+ public function testDynamicBundleOptionPrices(): void
+ {
+ $expectedData = [
+ 'options_prices' => [
+ [
+ 'oldPrice' => ['amount' => 10],
+ 'basePrice' => ['amount' => 10],
+ 'finalPrice' => ['amount' => 10],
+ ],
+ [
+ 'oldPrice' => ['amount' => 20],
+ 'basePrice' => ['amount' => 20],
+ 'finalPrice' => ['amount' => 20],
+ ],
+ [
+ 'oldPrice' => ['amount' => 30],
+ 'basePrice' => ['amount' => 30],
+ 'finalPrice' => ['amount' => 30],
+ ],
+ ],
+ 'bundle_prices' => [
+ 'oldPrice' => ['amount' => 0],
+ 'basePrice' => ['amount' => 0],
+ 'finalPrice' => ['amount' => 0],
+ ]
+ ];
+ $this->processBundlePriceView('bundle_product', $expectedData);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options_1.php
+ *
+ * @return void
+ */
+ public function testFixedBundleOptionPrices(): void
+ {
+ $expectedData = [
+ 'options_prices' => [
+ [
+ 'oldPrice' => ['amount' => 2.75],
+ 'basePrice' => ['amount' => 2.75],
+ 'finalPrice' => ['amount' => 2.75],
+ ],
+ [
+ 'oldPrice' => ['amount' => 6.75],
+ 'basePrice' => ['amount' => 6.75],
+ 'finalPrice' => ['amount' => 6.75],
+ ],
+ ],
+ 'bundle_prices' => [
+ 'oldPrice' => ['amount' => 12.75],
+ 'basePrice' => ['amount' => 10],
+ 'finalPrice' => ['amount' => 10],
+ ]
+ ];
+ $this->processBundlePriceView('bundle-product', $expectedData);
+ }
+
+ /**
+ * @param string $productSku
+ * @param array $expectedData
+ * @return void
+ */
+ private function processBundlePriceView(string $productSku, array $expectedData): void
+ {
+ $this->registerProduct($productSku);
+ $jsonConfig = $this->json->unserialize($this->block->getJsonConfig());
+ $this->assertEquals($expectedData['bundle_prices'], $jsonConfig['prices']);
+ $this->assertOptionsConfig($expectedData['options_prices'], $jsonConfig);
+ }
+
+ /**
+ * Assert options prices.
+ *
+ * @param array $expectedData
+ * @param array $actualData
+ * @return void
+ */
+ private function assertOptionsConfig(array $expectedData, array $actualData): void
+ {
+ $optionConfig = $actualData['options'] ?? null;
+ $this->assertNotNull($optionConfig);
+ $optionConfig = reset($optionConfig);
+ foreach (array_values($optionConfig['selections']) as $key => $selection) {
+ $this->assertEquals($expectedData[$key], $selection['prices']);
+ }
+ }
+
+ /**
+ * Register the product.
+ *
+ * @param string $productSku
+ * @return void
+ */
+ private function registerProduct(string $productSku): void
+ {
+ $product = $this->productRepository->get($productSku);
+ $this->registry->unregister('product');
+ $this->registry->register('product', $product);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php
index ce324ed774dc4..dab699064c4bb 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php
@@ -7,53 +7,66 @@
namespace Magento\Bundle\Block\Catalog\Product\View\Type;
+use Magento\Bundle\Model\Product\Price;
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Registry;
+use Magento\Framework\Serialize\SerializerInterface;
+use Magento\Framework\View\LayoutInterface;
+use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\TestCase;
+
/**
- * Test for Magento\Bundle\Block\Catalog\Product\View\Type\Bundle
+ * Class checks bundle product view behaviour
*
* @magentoDataFixture Magento/Bundle/_files/product.php
- * @magentoDbIsolation disabled
+ * @magentoDbIsolation enabled
* @magentoAppArea frontend
+ * @see \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle
*/
-class BundleTest extends \PHPUnit\Framework\TestCase
+class BundleTest extends TestCase
{
- /**
- * @var \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle
- */
+ /** @var Bundle */
private $block;
- /**
- * @var \Magento\Catalog\Api\Data\ProductInterface
- */
- private $product;
-
- /**
- * @var \Magento\TestFramework\ObjectManager
- */
+ /** @var ObjectManagerInterface */
private $objectManager;
- /**
- * @var \Magento\Catalog\Api\ProductRepositoryInterface
- */
+ /** @var ProductRepositoryInterface */
private $productRepository;
+ /** @var LayoutInterface */
+ private $layout;
+
+ /** @var SerializerInterface */
+ private $json;
+
+ /** @var Registry */
+ private $registry;
+
/**
* @inheritdoc
*/
protected function setUp()
{
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-
- $this->productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
- $this->product = $this->productRepository->get('bundle-product', false, null, true);
- $this->product->setPriceType(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC)->save();
- $this->objectManager->get(\Magento\Framework\Registry::class)->unregister('product');
- $this->objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->product);
-
- $this->block = $this->objectManager->get(
- \Magento\Framework\View\LayoutInterface::class
- )->createBlock(
- \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle::class
- );
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->block = $this->layout->createBlock(Bundle::class);
+ $this->json = $this->objectManager->get(SerializerInterface::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister('product');
+
+ parent::tearDown();
}
/**
@@ -61,12 +74,12 @@ protected function setUp()
*
* @return void
*/
- public function testGetJsonConfig()
+ public function testGetJsonConfig(): void
{
- $option = $this->productRepository->get('simple');
- $option->setSpecialPrice(5)
- ->save();
- $config = json_decode($this->block->getJsonConfig(), true);
+ $product = $this->updateProduct('bundle-product', ['price_type' => Price::PRICE_TYPE_DYNAMIC]);
+ $this->registerProduct($product);
+ $this->updateProduct('simple', ['special_price' => 5]);
+ $config = $this->json->unserialize($this->block->getJsonConfig());
$options = current($config['options']);
$selection = current($options['selections']);
$this->assertEquals(10, $selection['prices']['oldPrice']['amount']);
@@ -75,10 +88,124 @@ public function testGetJsonConfig()
}
/**
- * Tear Down
+ * @dataProvider isSalableForStockStatusProvider
+ *
+ * @param bool $isSalable
+ * @param string $expectedValue
+ * @return void
*/
- protected function tearDown()
+ public function testStockStatusView(bool $isSalable, string $expectedValue): void
{
- $this->objectManager->get(\Magento\Framework\Registry::class)->unregister('product');
+ $product = $this->productRepository->get('bundle-product');
+ $product->setAllItemsSalable($isSalable);
+ $this->block->setTemplate('Magento_Bundle::catalog/product/view/type/bundle.phtml');
+ $result = $this->renderBlockHtml($product);
+ $this->assertEquals($expectedValue, trim(strip_tags($result)));
+ }
+
+ /**
+ * @return array
+ */
+ public function isSalableForStockStatusProvider(): array
+ {
+ return [
+ 'is_salable' => [
+ 'is_salable' => true,
+ 'expected_value' => 'In stock',
+ ],
+ 'is_not_salable' => [
+ 'is_salable' => false,
+ 'expected_value' => 'Out of stock',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider isSalableForCustomizeButtonProvider
+ *
+ * @param bool $isSalable
+ * @param string $expectedValue
+ * @return void
+ */
+ public function testCustomizeButton(bool $isSalable, string $expectedValue): void
+ {
+ $product = $this->productRepository->get('bundle-product');
+ $product->setSalable($isSalable);
+ $this->block->setTemplate('Magento_Bundle::catalog/product/view/customize.phtml');
+ $result = $this->renderBlockHtml($product);
+ $this->assertEquals($expectedValue, trim(strip_tags($result)));
+ }
+
+ /**
+ * @return array
+ */
+ public function isSalableForCustomizeButtonProvider(): array
+ {
+ return [
+ 'is_salable' => [
+ 'is_salable' => true,
+ 'expected_value' => 'Customize and Add to Cart',
+ ],
+ 'is_not_salable' => [
+ 'is_salable' => false,
+ 'expected_value' => '',
+ ],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/empty_bundle_product.php
+ *
+ * @param bool $isSalable
+ * @param string $expectedValue
+ * @return void
+ */
+ public function testCustomizeButtonProductWithoutOptions(): void
+ {
+ $product = $this->productRepository->get('bundle-product');
+ $product->setSalable(true);
+ $this->block->setTemplate('Magento_Bundle::catalog/product/view/customize.phtml');
+ $result = $this->renderBlockHtml($product);
+ $this->assertEmpty(trim(strip_tags($result)));
+ }
+
+ /**
+ * Update product
+ *
+ * @param ProductInterface|string $productSku
+ * @param array $data
+ * @return ProductInterface
+ */
+ private function updateProduct(string $productSku, array $data): ProductInterface
+ {
+ $product = $this->productRepository->get($productSku);
+ $product->addData($data);
+
+ return $this->productRepository->save($product);
+ }
+
+ /**
+ * Register product
+ *
+ * @param ProductInterface $product
+ * @return void
+ */
+ private function registerProduct(ProductInterface $product): void
+ {
+ $this->registry->unregister('product');
+ $this->registry->register('product', $product);
+ }
+
+ /**
+ * Render block output
+ *
+ * @param ProductInterface $product
+ * @return string
+ */
+ private function renderBlockHtml(ProductInterface $product): string
+ {
+ $this->registerProduct($product);
+
+ return $this->block->toHtml();
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php
new file mode 100644
index 0000000000000..cbe150b9fb3f5
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php
@@ -0,0 +1,77 @@
+processMultiSelectionsView(
+ 'bundle-product-checkbox-options',
+ 'Checkbox Options',
+ $expectedSelectionsNames
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_required_options.php
+ *
+ * @return void
+ */
+ public function testRequiredSelectMultiSelectionsView(): void
+ {
+ $expectedSelectionsNames = ['Simple Product', 'Simple Product2'];
+ $this->processMultiSelectionsView(
+ 'bundle-product-checkbox-required-options',
+ 'Checkbox Options',
+ $expectedSelectionsNames,
+ true
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_required_option.php
+ *
+ * @return void
+ */
+ public function testShowSingle(): void
+ {
+ $this->processSingleSelectionView('bundle-product-checkbox-required-option', 'Checkbox Options');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getRequiredSelectXpath(): string
+ {
+ return "//input[@type='checkbox' and contains(@data-validate, 'validate-one-required-by-name')"
+ . "and contains(@class, 'bundle-option')]/../label//span[normalize-space(text()) = '1 x %s']";
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getNotRequiredSelectXpath(): string
+ {
+ return "//input[@type='checkbox' and not(contains(@data-validate, 'validate-one-required-by-name'))"
+ . "and contains(@class, 'bundle-option')]/../label//span[normalize-space(text()) = '1 x %s']";
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php
new file mode 100644
index 0000000000000..128fbe56185f3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php
@@ -0,0 +1,77 @@
+processMultiSelectionsView(
+ 'bundle-product-dropdown-options',
+ 'Dropdown Options',
+ $expectedSelectionsNames
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/bundle_product_dropdown_required_options.php
+ *
+ * @return void
+ */
+ public function testRequiredSelectMultiSelectionsView(): void
+ {
+ $expectedSelectionsNames = ['Simple Product', 'Simple Product2'];
+ $this->processMultiSelectionsView(
+ 'bundle-product-dropdown-required-options',
+ 'Dropdown Options',
+ $expectedSelectionsNames,
+ true
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/product.php
+ *
+ * @return void
+ */
+ public function testShowSingle(): void
+ {
+ $this->processSingleSelectionView('bundle-product', 'Bundle Product Items');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getRequiredSelectXpath(): string
+ {
+ return "//select[contains(@id, 'bundle-option') and contains(@data-validate, 'required:true')]"
+ . "/option/span[normalize-space(text()) = '%s']";
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getNotRequiredSelectXpath(): string
+ {
+ return "//select[contains(@id, 'bundle-option') and not(contains(@data-validate, 'required:true'))]"
+ . "/option/span[normalize-space(text()) = '%s']";
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php
new file mode 100644
index 0000000000000..234981f36fa94
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php
@@ -0,0 +1,77 @@
+processMultiSelectionsView(
+ 'bundle-product-multiselect-options',
+ 'Multiselect Options',
+ $expectedSelectionsNames
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_required_options.php
+ *
+ * @return void
+ */
+ public function testRequiredSelectMultiSelectionsView(): void
+ {
+ $expectedSelectionsNames = ['Simple Product', 'Simple Product2'];
+ $this->processMultiSelectionsView(
+ 'bundle-product-multiselect-required-options',
+ 'Multiselect Options',
+ $expectedSelectionsNames,
+ true
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_required_option.php
+ *
+ * @return void
+ */
+ public function testShowSingle(): void
+ {
+ $this->processSingleSelectionView('bundle-product-multiselect-required-option', 'Multiselect Options');
+ }
+
+ /**
+ * @inheridoc
+ */
+ protected function getRequiredSelectXpath(): string
+ {
+ return "//select[contains(@id, 'bundle-option') and @multiple='multiple' "
+ . "and contains(@data-validate, 'required:true')]/option/span[normalize-space(text()) = '1 x %s']";
+ }
+
+ /**
+ * @inheridoc
+ */
+ protected function getNotRequiredSelectXpath(): string
+ {
+ return "//select[contains(@id, 'bundle-option') and @multiple='multiple'"
+ . "and not(contains(@data-validate, 'required:true'))]/option/span[normalize-space(text()) = '1 x %s']";
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php
new file mode 100644
index 0000000000000..b58f5f7d4189e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php
@@ -0,0 +1,73 @@
+processMultiSelectionsView('bundle-product-radio-options', 'Radio Options', $expectedSelectionsNames);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_required_options.php
+ *
+ * @return void
+ */
+ public function testRequiredSelectMultiSelectionsView(): void
+ {
+ $expectedSelectionsNames = ['Simple Product', 'Simple Product2'];
+ $this->processMultiSelectionsView(
+ 'bundle-product-radio-required-options',
+ 'Radio Options',
+ $expectedSelectionsNames,
+ true
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_required_option.php
+ *
+ * @return void
+ */
+ public function testShowSingle(): void
+ {
+ $this->processSingleSelectionView('bundle-product-radio-required-option', 'Radio Options');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getRequiredSelectXpath(): string
+ {
+ return "//input[@type='radio' and contains(@data-validate, 'validate-one-required-by-name')"
+ . "and contains(@class, 'bundle option')]/../label//span[normalize-space(text()) = '%s']";
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getNotRequiredSelectXpath(): string
+ {
+ return "//input[@type='radio' and not(contains(@data-validate, 'validate-one-required-by-name'))"
+ . "and contains(@class, 'bundle option')]/../label//span[normalize-space(text()) = '%s']";
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php
new file mode 100644
index 0000000000000..f9636890e61f6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php
@@ -0,0 +1,77 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-checkbox-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Checkbox Options',
+ 'default_title' => 'Checkbox Options',
+ 'type' => 'checkbox',
+ 'required' => 0,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php
new file mode 100644
index 0000000000000..3475737790c86
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-checkbox-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php
new file mode 100644
index 0000000000000..453b531f75b2d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php
@@ -0,0 +1,68 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-checkbox-required-option')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Checkbox Options',
+ 'default_title' => 'Checkbox Options',
+ 'type' => 'checkbox',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php
new file mode 100644
index 0000000000000..f75241fb8b680
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php
@@ -0,0 +1,26 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-checkbox-required-option', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php
new file mode 100644
index 0000000000000..9b84d1236c5c9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php
@@ -0,0 +1,76 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-checkbox-required-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Checkbox Options',
+ 'default_title' => 'Checkbox Options',
+ 'type' => 'checkbox',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php
new file mode 100644
index 0000000000000..f601d1d6793e6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-checkbox-required-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php
new file mode 100644
index 0000000000000..06f6473802ee2
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php
@@ -0,0 +1,76 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-dropdown-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Dropdown Options',
+ 'default_title' => 'Dropdown Options',
+ 'type' => 'select',
+ 'required' => 0,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php
new file mode 100644
index 0000000000000..857e44d0298cb
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-dropdown-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php
new file mode 100644
index 0000000000000..1789f472f968d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php
@@ -0,0 +1,76 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-dropdown-required-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Dropdown Options',
+ 'default_title' => 'Dropdown Options',
+ 'type' => 'select',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php
new file mode 100644
index 0000000000000..ad4464153dbf1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-dropdown-required-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php
new file mode 100644
index 0000000000000..a5667b89f8bf4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php
@@ -0,0 +1,76 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-multiselect-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Multiselect Options',
+ 'default_title' => 'Multiselect Options',
+ 'type' => 'multi',
+ 'required' => 0,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php
new file mode 100644
index 0000000000000..c02f218131b20
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-multiselect-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php
new file mode 100644
index 0000000000000..7789045f6f7ef
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php
@@ -0,0 +1,68 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-multiselect-required-option')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Multiselect Options',
+ 'default_title' => 'Multiselect Options',
+ 'type' => 'multi',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php
new file mode 100644
index 0000000000000..8e16ecb5f3890
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php
@@ -0,0 +1,26 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-multiselect-required-option', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php
new file mode 100644
index 0000000000000..65bb49f3b6122
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php
@@ -0,0 +1,76 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-multiselect-required-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Multiselect Options',
+ 'default_title' => 'Multiselect Options',
+ 'type' => 'multi',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php
new file mode 100644
index 0000000000000..bf78eece56938
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-multiselect-required-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php
new file mode 100644
index 0000000000000..def31b48b2172
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php
@@ -0,0 +1,76 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-radio-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Radio Options',
+ 'default_title' => 'Radio Options',
+ 'type' => 'radio',
+ 'required' => 0,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php
new file mode 100644
index 0000000000000..5630b2f88ba4d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-radio-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php
new file mode 100644
index 0000000000000..c659387e09dcc
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php
@@ -0,0 +1,68 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-radio-required-option')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Radio Options',
+ 'default_title' => 'Radio Options',
+ 'type' => 'radio',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php
new file mode 100644
index 0000000000000..9a44ccbca6110
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php
@@ -0,0 +1,26 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-radio-required-option', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php
new file mode 100644
index 0000000000000..ec28bf556b69c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php
@@ -0,0 +1,77 @@
+get(WebsiteRepositoryInterface::class);
+$baseWebsiteId = $websiteRepository->get('base')->getId();
+/** @var PrepareBundleLinks $prepareBundleLinks */
+$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle-product-radio-required-options')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setPriceView(0)
+ ->setSkuType(1)
+ ->setWeightType(1)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setPrice(10.0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER);
+
+$bundleOptionsData = [
+ [
+ 'title' => 'Radio Options',
+ 'default_title' => 'Radio Options',
+ 'type' => 'radio',
+ 'required' => 1,
+ 'delete' => '',
+ ],
+];
+$bundleSelectionsData = [
+ [
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ ],
+ [
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_price_value' => 0,
+ 'selection_can_change_qty' => 1,
+ 'delete' => '',
+ 'option_id' => 1,
+ ],
+];
+
+$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php
new file mode 100644
index 0000000000000..8536a76979430
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php
@@ -0,0 +1,27 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+try {
+ $product = $productRepository->get('bundle-product-radio-required-options', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php
new file mode 100644
index 0000000000000..c9e2bfb74090b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php
@@ -0,0 +1,97 @@
+get(ProductExtensionFactory::class);
+/** @var OptionInterfaceFactory $optionFactory */
+$optionFactory = $objectManager->get(OptionInterfaceFactory::class);
+/** @var LinkInterfaceFactory $linkFactory */
+$linkFactory = $objectManager->get(LinkInterfaceFactory::class);
+
+$bundleProduct = $productFactory->create();
+$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
+ ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId())
+ ->setWebsiteIds([$defaultWebsiteId])
+ ->setName('Bundle Product')
+ ->setSku('bundle_product')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(
+ [
+ 'use_config_manage_stock' => 1,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1,
+ ]
+ )
+ ->setSkuType(0)
+ ->setPriceView(0)
+ ->setPriceType(Price::PRICE_TYPE_DYNAMIC)
+ ->setWeightType(0)
+ ->setShipmentType(AbstractType::SHIPMENT_TOGETHER)
+ ->setBundleOptionsData(
+ [
+ [
+ 'title' => 'Option 1',
+ 'default_title' => 'Option 1',
+ 'type' => 'multi',
+ 'required' => 1,
+ ],
+ ]
+ )->setBundleSelectionsData(
+ [
+ [
+ [
+ 'product_id' => $product->getId(),
+ 'sku' => $product->getSku(),
+ 'selection_qty' => 1,
+ 'selection_can_change_qty' => 1,
+ ],
+ [
+ 'product_id' => $product2->getId(),
+ 'sku' => $product2->getSku(),
+ 'selection_qty' => 1,
+ 'selection_can_change_qty' => 1,
+ ],
+ [
+ 'product_id' => $product3->getId(),
+ 'sku' => $product3->getSku(),
+ 'selection_qty' => 1,
+ 'selection_can_change_qty' => 1,
+ ],
+ ]
+ ]
+ );
+
+$options = [];
+foreach ($bundleProduct->getBundleOptionsData() as $key => $optionData) {
+ $option = $optionFactory->create(['data' => $optionData]);
+ $option->setSku($bundleProduct->getSku());
+ $option->setOptionId(null);
+ $links = [];
+ foreach ($bundleProduct->getBundleSelectionsData()[$key] as $linkData) {
+ $link = $linkFactory->create(['data' => $linkData]);
+ $links[] = $link;
+ }
+ $option->setProductLinks($links);
+ $options[] = $option;
+}
+$extensionAttributes = $bundleProduct->getExtensionAttributes() ?: $extensionAttributesFactory->create();
+$extensionAttributes->setBundleProductOptions($options);
+$bundleProduct->setExtensionAttributes($extensionAttributes);
+$productRepository->save($bundleProduct);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php
new file mode 100644
index 0000000000000..25ebbd14bfa39
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php
@@ -0,0 +1,22 @@
+unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+try {
+ $product = $productRepository->get('bundle_product', false, null, true);
+ $productRepository->delete($product);
+} catch (NoSuchEntityException $e) {
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php
index 662b69c89bc6d..fa957a0bfd3f8 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php
@@ -3,31 +3,44 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
-$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
- ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Api\Data\ProductInterfaceFactory;
+use Magento\Catalog\Model\Product\Type as ProductType;
+use Magento\Catalog\Model\Product\Attribute\Source\Status;
+use Magento\Catalog\Model\Product\Visibility;
+use Magento\Msrp\Model\Product\Attribute\Source\Type;
+use Magento\Store\Api\WebsiteRepositoryInterface;
+use Magento\TestFramework\Helper\Bootstrap;
-/** @var $product \Magento\Catalog\Model\Product */
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+$objectManager = Bootstrap::getObjectManager();
+/** @var ProductInterfaceFactory $productFactory */
+$productFactory = $objectManager->get(ProductInterfaceFactory::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->create(ProductRepositoryInterface::class);
+/** @var WebsiteRepositoryInterface $websiteRepository */
+$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
+$defaultWebsiteId = $websiteRepository->get('base')->getId();
+$product = $productFactory->create();
+$product->setTypeId(ProductType::TYPE_SIMPLE)
->setId(10)
- ->setAttributeSetId(4)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
->setName('Simple Product')
->setSku('simple1')
- ->setTaxClassId('none')
+ ->setTaxClassId(0)
->setDescription('description')
->setShortDescription('short description')
->setOptionsContainer('container1')
- ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART)
+ ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART)
->setPrice(10)
->setWeight(1)
->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)
- ->setWebsiteIds([1])
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setWebsiteIds([$defaultWebsiteId])
->setCategoryIds([])
->setStockData([
'use_config_manage_stock' => 1,
@@ -36,29 +49,27 @@
'is_in_stock' => 1,
'manage_stock' => 1,
]);
-
$productRepository->save($product);
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+$product2 = $productFactory->create();
+$product2->setTypeId(ProductType::TYPE_SIMPLE)
->setId(11)
- ->setAttributeSetId(4)
+ ->setAttributeSetId($product2->getDefaultAttributeSetId())
->setName('Simple Product2')
->setSku('simple2')
- ->setTaxClassId('none')
+ ->setTaxClassId(0)
->setDescription('description')
->setShortDescription('short description')
->setOptionsContainer('container1')
- ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_ON_GESTURE)
+ ->setMsrpDisplayActualPriceType(Type::TYPE_ON_GESTURE)
->setPrice(20)
->setWeight(1)
->setMetaTitle('meta title')
->setMetaKeyword('meta keyword')
->setMetaDescription('meta description')
- ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG)
- ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
- ->setWebsiteIds([1])
+ ->setVisibility(Visibility::VISIBILITY_IN_CATALOG)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setWebsiteIds([$defaultWebsiteId])
->setCategoryIds([])
->setStockData([
'use_config_manage_stock' => 1,
@@ -67,24 +78,22 @@
'is_in_stock' => 1,
'manage_stock' => 1,
]);
+$productRepository->save($product2);
-$productRepository->save($product);
-
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+$product3 = $productFactory->create();
+$product3->setTypeId(ProductType::TYPE_SIMPLE)
->setId(12)
- ->setAttributeSetId(4)
+ ->setAttributeSetId($product3->getDefaultAttributeSetId())
->setName('Simple Product 3')
->setSku('simple3')
- ->setTaxClassId('none')
+ ->setTaxClassId(0)
->setDescription('description')
->setShortDescription('short description')
->setPrice(30)
->setWeight(1)
- ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG)
- ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
- ->setWebsiteIds([1])
+ ->setVisibility(Visibility::VISIBILITY_IN_CATALOG)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setWebsiteIds([$defaultWebsiteId])
->setCategoryIds([])
->setStockData([
'use_config_manage_stock' => 1,
@@ -93,29 +102,27 @@
'is_in_stock' => 1,
'manage_stock' => 1,
]);
+$productRepository->save($product3);
-$productRepository->save($product);
-
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+$product4 = $productFactory->create();
+$product4->setTypeId(ProductType::TYPE_SIMPLE)
->setId(13)
- ->setAttributeSetId(4)
+ ->setAttributeSetId($product4->getDefaultAttributeSetId())
->setName('Simple Product 4')
->setSku('simple4')
- ->setTaxClassId('none')
+ ->setTaxClassId(0)
->setDescription('description')
->setShortDescription('short description')
->setOptionsContainer('container1')
- ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART)
+ ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART)
->setPrice(13)
->setWeight(12)
->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)
- ->setWebsiteIds([1])
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setWebsiteIds([$defaultWebsiteId])
->setCategoryIds([])
->setStockData([
'use_config_manage_stock' => 1,
@@ -124,29 +131,27 @@
'is_in_stock' => 1,
'manage_stock' => 1,
]);
+$productRepository->save($product4);
-$productRepository->save($product);
-
-$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class);
-$product->isObjectNew(true);
-$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+$product5 = $productFactory->create();
+$product5->setTypeId(ProductType::TYPE_SIMPLE)
->setId(14)
- ->setAttributeSetId(4)
+ ->setAttributeSetId($product5->getDefaultAttributeSetId())
->setName('Simple Product 5')
->setSku('simple5')
- ->setTaxClassId('none')
+ ->setTaxClassId(0)
->setDescription('description')
->setShortDescription('short description')
->setOptionsContainer('container1')
- ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART)
+ ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART)
->setPrice(14)
->setWeight(10)
->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)
- ->setWebsiteIds([1])
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setWebsiteIds([$defaultWebsiteId])
->setCategoryIds([])
->setStockData([
'use_config_manage_stock' => 1,
@@ -155,5 +160,4 @@
'is_in_stock' => 1,
'manage_stock' => 1,
]);
-
-$productRepository->save($product);
+$productRepository->save($product5);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php
index f979bad9d0f76..a4631526bd4c5 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php
@@ -19,9 +19,15 @@
*
* @magentoAppArea adminhtml
* @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Catalog/_files/product_without_options.php
*/
class CreateCustomOptionsTest extends AbstractBackendController
{
+ /**
+ * @var string
+ */
+ protected $productSku = 'simple';
+
/**
* @var ProductRepositoryInterface
*/
@@ -46,8 +52,6 @@ protected function setUp()
/**
* Test add to product custom option with type "field".
*
- * @magentoDataFixture Magento/Catalog/_files/product_without_options.php
- *
* @dataProvider productWithNewOptionsDataProvider
*
* @param array $productPostData
@@ -57,7 +61,7 @@ protected function setUp()
public function testSaveCustomOptionWithTypeField(array $productPostData): void
{
$this->getRequest()->setPostValue($productPostData);
- $product = $this->productRepository->get('simple');
+ $product = $this->productRepository->get($this->productSku);
$this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId());
$this->assertSessionMessages(
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php
index f1af6e6e41cff..6a4ff066f710d 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php
@@ -21,9 +21,15 @@
*
* @magentoAppArea adminhtml
* @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Catalog/_files/product_without_options.php
*/
class DeleteCustomOptionsTest extends AbstractBackendController
{
+ /**
+ * @var string
+ */
+ protected $productSku = 'simple';
+
/**
* @var ProductRepositoryInterface
*/
@@ -54,8 +60,6 @@ protected function setUp()
/**
* Test delete custom option with type "field".
*
- * @magentoDataFixture Magento/Catalog/_files/product_without_options.php
- *
* @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForCreateOptions
*
* @param array $optionData
@@ -63,7 +67,7 @@ protected function setUp()
*/
public function testDeleteCustomOptionWithTypeField(array $optionData): void
{
- $product = $this->productRepository->get('simple');
+ $product = $this->productRepository->get($this->productSku);
/** @var ProductCustomOptionInterface $option */
$option = $this->optionRepositoryFactory->create(['data' => $optionData]);
$option->setProductSku($product->getSku());
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php
index a45c21444a5d7..1badf6a1a081a 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php
@@ -22,9 +22,15 @@
*
* @magentoAppArea adminhtml
* @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Catalog/_files/product_without_options.php
*/
class UpdateCustomOptionsTest extends AbstractBackendController
{
+ /**
+ * @var string
+ */
+ protected $productSku = 'simple';
+
/**
* @var ProductRepositoryInterface
*/
@@ -55,8 +61,6 @@ protected function setUp()
/**
* Test add to product custom option with type "field".
*
- * @magentoDataFixture Magento/Catalog/_files/product_without_options.php
- *
* @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForUpdateOptions
*
* @param array $optionData
@@ -65,7 +69,7 @@ protected function setUp()
*/
public function testUpdateCustomOptionWithTypeField(array $optionData, array $updateData): void
{
- $product = $this->productRepository->get('simple');
+ $product = $this->productRepository->get($this->productSku);
/** @var ProductCustomOptionInterface|Option $option */
$option = $this->optionRepositoryFactory->create(['data' => $optionData]);
$option->setProductSku($product->getSku());
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php
index f3989248a8ed1..602af4d0e3c78 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php
@@ -32,7 +32,7 @@
\Magento\Catalog\Model\Product::class
);
$product = $product->loadByAttribute('sku', 'simple_product_' . $option->getId());
- if ($product->getId()) {
+ if ($product instanceof \Magento\Catalog\Model\Product && $product->getId()) {
$product->delete();
}
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php
new file mode 100644
index 0000000000000..20cfbda0ac920
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php
@@ -0,0 +1,26 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->random = $this->objectManager->get(Random::class);
+ $this->block = $this->layout->createBlock(Resetpassword::class);
+ $this->block->setTemplate('Magento_Customer::form/resetforgottenpassword.phtml');
+ }
+
+ /**
+ * @return void
+ */
+ public function testResetPasswordForm(): void
+ {
+ $token = $this->random->getUniqueHash();
+ $this->block->setResetPasswordLinkToken($token);
+ $output = $this->block->toHtml();
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf(self::FORM_XPATH, $token), $output),
+ 'Form action does not include correct token'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::NEW_PASSWORD_LABEL_XPATH, $output),
+ 'New password label was not found on the page'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::PASSWORD_CONFIRMATION_LABEL_XPATH, $output),
+ 'Confirm password label was not found on the page'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::SET_NEW_PASSWORD_BUTTON_XPATH, $output),
+ 'Set password button was not found on the page'
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
index df4acf3acca91..18f9b2d2cb737 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
@@ -132,35 +132,6 @@ public function testLogoutAction()
$this->assertRedirect($this->stringContains('customer/account/logoutSuccess'));
}
- /**
- * Test that forgot password email message displays special characters correctly.
- *
- * @codingStandardsIgnoreStart
- * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0
- * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template
- * @magentoConfigFixture current_store customer/password/forgot_email_identity support
- * @magentoConfigFixture current_store general/store_information/name Test special' characters
- * @magentoConfigFixture current_store customer/captcha/enable 0
- * @magentoDataFixture Magento/Customer/_files/customer.php
- * @codingStandardsIgnoreEnd
- */
- public function testForgotPasswordEmailMessageWithSpecialCharacters()
- {
- $email = 'customer@example.com';
-
- $this->getRequest()->setPostValue(['email' => $email]);
- $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
-
- $this->dispatch('customer/account/forgotPasswordPost');
- $this->assertRedirect($this->stringContains('customer/account/'));
-
- $subject = $this->transportBuilderMock->getSentMessage()->getSubject();
- $this->assertContains(
- 'Test special\' characters',
- $subject
- );
- }
-
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
*/
@@ -397,56 +368,6 @@ public function testActiveUserConfirmationAction()
);
}
- /**
- * @codingStandardsIgnoreStart
- * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0
- * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template
- * @magentoConfigFixture current_store customer/password/forgot_email_identity support
- * @magentoConfigFixture current_store customer/captcha/enable 0
- * @magentoDataFixture Magento/Customer/_files/customer.php
- * @codingStandardsIgnoreEnd
- */
- public function testForgotPasswordPostAction()
- {
- $email = 'customer@example.com';
-
- $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
- $this->getRequest()->setPostValue(['email' => $email]);
-
- $this->dispatch('customer/account/forgotPasswordPost');
- $this->assertRedirect($this->stringContains('customer/account/'));
-
- $message = __(
- 'If there is an account associated with %1 you will receive an email with a link to reset your password.',
- $email
- );
- $this->assertSessionMessages(
- $this->equalTo([$message]),
- MessageInterface::TYPE_SUCCESS
- );
- }
-
- /**
- * @magentoConfigFixture current_store customer/captcha/enable 0
- */
- public function testForgotPasswordPostWithBadEmailAction()
- {
- $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
- $this->getRequest()
- ->setPostValue(
- [
- 'email' => 'bad@email',
- ]
- );
-
- $this->dispatch('customer/account/forgotPasswordPost');
- $this->assertRedirect($this->stringContains('customer/account/forgotpassword'));
- $this->assertSessionMessages(
- $this->equalTo(['The email address is incorrect. Verify the email address and try again.']),
- MessageInterface::TYPE_ERROR
- );
- }
-
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
*/
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php
new file mode 100644
index 0000000000000..bbaf55494294e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php
@@ -0,0 +1,97 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->session = $this->objectManager->get(Session::class);
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->random = $this->objectManager->get(Random::class);
+ $this->customerResource = $this->objectManager->get(CustomerResource::class);
+ $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class);
+ $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->customerRegistry->remove($this->customerId);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer_with_website.php
+ *
+ * @return void
+ */
+ public function testCreatePassword(): void
+ {
+ $defaultWebsite = $this->websiteRepository->get('base')->getId();
+ $customer = $this->customerRegistry->retrieveByEmail('john.doe@magento.com', $defaultWebsite);
+ $this->customerId = $customer->getId();
+ $token = $this->random->getUniqueHash();
+ $customer->changeResetPasswordLinkToken($token);
+ $customer->setData('confirmation', 'confirmation');
+ $this->customerResource->save($customer);
+ $this->session->setRpToken($token);
+ $this->session->setRpCustomerId($customer->getId());
+ $this->dispatch('customer/account/createPassword');
+ $block = $this->layout->getBlock('resetPassword');
+ $this->assertEquals($token, $block->getResetPasswordLinkToken());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php
new file mode 100644
index 0000000000000..8bfe3b5524487
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php
@@ -0,0 +1,137 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class);
+ }
+
+ /**
+ * @magentoConfigFixture current_store customer/captcha/enable 0
+ *
+ * @return void
+ */
+ public function testWithoutEmail(): void
+ {
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setPostValue(['email' => '']);
+ $this->dispatch('customer/account/forgotPasswordPost');
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('Please enter your email.')]),
+ MessageInterface::TYPE_ERROR
+ );
+ $this->assertRedirect($this->stringContains('customer/account/forgotpassword'));
+ }
+
+ /**
+ * Test that forgot password email message displays special characters correctly.
+ *
+ * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0
+ * @codingStandardsIgnoreStart
+ * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template
+ * @codingStandardsIgnoreEnd
+ * @magentoConfigFixture current_store customer/password/forgot_email_identity support
+ * @magentoConfigFixture current_store general/store_information/name Test special' characters
+ * @magentoConfigFixture current_store customer/captcha/enable 0
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testForgotPasswordEmailMessageWithSpecialCharacters(): void
+ {
+ $email = 'customer@example.com';
+ $this->getRequest()->setPostValue(['email' => $email]);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->dispatch('customer/account/forgotPasswordPost');
+ $this->assertRedirect($this->stringContains('customer/account/'));
+ $this->assertSuccessSessionMessage($email);
+ $subject = $this->transportBuilderMock->getSentMessage()->getSubject();
+ $this->assertContains('Test special\' characters', $subject);
+ }
+
+ /**
+ * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0
+ * @codingStandardsIgnoreStart
+ * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template
+ * @codingStandardsIgnoreEnd
+ * @magentoConfigFixture current_store customer/password/forgot_email_identity support
+ * @magentoConfigFixture current_store customer/captcha/enable 0
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testForgotPasswordPostAction(): void
+ {
+ $email = 'customer@example.com';
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setPostValue(['email' => $email]);
+ $this->dispatch('customer/account/forgotPasswordPost');
+ $this->assertRedirect($this->stringContains('customer/account/'));
+ $this->assertSuccessSessionMessage($email);
+ }
+
+ /**
+ * @magentoConfigFixture current_store customer/captcha/enable 0
+ *
+ * @return void
+ */
+ public function testForgotPasswordPostWithBadEmailAction(): void
+ {
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setPostValue(['email' => 'bad@email']);
+ $this->dispatch('customer/account/forgotPasswordPost');
+ $this->assertRedirect($this->stringContains('customer/account/forgotpassword'));
+ $this->assertSessionMessages(
+ $this->equalTo(['The email address is incorrect. Verify the email address and try again.']),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+
+ /**
+ * Assert success session message
+ *
+ * @param string $email
+ * @return void
+ */
+ private function assertSuccessSessionMessage(string $email): void
+ {
+ $message = __(
+ 'If there is an account associated with %1 you will receive an email with a link to reset your password.',
+ $email
+ );
+ $this->assertSessionMessages($this->equalTo([$message]), MessageInterface::TYPE_SUCCESS);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php
new file mode 100644
index 0000000000000..03473e9247c51
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php
@@ -0,0 +1,177 @@
+ 'customer@example.com',
+ 'firstname' => 'First name',
+ 'lastname' => 'Last name',
+ ];
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class);
+ $this->customerFactory = $this->objectManager->get(CustomerInterfaceFactory::class);
+ $this->dataObjectHelper = $this->objectManager->create(DataObjectHelper::class);
+ parent::setUp();
+ }
+
+ /**
+ * @dataProvider createInvalidAccountDataProvider
+ * @param array $customerData
+ * @param string $password
+ * @param string $errorType
+ * @param string $errorMessage
+ * @return void
+ */
+ public function testCreateAccountWithInvalidFields(
+ array $customerData,
+ string $password,
+ string $errorType,
+ array $errorMessage
+ ): void {
+ $data = array_merge($this->defaultCustomerData, $customerData);
+ $customerEntity = $this->customerFactory->create();
+ $this->dataObjectHelper->populateWithArray($customerEntity, $data, CustomerInterface::class);
+ $this->expectException($errorType);
+ $this->expectExceptionMessage((string)__(...$errorMessage));
+ $this->accountManagement->createAccount($customerEntity, $password);
+ }
+
+ /**
+ * @return array
+ */
+ public function createInvalidAccountDataProvider(): array
+ {
+ return [
+ 'empty_firstname' => [
+ 'customer_data' => ['firstname' => ''],
+ 'password' => '_aPassword1',
+ 'error_type' => Exception::class,
+ 'error_message' => ['"%1" is a required value.', 'First Name'],
+ ],
+ 'empty_lastname' => [
+ 'customer_data' => ['lastname' => ''],
+ 'password' => '_aPassword1',
+ 'error_type' => Exception::class,
+ 'error_message' => ['"%1" is a required value.', 'Last Name'],
+ ],
+ 'empty_email' => [
+ 'customer_data' => ['email' => ''],
+ 'password' => '_aPassword1',
+ 'error_type' => Exception::class,
+ 'error_message' => ['The customer email is missing. Enter and try again.'],
+ ],
+ 'invalid_email' => [
+ 'customer_data' => ['email' => 'zxczxczxc'],
+ 'password' => '_aPassword1',
+ 'error_type' => Exception::class,
+ 'error_message' => ['"%1" is not a valid email address.', 'Email'],
+ ],
+ 'empty_password' => [
+ 'customer_data' => [],
+ 'password' => '',
+ 'error_type' => InputException::class,
+ 'error_message' => ['The password needs at least 8 characters. Create a new password and try again.'],
+ ],
+ 'invalid_password_minimum_length' => [
+ 'customer_data' => [],
+ 'password' => 'test',
+ 'error_type' => InputException::class,
+ 'error_message' => ['The password needs at least 8 characters. Create a new password and try again.'],
+ ],
+ 'invalid_password_maximum_length' => [
+ 'customer_data' => [],
+ 'password' => $this->getRandomNumericString(257),
+ 'error_type' => InputException::class,
+ 'error_message' => ['Please enter a password with at most 256 characters.'],
+ ],
+ 'invalid_password_without_minimum_characters_classes' => [
+ 'customer_data' => [],
+ 'password' => 'test_password',
+ 'error_type' => InputException::class,
+ 'error_message' => [
+ 'Minimum of different classes of characters in password is %1.'
+ . ' Classes of characters: Lower Case, Upper Case, Digits, Special Characters.',
+ 3,
+ ],
+ ],
+ 'password_same_as_email' => [
+ 'customer_data' => ['email' => 'test1@test.com'],
+ 'password' => 'test1@test.com',
+ 'error_type' => LocalizedException::class,
+ 'error_message' => [
+ 'The password can\'t be the same as the email address. Create a new password and try again.',
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Returns random numeric string with given length.
+ *
+ * @param int $length
+ * @return string
+ */
+ private function getRandomNumericString(int $length): string
+ {
+ $string = '';
+ for ($i = 0; $i <= $length; $i++) {
+ $string .= Random::getRandomNumber(0, 9);
+ }
+
+ return $string;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php
new file mode 100644
index 0000000000000..7820316d9f41f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php
@@ -0,0 +1,64 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class);
+ $this->transportBuilder = $this->objectManager->get(TransportBuilderMock::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testForgotPassword(): void
+ {
+ $email = 'customer@example.com';
+ $result = $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET);
+ $message = $this->transportBuilder->getSentMessage();
+ $messageContent = $message->getBody()->getParts()[0]->getRawContent();
+ $this->assertTrue($result);
+ $this->assertEquals(1, Xpath::getElementsCountForXpath($this->newPasswordLinkPath, $messageContent));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php
new file mode 100644
index 0000000000000..ae65c32fe3f43
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php
@@ -0,0 +1,368 @@
+ 3468676,
+ AddressInterface::POSTCODE => 75477,
+ AddressInterface::COUNTRY_ID => 'US',
+ 'custom_region_name' => 'Alabama',
+ AddressInterface::CITY => 'CityM',
+ AddressInterface::STREET => 'Green str, 67',
+ AddressInterface::LASTNAME => 'Smith',
+ AddressInterface::FIRSTNAME => 'John',
+ ];
+
+ /**
+ * @var ObjectManager
+ */
+ private $objectManager;
+
+ /**
+ * @var GetRegionIdByName
+ */
+ private $getRegionIdByName;
+
+ /**
+ * @var AddressInterfaceFactory
+ */
+ private $addressFactory;
+
+ /**
+ * @var AddressRegistry
+ */
+ private $addressRegistry;
+
+ /**
+ * @var Address
+ */
+ private $addressResource;
+
+ /**
+ * @var CustomerRegistry
+ */
+ private $customerRegistry;
+
+ /**
+ * @var AddressRepositoryInterface
+ */
+ private $addressRepository;
+
+ /**
+ * @var CustomerRepositoryInterface
+ */
+ private $customerRepository;
+
+ /**
+ * @var int[]
+ */
+ private $createdAddressesIds = [];
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->getRegionIdByName = $this->objectManager->get(GetRegionIdByName::class);
+ $this->addressFactory = $this->objectManager->get(AddressInterfaceFactory::class);
+ $this->addressRegistry = $this->objectManager->get(AddressRegistry::class);
+ $this->addressResource = $this->objectManager->get(Address::class);
+ $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class);
+ $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class);
+ $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
+ parent::setUp();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ foreach ($this->createdAddressesIds as $createdAddressesId) {
+ $this->addressRegistry->remove($createdAddressesId);
+ }
+ parent::tearDown();
+ }
+
+ /**
+ * Assert that default addresses properly created for customer.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer_no_address.php
+ *
+ * @dataProvider createDefaultAddressesDataProvider
+ *
+ * @param array $addressData
+ * @param bool $isShippingDefault
+ * @param bool $isBillingDefault
+ * @return void
+ */
+ public function testCreateDefaultAddress(
+ array $addressData,
+ bool $isShippingDefault,
+ bool $isBillingDefault
+ ): void {
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->assertNull($customer->getDefaultShipping(), 'Customer already has default shipping address');
+ $this->assertNull($customer->getDefaultBilling(), 'Customer already has default billing address');
+ $address = $this->createAddress(
+ (int)$customer->getId(),
+ $addressData,
+ $isShippingDefault,
+ $isBillingDefault
+ );
+ $expectedShipping = $isShippingDefault ? $address->getId() : null;
+ $expectedBilling = $isBillingDefault ? $address->getId() : null;
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->assertEquals($expectedShipping, $customer->getDefaultShipping());
+ $this->assertEquals($expectedBilling, $customer->getDefaultBilling());
+ }
+
+ /**
+ * Data provider for create default or not default address.
+ *
+ * @return array
+ */
+ public function createDefaultAddressesDataProvider(): array
+ {
+ return [
+ 'any_addresses_are_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, false, false],
+ 'shipping_address_is_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, true, false],
+ 'billing_address_is_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, false, true],
+ 'all_addresses_are_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, true, true],
+ ];
+ }
+
+ /**
+ * Assert that address created successfully.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer_no_address.php
+ *
+ * @dataProvider createAddressesDataProvider
+ *
+ * @param array $addressData
+ * @param array $expectedData
+ * @return void
+ */
+ public function testAddressCreatedWithProperData(array $addressData, array $expectedData): void
+ {
+ if (isset($expectedData['custom_region_name'])) {
+ $expectedData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute(
+ $expectedData['custom_region_name'],
+ $expectedData[AddressInterface::COUNTRY_ID]
+ );
+ unset($expectedData['custom_region_name']);
+ }
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $createdAddressData = $this->createAddress((int)$customer->getId(), $addressData)->__toArray();
+ foreach ($expectedData as $fieldCode => $expectedValue) {
+ $this->assertTrue(isset($createdAddressData[$fieldCode]), "Field $fieldCode wasn't found.");
+ $this->assertEquals($createdAddressData[$fieldCode], $expectedValue);
+ }
+ }
+
+ /**
+ * Data provider for create address with proper data.
+ *
+ * @return array
+ */
+ public function createAddressesDataProvider(): array
+ {
+ return [
+ 'required_fields_valid_data' => [
+ self::STATIC_CUSTOMER_ADDRESS_DATA,
+ [
+ AddressInterface::TELEPHONE => 3468676,
+ AddressInterface::COUNTRY_ID => 'US',
+ AddressInterface::POSTCODE => 75477,
+ 'custom_region_name' => 'Alabama',
+ AddressInterface::FIRSTNAME => 'John',
+ AddressInterface::LASTNAME => 'Smith',
+ AddressInterface::STREET => ['Green str, 67'],
+ AddressInterface::CITY => 'CityM',
+ ],
+ ],
+ 'required_field_empty_postcode_for_uk' => [
+ array_replace(
+ self::STATIC_CUSTOMER_ADDRESS_DATA,
+ [AddressInterface::POSTCODE => '', AddressInterface::COUNTRY_ID => 'GB']
+ ),
+ [
+ AddressInterface::COUNTRY_ID => 'GB',
+ AddressInterface::POSTCODE => null,
+ ],
+ ],
+ 'required_field_empty_region_id_for_ua' => [
+ array_replace(
+ self::STATIC_CUSTOMER_ADDRESS_DATA,
+ [AddressInterface::REGION_ID => '', AddressInterface::COUNTRY_ID => 'UA']
+ ),
+ [
+ AddressInterface::COUNTRY_ID => 'UA',
+ AddressInterface::REGION => [
+ 'region' => null,
+ 'region_code' => null,
+ 'region_id' => 0,
+ ],
+ ],
+ ],
+ 'required_field_street_as_array' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => ['', 'Green str, 67']]),
+ [AddressInterface::STREET => ['Green str, 67']],
+ ],
+ 'field_name_prefix' => [
+ array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::PREFIX => 'My prefix']),
+ [AddressInterface::PREFIX => 'My prefix'],
+ ],
+ 'field_middle_name_initial' => [
+ array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::MIDDLENAME => 'My middle name']),
+ [AddressInterface::MIDDLENAME => 'My middle name'],
+ ],
+ 'field_name_suffix' => [
+ array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::SUFFIX => 'My suffix']),
+ [AddressInterface::SUFFIX => 'My suffix'],
+ ],
+ 'field_company_name' => [
+ array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::COMPANY => 'My company']),
+ [AddressInterface::COMPANY => 'My company'],
+ ],
+ 'field_vat_number' => [
+ array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::VAT_ID => 'My VAT number']),
+ [AddressInterface::VAT_ID => 'My VAT number'],
+ ],
+ ];
+ }
+
+ /**
+ * Assert that proper error message has thrown if address creating with wrong data.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer_no_address.php
+ *
+ * @dataProvider createWrongAddressesDataProvider
+ *
+ * @param array $addressData
+ * @param \Exception $expectException
+ * @return void
+ */
+ public function testExceptionThrownDuringCreateAddress(array $addressData, \Exception $expectException): void
+ {
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->expectExceptionObject($expectException);
+ $this->createAddress((int)$customer->getId(), $addressData);
+ }
+
+ /**
+ * Data provider for create address with wrong data.
+ *
+ * @return array
+ */
+ public function createWrongAddressesDataProvider(): array
+ {
+ return [
+ 'required_field_empty_telephone' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::TELEPHONE => '']),
+ InputException::requiredField('telephone'),
+ ],
+ 'required_field_empty_postcode_for_us' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::POSTCODE => '']),
+ InputException::requiredField('postcode'),
+ ],
+// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031
+// 'required_field_empty_region_id_for_us' => [
+// array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::REGION_ID => '']),
+// InputException::requiredField('regionId'),
+// ],
+ 'required_field_empty_firstname' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::FIRSTNAME => '']),
+ InputException::requiredField('firstname'),
+ ],
+ 'required_field_empty_lastname' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::LASTNAME => '']),
+ InputException::requiredField('lastname'),
+ ],
+ 'required_field_empty_street_as_string' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => '']),
+ InputException::requiredField('street'),
+ ],
+ 'required_field_empty_street_as_array' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => []]),
+ InputException::requiredField('street'),
+ ],
+ 'required_field_empty_city' => [
+ array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::CITY => '']),
+ InputException::requiredField('city'),
+ ],
+// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031
+// 'field_invalid_vat_number' => [
+// array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::VAT_ID => '/>.<*']),
+// null// It need to create some error but currently magento doesn't has validation for this field.,
+// ],
+ ];
+ }
+
+ /**
+ * Create customer address with provided address data.
+ *
+ * @param int $customerId
+ * @param array $addressData
+ * @param bool $isDefaultShipping
+ * @param bool $isDefaultBilling
+ * @return AddressInterface
+ */
+ private function createAddress(
+ int $customerId,
+ array $addressData,
+ bool $isDefaultShipping = false,
+ bool $isDefaultBilling = false
+ ): AddressInterface {
+ if (isset($addressData['custom_region_name'])) {
+ $addressData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute(
+ $addressData['custom_region_name'],
+ $addressData[AddressInterface::COUNTRY_ID]
+ );
+ unset($addressData['custom_region_name']);
+ }
+
+ $addressData['attribute_set_id'] = $this->addressResource->getEntityType()->getDefaultAttributeSetId();
+ $address = $this->addressFactory->create(['data' => $addressData]);
+ $address->setCustomerId($customerId);
+ $address->setIsDefaultShipping($isDefaultShipping);
+ $address->setIsDefaultBilling($isDefaultBilling);
+ $address = $this->addressRepository->save($address);
+ $this->customerRegistry->remove($customerId);
+ $this->addressRegistry->remove($address->getId());
+ $this->createdAddressesIds[] = (int)$address->getId();
+
+ return $address;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php
new file mode 100644
index 0000000000000..fe5437e294fc6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php
@@ -0,0 +1,92 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class);
+ $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class);
+ $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
+ parent::setUp();
+ }
+
+ /**
+ * Assert that address deleted successfully.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ *
+ * @return void
+ */
+ public function testDeleteDefaultAddress(): void
+ {
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertEquals(1, $customer->getDefaultShipping());
+ $this->assertEquals(1, $customer->getDefaultBilling());
+ $customerAddresses = $customer->getAddresses() ?? [];
+ foreach ($customerAddresses as $address) {
+ $this->addressRepository->delete($address);
+ }
+ $this->customerRegistry->remove($customer->getId());
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertNull($customer->getDefaultShipping());
+ $this->assertNull($customer->getDefaultBilling());
+ }
+
+ /**
+ * Assert that deleting non-existent address throws exception.
+ *
+ * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+ * @expectedExceptionMessage No such entity with addressId = 1
+ *
+ * @return void
+ */
+ public function testDeleteMissingAddress(): void
+ {
+ $this->addressRepository->deleteById(1);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php
new file mode 100644
index 0000000000000..8867f269cdf37
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php
@@ -0,0 +1,294 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->addressRegistry = $this->objectManager->get(AddressRegistry::class);
+ $this->addressResource = $this->objectManager->get(Address::class);
+ $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class);
+ $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class);
+ $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
+ parent::setUp();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ foreach ($this->processedAddressesIds as $createdAddressesId) {
+ $this->addressRegistry->remove($createdAddressesId);
+ }
+ parent::tearDown();
+ }
+
+ /**
+ * Assert that default addresses properly updated for customer.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ *
+ * @dataProvider updateAddressIsDefaultDataProvider
+ *
+ * @param bool $isShippingDefault
+ * @param bool $isBillingDefault
+ * @param int|null $expectedShipping
+ * @param int|null $expectedBilling
+ * @return void
+ */
+ public function testUpdateAddressIsDefault(
+ bool $isShippingDefault,
+ bool $isBillingDefault,
+ ?int $expectedShipping,
+ ?int $expectedBilling
+ ): void {
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertEquals(1, $customer->getDefaultShipping());
+ $this->assertEquals(1, $customer->getDefaultBilling());
+ $this->processedAddressesIds[] = 1;
+ $address = $this->addressRepository->getById(1);
+ $address->setIsDefaultShipping($isShippingDefault);
+ $address->setIsDefaultBilling($isBillingDefault);
+ $this->addressRepository->save($address);
+ $this->customerRegistry->remove(1);
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertEquals($customer->getDefaultShipping(), $expectedShipping);
+ $this->assertEquals($customer->getDefaultBilling(), $expectedBilling);
+ }
+
+ /**
+ * Data provider for update address as default billing or default shipping.
+ *
+ * @return array
+ */
+ public function updateAddressIsDefaultDataProvider(): array
+ {
+ return [
+ 'update_shipping_address_default' => [true, false, 1, null],
+ 'update_billing_address_default' => [false, true, null, 1],
+ ];
+ }
+
+ /**
+ * Assert that address updated successfully.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ *
+ * @dataProvider updateAddressesDataProvider
+ *
+ * @param array $updateData
+ * @param array $expectedData
+ * @return void
+ */
+ public function testUpdateAddress(array $updateData, array $expectedData): void
+ {
+ $this->processedAddressesIds[] = 1;
+ $address = $this->addressRepository->getById(1);
+ foreach ($updateData as $setFieldName => $setValue) {
+ $address->setData($setFieldName, $setValue);
+ }
+ $updatedAddressData = $this->addressRepository->save($address)->__toArray();
+ foreach ($expectedData as $getFieldName => $getValue) {
+ $this->assertTrue(isset($updatedAddressData[$getFieldName]), "Field $getFieldName wasn't found.");
+ $this->assertEquals($getValue, $updatedAddressData[$getFieldName]);
+ }
+ }
+
+ /**
+ * Data provider for update address with proper data.
+ *
+ * @return array
+ */
+ public function updateAddressesDataProvider(): array
+ {
+ return [
+ 'required_field_telephone' => [
+ [AddressInterface::TELEPHONE => 251512979595],
+ [AddressInterface::TELEPHONE => 251512979595],
+ ],
+ 'required_field_postcode' => [
+ [AddressInterface::POSTCODE => 55425],
+ [AddressInterface::POSTCODE => 55425],
+ ],
+ 'required_field_empty_postcode_for_uk' => [
+ [AddressInterface::COUNTRY_ID => 'GB', AddressInterface::POSTCODE => ''],
+ [AddressInterface::COUNTRY_ID => 'GB', AddressInterface::POSTCODE => null],
+ ],
+ 'required_field_empty_region_id_for_ua' => [
+ [AddressInterface::COUNTRY_ID => 'UA', AddressInterface::REGION_ID => ''],
+ [
+ AddressInterface::COUNTRY_ID => 'UA',
+ AddressInterface::REGION_ID => 0,
+ ],
+ ],
+ 'required_field_firstname' => [
+ [AddressInterface::FIRSTNAME => 'Test firstname'],
+ [AddressInterface::FIRSTNAME => 'Test firstname'],
+ ],
+ 'required_field_lastname' => [
+ [AddressInterface::LASTNAME => 'Test lastname'],
+ [AddressInterface::LASTNAME => 'Test lastname'],
+ ],
+ 'required_field_street_as_array' => [
+ [AddressInterface::STREET => ['', 'Test str, 55']],
+ [AddressInterface::STREET => ['Test str, 55']],
+ ],
+ 'required_field_city' => [
+ [AddressInterface::CITY => 'Test city'],
+ [AddressInterface::CITY => 'Test city'],
+ ],
+ 'field_name_prefix' => [
+ [AddressInterface::PREFIX => 'My prefix'],
+ [AddressInterface::PREFIX => 'My prefix'],
+ ],
+ 'field_middle_name_initial' => [
+ [AddressInterface::MIDDLENAME => 'My middle name'],
+ [AddressInterface::MIDDLENAME => 'My middle name'],
+ ],
+ 'field_name_suffix' => [
+ [AddressInterface::SUFFIX => 'My suffix'],
+ [AddressInterface::SUFFIX => 'My suffix'],
+ ],
+ 'field_company_name' => [
+ [AddressInterface::COMPANY => 'My company'],
+ [AddressInterface::COMPANY => 'My company'],
+ ],
+ 'field_vat_number' => [
+ [AddressInterface::VAT_ID => 'My VAT number'],
+ [AddressInterface::VAT_ID => 'My VAT number'],
+ ],
+ ];
+ }
+
+ /**
+ * Assert that error message has thrown during process address update.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ *
+ * @dataProvider updateWrongAddressesDataProvider
+ *
+ * @param array $updateData
+ * @param \Exception $expectException
+ * @return void
+ */
+ public function testExceptionThrownDuringUpdateAddress(array $updateData, \Exception $expectException): void
+ {
+ $this->processedAddressesIds[] = 1;
+ $address = $this->addressRepository->getById(1);
+ foreach ($updateData as $setFieldName => $setValue) {
+ $address->setData($setFieldName, $setValue);
+ }
+ $this->expectExceptionObject($expectException);
+ $this->addressRepository->save($address);
+ }
+
+ /**
+ * Data provider for update address with proper data or with error.
+ *
+ * @return array
+ */
+ public function updateWrongAddressesDataProvider(): array
+ {
+ return [
+ 'required_field_empty_telephone' => [
+ [AddressInterface::TELEPHONE => ''],
+ InputException::requiredField('telephone'),
+ ],
+ 'required_field_empty_postcode_for_us' => [
+ [AddressInterface::POSTCODE => ''],
+ InputException::requiredField('postcode'),
+ ],
+// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031
+// 'required_field_empty_region_id_for_us' => [
+// [AddressInterface::REGION_ID => ''],
+// InputException::requiredField('regionId'),
+// ],
+ 'required_field_empty_firstname' => [
+ [AddressInterface::FIRSTNAME => ''],
+ InputException::requiredField('firstname'),
+ ],
+ 'required_field_empty_lastname' => [
+ [AddressInterface::LASTNAME => ''],
+ InputException::requiredField('lastname'),
+ ],
+ 'required_field_empty_street_as_array' => [
+ [AddressInterface::STREET => []],
+ InputException::requiredField('street'),
+ ],
+ 'required_field_empty_city' => [
+ [AddressInterface::CITY => ''],
+ InputException::requiredField('city'),
+ ],
+// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031
+// 'field_invalid_vat_number' => [
+// [AddressInterface::VAT_ID => '/>.<*'],
+// null// It need to create some error but currently magento doesn't has validation for this field.,
+// ],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php
new file mode 100644
index 0000000000000..9909856322e13
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php
@@ -0,0 +1,30 @@
+get(Registry::class);
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = $objectManager->get(CustomerRepositoryInterface::class);
+/** @var CustomerRegistry $customerRegistry */
+$customerRegistry = $objectManager->get(CustomerRegistry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+try {
+ $customerRegistry->remove(5);
+ $customerRepository->deleteById(5);
+} catch (NoSuchEntityException $e) {
+ //Customer already deleted.
+}
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php
new file mode 100644
index 0000000000000..8856356227ed9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php
@@ -0,0 +1,60 @@
+block = Bootstrap::getObjectManager()->get(Grouped::class);
+ $this->creditMemo = Bootstrap::getObjectManager()->get(CreditMemo::class);
+ }
+
+ /**
+ * Verify, grouped block will output correct product sku and name.
+ *
+ * @magentoDataFixture Magento/Sales/_files/creditmemo_with_grouped_product.php
+ */
+ public function testToHtml()
+ {
+ $creditMemo = $this->creditMemo->load('100000002', 'increment_id');
+ $creditMemoItem = $creditMemo->getItemsCollection()->getFirstItem();
+ $priceBlock = Bootstrap::getObjectManager()->create(DefaultItems::class);
+ $this->block->setTemplate('Magento_Sales::email/items/creditmemo/default.phtml');
+ $this->block->setItem($creditMemoItem);
+ $this->block->getLayout()->setBlock('item_price', $priceBlock);
+ $output = $this->block->toHtml();
+ self::assertContains('SKU: simple_11', $output);
+ self::assertContains('"product-name">Simple 11', $output);
+ }
+}
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 6dba48092c400..2ac14fbdc68cc 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
@@ -68,6 +68,14 @@ protected function setUp()
*/
public function testExecuteWithPaymentOperation()
{
+ /** @var OrderService|MockObject $orderService */
+ $orderService = $this->getMockBuilder(OrderService::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $orderService->method('place')
+ ->willThrowException(new LocalizedException(__('Transaction has been declined.')));
+ $this->_objectManager->addSharedInstance($orderService, OrderService::class);
+
$quote = $this->getQuote('2000000001');
$session = $this->_objectManager->get(Quote::class);
$session->setQuoteId($quote->getId());
@@ -82,14 +90,6 @@ public function testExecuteWithPaymentOperation()
$this->getRequest()->setMethod(Http::METHOD_POST);
$this->getRequest()->setPostValue(['order' => $data]);
- /** @var OrderService|MockObject $orderService */
- $orderService = $this->getMockBuilder(OrderService::class)
- ->disableOriginalConstructor()
- ->getMock();
- $orderService->method('place')
- ->willThrowException(new LocalizedException(__('Transaction has been declined.')));
- $this->_objectManager->addSharedInstance($orderService, OrderService::class);
-
$this->dispatch('backend/sales/order_create/save');
$this->assertSessionMessages(
self::equalTo(['Transaction has been declined.']),
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php
index 724865176188a..83bc7e10647b4 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php
@@ -10,6 +10,9 @@
/**
* @magentoAppArea frontend
+ *
+ * @deprecated since ShipmentSender is deprecated
+ * @see \Magento\Sales\Model\Order\Email\Sender\ShipmentSender
*/
class ShipmentSenderTest extends \PHPUnit\Framework\TestCase
{
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php
new file mode 100644
index 0000000000000..5f5f534ae2cee
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php
@@ -0,0 +1,36 @@
+get(CreditmemoFactory::class);
+$creditmemo = $creditmemoFactory->createByOrder($order, $order->getData());
+$creditmemo->setOrder($order);
+$creditmemo->setState(Creditmemo::STATE_OPEN);
+$creditmemo->setIncrementId('100000002');
+$creditmemo->save();
+
+$orderItem = current($order->getAllItems());
+$orderItem->setName('Test item')
+ ->setQtyRefunded(1)
+ ->setQtyInvoiced(10)
+ ->setOriginalPrice(20);
+
+$creditItem = $objectManager->get(Item::class);
+$creditItem->setCreditmemo($creditmemo)
+ ->setName('Creditmemo item')
+ ->setOrderItemId($orderItem->getId())
+ ->setQty(1)
+ ->setPrice(20)
+ ->save();
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php
new file mode 100644
index 0000000000000..c8420f8a252ca
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php
@@ -0,0 +1,18 @@
+create(Collection::class);
+foreach ($creditmemoCollection as $creditmemo) {
+ $creditmemo->delete();
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php
new file mode 100644
index 0000000000000..0cdc2d10e6f06
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php
@@ -0,0 +1,65 @@
+create(OrderAddress::class, ['data' => $addressData]);
+$billingAddress->setAddressType('billing');
+
+$shippingAddress = clone $billingAddress;
+$shippingAddress->setId(null)->setAddressType('shipping');
+
+$payment = $objectManager->create(Payment::class);
+$payment->setMethod('checkmo')
+ ->setAdditionalInformation('last_trans_id', '11122')
+ ->setAdditionalInformation(
+ 'metadata',
+ [
+ 'type' => 'free',
+ 'fraudulent' => false,
+ ]
+ );
+$product = $objectManager->get(ProductRepositoryInterface::class)->get('simple_11');
+$orderItem = $objectManager->create(OrderItem::class);
+$orderItem->setProductId($product->getId())
+ ->setQtyOrdered(2)
+ ->setBasePrice($product->getPrice())
+ ->setPrice($product->getPrice())
+ ->setRowTotal($product->getPrice())
+ ->setProductType('grouped')
+ ->setName($product->getName())
+ ->setSku($product->getSku());
+
+$order = $objectManager->create(Order::class);
+$order->setIncrementId('100000002')
+ ->setState(Order::STATE_PROCESSING)
+ ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING))
+ ->setSubtotal(100)
+ ->setGrandTotal(100)
+ ->setBaseSubtotal(100)
+ ->setBaseGrandTotal(100)
+ ->setCustomerIsGuest(true)
+ ->setCustomerEmail('customer@null.com')
+ ->setBillingAddress($billingAddress)
+ ->setShippingAddress($shippingAddress)
+ ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId())
+ ->addItem($orderItem)
+ ->setPayment($payment);
+$orderRepository = $objectManager->create(OrderRepositoryInterface::class);
+$orderRepository->save($order);
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php
new file mode 100644
index 0000000000000..1fb4b4636ab29
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php
@@ -0,0 +1,8 @@
+');
+
+ $(document.body).append(input);
+ input.focus();
+ imagePreview.visibleRecord = jasmine.createSpy().and.returnValue(1);
+ imagePreview.displayedRecord = ko.observable(1);
+ imagePreview.handleKeyDown(elementMock);
+ expect(imagePreview.displayedRecord()).toBe(1);
+
+ $('#input-fixture').remove();
+ });
+ });
});
});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js
index 5f8bfc2c98cc2..b53f49bd6103d 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js
@@ -6,63 +6,190 @@
/*eslint max-nested-callbacks: 0*/
/*jscs:disable requireCamelCaseOrUpperCaseIdentifiers*/
define([
- 'mageUtils',
+ 'jquery',
'Magento_Ui/js/grid/data-storage'
-], function (utils, DataStorage) {
+], function ($, DataStorage) {
'use strict';
describe('Magento_Ui/js/grid/data-storage', function () {
- describe('costructor', function () {
- it('converts dataScope property to array', function () {
+
+ describe('"initConfig" method', function () {
+
+ it('returns self', function () {
var model = new DataStorage({
dataScope: 'magento'
});
- expect(model.dataScope).toEqual(['magento']);
+ expect(model.initConfig()).toEqual(model);
});
- });
- describe('hasScopeChanged', function () {
- it('is function', function () {
+ it('changes string dataScope property to an array', function () {
var model = new DataStorage({
- dataScope: ''
+ dataScope: 'magento'
});
- expect(model.hasScopeChanged).toBeDefined();
- expect(typeof model.hasScopeChanged).toEqual('function');
+ expect(model.dataScope).toEqual(['magento']);
});
- it('returns false if no requests have been made', function () {
+ it('changes empty string dataScope property to an empty array', function () {
var model = new DataStorage({
dataScope: ''
});
- expect(model.hasScopeChanged()).toBeFalsy();
+ expect(model.dataScope).toEqual([]);
});
- it('tells whether parameters defined in the dataScope property have changed', function () {
- var params, newParams, model;
+ it('doesn\'t change non-string dataScope property', function () {
+ var testScope = {
+ testKey: 'test value'
+ },
+ model = new DataStorage({
+ dataScope: testScope
+ });
- params = {
- namespace: 'magento',
- search: '',
- filters: {
- store_id: 0
+ expect(model.dataScope).toEqual(testScope);
+ });
+
+ it('initializes _requests property as an empty array', function () {
+ var model = new DataStorage();
+
+ model._requests = null;
+ model.initConfig();
+ expect(model._requests).toEqual([]);
+ });
+ });
+
+ describe('"getByIds" method', function () {
+
+ it('returns false if data for ids is missing', function () {
+ var model = new DataStorage();
+
+ expect(model.getByIds([1,2,3])).toEqual(false);
+ });
+
+ it('returns array of items', function () {
+ var item = {
+ id_field_name: 'entity_id',
+ entity_id: '1'
},
- sorting: {},
- paging: {}
- };
+ model = new DataStorage({
+ data: {
+ 1: item
+ }
+ });
- newParams = utils.extend({}, params, {
- search: 'magento',
- filters: {
- store_id: 1
- }
- });
+ expect(model.getByIds([1])).toEqual([item]);
+ });
- model = new DataStorage({
- dataScope: 'filters.store_id'
- });
+ });
+
+ describe('"getIds" method', function () {
+
+ it('returns an array of entity_id\'s from provided data', function () {
+ var model = new DataStorage(),
+ ids = [
+ {
+ id_field_name: 'entity_id',
+ entity_id: '1'
+ },
+ {
+ id_field_name: 'entity_id',
+ entity_id: '54'
+ }
+ ];
+
+ expect(model.getIds(ids)).toEqual(['1', '54']);
+ });
+
+ it('returns an array of entity_id\'s from stored data if no arguments provided', function () {
+ var model = new DataStorage({
+ data: {
+ 1: {
+ id_field_name: 'entity_id',
+ entity_id: '1'
+ },
+ 2: {
+ id_field_name: 'entity_id',
+ entity_id: '42'
+ }
+ }
+ });
+
+ expect(model.getIds()).toEqual(['1', '42']);
+ });
+
+ });
+
+ describe('"getData" method', function () {
+
+ var model = new DataStorage();
+
+ it('returns the result of requestData method if scope have been changed', function () {
+ var requestDataResult = 'requestDataResult';
+
+ spyOn(model, 'clearRequests');
+ spyOn(model, 'hasScopeChanged').and.returnValue(true);
+ spyOn(model, 'requestData').and.returnValue(requestDataResult);
+ spyOn(model, 'getRequest');
+ expect(model.getData()).toEqual(requestDataResult);
+ expect(model.clearRequests).toHaveBeenCalled();
+ expect(model.getRequest).not.toHaveBeenCalled();
+ });
+
+ it('returns the cached result if scope have not been changed', function () {
+ var cachedRequestDataResult = 'cachedRequestDataResult';
+
+ spyOn(model, 'clearRequests');
+ spyOn(model, 'requestData');
+ spyOn(model, 'hasScopeChanged').and.returnValue(false);
+ spyOn(model, 'getRequest').and.returnValue(true);
+ spyOn(model, 'getRequestData').and.returnValue(cachedRequestDataResult);
+
+ expect(model.getData()).toEqual(cachedRequestDataResult);
+ expect(model.clearRequests).not.toHaveBeenCalled();
+ expect(model.requestData).not.toHaveBeenCalled();
+ });
+
+ it('returns the result of requestData method if refresh option is provided', function () {
+ var requestDataResult = 'requestDataResult',
+ options = {
+ refresh: true
+ };
+
+ spyOn(model, 'getRequest').and.returnValue(true);
+ spyOn(model, 'clearRequests');
+ spyOn(model, 'hasScopeChanged').and.returnValue(true);
+ spyOn(model, 'requestData').and.returnValue(requestDataResult);
+ expect(model.getData({}, options)).toEqual(requestDataResult);
+ expect(model.clearRequests).toHaveBeenCalled();
+ });
+
+ });
+
+ describe('"hasScopeChanged" method', function () {
+
+ it('returns false if no requests have been made', function () {
+ var model = new DataStorage();
+
+ expect(model.hasScopeChanged()).toBeFalsy();
+ });
+
+ it('returns true for not cached params', function () {
+ var params = {
+ search: '1',
+ filters: {
+ store_id: 0
+ }
+ },
+ newParams = {
+ search: '2',
+ filters: {
+ store_id: 1
+ }
+ },
+ model = new DataStorage({
+ dataScope: 'filters.store_id'
+ });
model.cacheRequest({
totalRecords: 0
@@ -72,5 +199,303 @@ define([
expect(model.hasScopeChanged(newParams)).toBeTruthy();
});
});
+
+ describe('"updateData" method', function () {
+ var model = new DataStorage({
+ dataScope: 'magento',
+ requestConfig: {
+ url: 'magento.com',
+ method: 'GET',
+ dataType: 'json'
+ },
+ data: {
+ 1: {
+ id_field_name: 'entity_id',
+ entity_id: '1',
+ field: 'value'
+ }
+ }
+ });
+
+ it('updates data items', function () {
+ var data = [{
+ id_field_name: 'entity_id',
+ entity_id: '1',
+ field: 'updatedValue'
+ }];
+
+ expect(model.updateData(data)).toEqual(model);
+ expect(model.getByIds([1])).toEqual(data);
+ });
+ });
+
+ describe('"requestData" method', function () {
+ var model = new DataStorage();
+
+ it('Check Ajax request', function () {
+ var result = 'result';
+
+ spyOn(model, 'onRequestComplete').and.returnValue(result);
+ spyOn($, 'ajax').and.returnValue({
+ /**
+ * Success result for ajax request
+ *
+ * @param {Function} handler
+ * @returns {*}
+ */
+ done: function (handler) {
+ return handler();
+ }
+ });
+ expect(model.requestData({})).toEqual(result);
+ });
+ });
+
+ describe('"getRequest" method', function () {
+ var model = new DataStorage({
+ dataScope: 'magento'
+ });
+
+ it('returns cached request', function () {
+ var params = {
+ namespace: 'magento',
+ search: '',
+ sorting: {},
+ paging: {}
+ },
+ request = {
+ ids: ['1'],
+ params: params,
+ totalRecords: 1,
+ errorMessage: ''
+ };
+
+ model._requests.push(request);
+ expect(model.getRequest(params)).toEqual(request);
+ });
+ });
+
+ describe('"getRequestData" method', function () {
+ it('returns request data', function () {
+ var request = {
+ ids: [1,2],
+ totalRecords: 2,
+ errorMessage: ''
+ },
+ items = [
+ {
+ id_field_name: 'entity_id',
+ entity_id: '1'
+ },
+ {
+ id_field_name: 'entity_id',
+ entity_id: '2'
+ }
+ ],
+ result = {
+ items: items,
+ totalRecords: 2,
+ errorMessage: ''
+ },
+ model = new DataStorage({
+ cachedRequestDelay: 0
+ });
+
+ spyOn(model, 'getByIds').and.returnValue(items);
+ model.getRequestData(request).then(function (promiseResult) {
+ expect(promiseResult).toEqual(result);
+ });
+ });
+ });
+
+ describe('"cacheRequest" method', function () {
+ var model = new DataStorage({
+ dataScope: 'magento'
+ });
+
+ it('adds the request to the cache', function () {
+ var params = {
+ namespace: 'magento',
+ search: '',
+ sorting: {},
+ paging: {}
+ },
+ ids = ['1','2','3'],
+ data = {
+ items: ids,
+ totalRecords: 3,
+ errorMessage: ''
+ },
+ request = {
+ ids: ids,
+ params: params,
+ totalRecords: 3,
+ errorMessage: ''
+ };
+
+ spyOn(model, 'removeRequest');
+ spyOn(model, 'getIds').and.returnValue(ids);
+ model.cacheRequest(data, params);
+ expect(model.getRequest(params)).toEqual(request);
+ expect(model.removeRequest).not.toHaveBeenCalled();
+ });
+
+ it('overwrites the previously cached request for the same params', function () {
+ var params = {
+ namespace: 'magento',
+ search: '',
+ sorting: {},
+ paging: {}
+ },
+ ids = ['1','2','3'],
+ firstData = {
+ items: ids,
+ totalRecords: 3,
+ errorMessage: ''
+ },
+ secondData = {
+ items: ids,
+ totalRecords: 3,
+ errorMessage: 'Error message'
+ },
+ firstRequest = {
+ ids: ids,
+ params: params,
+ totalRecords: 3,
+ errorMessage: ''
+ },
+ secondRequest = {
+ ids: ids,
+ params: params,
+ totalRecords: 3,
+ errorMessage: 'Error message'
+ };
+
+ spyOn(model, 'getIds').and.returnValue(ids);
+ model.cacheRequest(firstData, params);
+ expect(model.getRequest(params)).toEqual(firstRequest);
+ model.cacheRequest(secondData, params);
+ expect(model.getRequest(params)).toEqual(secondRequest);
+ });
+ });
+
+ describe('"clearRequests" method', function () {
+
+ it('removes all cached requests', function () {
+ var model = new DataStorage(),
+ params = {
+ namespace: 'magento',
+ search: 'magento',
+ filters: {
+ store_id: 1
+ }
+ };
+
+ model._requests.push({
+ ids: ['1','2','3','4'],
+ params: params,
+ totalRecords: 4,
+ errorMessage: 'errorMessage'
+ });
+ model.clearRequests();
+ expect(model._requests).toEqual([]);
+ });
+ });
+
+ describe('"removeRequest" method', function () {
+
+ var model = new DataStorage();
+
+ it('removes the request from the cache', function () {
+ var params = {
+ namespace: 'magento',
+ search: '',
+ sorting: {},
+ paging: {}
+ },
+ request = {
+ ids: ['1','2','3'],
+ params: params,
+ totalRecords: 3,
+ errorMessage: ''
+ };
+
+ model._requests = [request];
+ expect(model.getRequest(params)).toEqual(request);
+ model.removeRequest(request);
+ expect(model.getRequest(params)).toBeFalsy();
+ });
+ });
+
+ describe('"wasRequested" method', function () {
+ var model = new DataStorage({
+ dataScope: 'magento'
+ });
+
+ it('returns false if request is not present in cache', function () {
+ var params = {
+ namespace: 'magento',
+ search: '',
+ sorting: {},
+ paging: {}
+ };
+
+ expect(model.wasRequested(params)).toBeFalsy();
+ });
+
+ it('returns true if request is present in cache', function () {
+ var params = {
+ namespace: 'magento',
+ search: '',
+ sorting: {},
+ paging: {}
+ },
+ request = {
+ ids: ['1','2','3'],
+ params: params,
+ totalRecords: 3,
+ errorMessage: ''
+ };
+
+ model._requests = [request];
+
+ expect(model.wasRequested(params)).toBeTruthy();
+ });
+ });
+
+ describe('"onRequestComplete" method', function () {
+
+ it('updates data and does not cache the request if caching is disabled', function () {
+ var model = new DataStorage({
+ cacheRequests: false
+ }),
+ data = {
+ items: []
+ },
+ params = {};
+
+ spyOn(model, 'updateData');
+ spyOn(model, 'cacheRequest');
+ model.onRequestComplete(params, data);
+ expect(model.updateData).toHaveBeenCalled();
+ expect(model.cacheRequest).not.toHaveBeenCalled();
+ });
+
+ it('updates data and adds the request to cache if caching is enabled', function () {
+ var model = new DataStorage({
+ cacheRequests: true
+ }),
+ data = {
+ items: []
+ },
+ params = {};
+
+ spyOn(model, 'updateData');
+ spyOn(model, 'cacheRequest');
+ model.onRequestComplete(params, data);
+ expect(model.updateData).toHaveBeenCalled();
+ expect(model.cacheRequest).toHaveBeenCalled();
+ });
+ });
});
});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js
index 8b00ecd3a2aed..3625d0898e942 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js
@@ -10,18 +10,45 @@ define([
'use strict';
describe('ui/js/modal/modal', function () {
- var element = $('
some element
'),
+
+ var element,
+ modal;
+
+ beforeEach(function () {
+ element = $('Element
');
modal = element.modal({}).data('mage-modal');
+ $(element).append('Title
' +
+ '');
+ });
+
+ afterEach(function () {
+ $('.modal-title').remove();
+ $('#element').remove();
+
+ });
+
it('Check for modal definition', function () {
expect(modal).toBeDefined();
});
+
it('Show/hide function check', function () {
expect(element.trigger('openModal')).toBe(element);
expect(element.trigger('closeModal')).toBe(element);
});
+
it('Integration: modal created on page', function () {
expect($(modal).length).toEqual(1);
});
+
+ it('Verify set title', function () {
+ var newTitle = 'New modal title';
+
+ modal.setTitle(newTitle);
+ expect($(modal.options.modalTitle).text()).toContain(newTitle);
+ expect($(modal.options.modalTitle).find(modal.options.modalSubTitle).length).toBe(1);
+ });
});
});
diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
index 8ccda77a25191..b7717492fd124 100644
--- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
@@ -142,7 +142,7 @@ private static function getAddedFilesList($changedFilesBaseDir)
function () {
// if no list files, probably, this is the dev environment
// phpcs:ignore Generic.PHP.NoSilencedErrors,Magento2.Security.InsecureFunction
- @exec('git diff --cached --name-only', $addedFiles);
+ @exec('git diff --cached --name-only --diff-filter=A', $addedFiles);
return $addedFiles;
}
);