diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 86b150062d3c0..c9d155177e48a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -953,7 +953,7 @@ protected function collectRawData() if (is_scalar($attrValue)) { if (!in_array($fieldName, $this->_getExportMainAttrCodes())) { $additionalAttributes[$fieldName] = $fieldName . - ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $attrValue; + ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $this->wrapValue($attrValue); } $data[$itemId][$storeId][$fieldName] = htmlspecialchars_decode($attrValue); } @@ -963,7 +963,7 @@ protected function collectRawData() $additionalAttributes[$code] = $fieldName . ImportProduct::PAIR_NAME_VALUE_SEPARATOR . implode( ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, - $this->collectedMultiselectsData[$storeId][$productLinkId][$code] + $this->wrapValue($this->collectedMultiselectsData[$storeId][$productLinkId][$code]) ); } } @@ -994,6 +994,25 @@ protected function collectRawData() return $data; } + /** + * Wrap values with double quotes if "Fields Enclosure" option is enabled + * + * @param string|array $value + * @return string|array + */ + private function wrapValue($value) + { + if (!empty($this->_parameters[\Magento\ImportExport\Model\Export::FIELDS_ENCLOSURE])) { + $wrap = function ($value) { + return sprintf('"%s"', str_replace('"', '""', $value)); + }; + + $value = is_array($value) ? array_map($wrap, $value) : $wrap($value); + } + + return $value; + } + /** * @return array */ diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 16df0866135a7..afb2e86b6248a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -644,6 +644,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ private $productEntityIdentifierField; + /** + * Escaped separator value for regular expression. + * The value is based on PSEUDO_MULTI_LINE_SEPARATOR constant. + * @var string + */ + private $multiLineSeparatorForRegexp; + /** * @param \Magento\Framework\Json\Helper\Data $jsonHelper * @param \Magento\ImportExport\Helper\Data $importExportData @@ -2438,14 +2445,40 @@ private function _parseAdditionalAttributes($rowData) } /** - * Retrieves additional attributes as array code=>value. + * Retrieves additional attributes in format: + * [ + * code1 => value1, + * code2 => value2, + * ... + * codeN => valueN + * ] * - * @param string $additionalAttributes + * @param string $additionalAttributes Attributes data that will be parsed * @return array */ private function parseAdditionalAttributes($additionalAttributes) { - $attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $additionalAttributes); + return empty($this->_parameters[Import::FIELDS_ENCLOSURE]) + ? $this->parseAttributesWithoutWrappedValues($additionalAttributes) + : $this->parseAttributesWithWrappedValues($additionalAttributes); + } + + /** + * Parses data and returns attributes in format: + * [ + * code1 => value1, + * code2 => value2, + * ... + * codeN => valueN + * ] + * + * @param string $attributesData Attributes data that will be parsed. It keeps data in format: + * code=value,code2=value2...,codeN=valueN + * @return array + */ + private function parseAttributesWithoutWrappedValues($attributesData) + { + $attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $attributesData); $preparedAttributes = []; $code = ''; foreach ($attributeNameValuePairs as $attributeData) { @@ -2463,6 +2496,75 @@ private function parseAdditionalAttributes($additionalAttributes) return $preparedAttributes; } + /** + * Parses data and returns attributes in format: + * [ + * code1 => value1, + * code2 => value2, + * ... + * codeN => valueN + * ] + * All values have unescaped data except mupliselect attributes, + * they should be parsed in additional method - parseMultiselectValues() + * + * @param string $attributesData Attributes data that will be parsed. It keeps data in format: + * code="value",code2="value2"...,codeN="valueN" + * where every value is wrapped in double quotes. Double quotes as part of value should be duplicated. + * E.g. attribute with code 'attr_code' has value 'my"value'. This data should be stored as attr_code="my""value" + * + * @return array + */ + private function parseAttributesWithWrappedValues($attributesData) + { + $attributes = []; + preg_match_all('~((?:[a-z0-9_])+)="((?:[^"]|""|"' . $this->getMultiLineSeparatorForRegexp() . '")+)"+~', + $attributesData, + $matches + ); + foreach ($matches[1] as $i => $attributeCode) { + $attribute = $this->retrieveAttributeByCode($attributeCode); + $value = 'multiselect' != $attribute->getFrontendInput() + ? str_replace('""', '"', $matches[2][$i]) + : '"' . $matches[2][$i] . '"'; + $attributes[$attributeCode] = $value; + } + return $attributes; + } + + /** + * Parse values of multiselect attributes depends on "Fields Enclosure" parameter + * + * @param string $values + * @return array + */ + public function parseMultiselectValues($values) + { + if (empty($this->_parameters[Import::FIELDS_ENCLOSURE])) { + return explode(self::PSEUDO_MULTI_LINE_SEPARATOR, $values); + } + if (preg_match_all('~"((?:[^"]|"")*)"~', $values, $matches)) { + return $values = array_map(function ($value) { + return str_replace('""', '"', $value); + }, $matches[1]); + } + return [$values]; + } + + /** + * Retrieves escaped PSEUDO_MULTI_LINE_SEPARATOR if it is metacharacter for regular expression + * + * @return string + */ + private function getMultiLineSeparatorForRegexp() + { + if (!$this->multiLineSeparatorForRegexp) { + $this->multiLineSeparatorForRegexp = in_array(self::PSEUDO_MULTI_LINE_SEPARATOR, str_split('[\^$.|?*+(){}')) + ? '\\' . self::PSEUDO_MULTI_LINE_SEPARATOR + : self::PSEUDO_MULTI_LINE_SEPARATOR; + } + return $this->multiLineSeparatorForRegexp; + } + /** * Set values in use_config_ fields. * diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index 8461e617431d6..0d19b9fcbedc8 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -490,23 +490,25 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe $resultAttrs = []; foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) { - if (!$attrParams['is_static']) { - if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) { - $resultAttrs[$attrCode] = in_array($attrParams['type'], ['select', 'boolean']) - ? $attrParams['options'][strtolower($rowData[$attrCode])] - : $rowData[$attrCode]; - if ('multiselect' == $attrParams['type']) { - $resultAttrs[$attrCode] = []; - foreach (explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]) as $value) { - $resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)]; - } - $resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]); + if ($attrParams['is_static']) { + continue; + } + if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) { + if (in_array($attrParams['type'], ['select', 'boolean'])) { + $resultAttrs[$attrCode] = $attrParams['options'][strtolower($rowData[$attrCode])]; + } elseif ('multiselect' == $attrParams['type']) { + $resultAttrs[$attrCode] = []; + foreach ($this->_entityModel->parseMultiselectValues($rowData[$attrCode]) as $value) { + $resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)]; } - } elseif (array_key_exists($attrCode, $rowData)) { + $resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]); + } else { $resultAttrs[$attrCode] = $rowData[$attrCode]; - } elseif ($withDefaultValue && null !== $attrParams['default_value']) { - $resultAttrs[$attrCode] = $attrParams['default_value']; } + } elseif (array_key_exists($attrCode, $rowData)) { + $resultAttrs[$attrCode] = $rowData[$attrCode]; + } elseif ($withDefaultValue && null !== $attrParams['default_value']) { + $resultAttrs[$attrCode] = $attrParams['default_value']; } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index e57983796f695..0ca40e916c85b 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -71,6 +71,32 @@ protected function textValidation($attrCode, $type) return $valid; } + /** + * Check if value is valid attribute option + * + * @param string $attrCode + * @param array $possibleOptions + * @param string $value + * @return bool + */ + private function validateOption($attrCode, $possibleOptions, $value) + { + if (!isset($possibleOptions[strtolower($value)])) { + $this->_addMessages( + [ + sprintf( + $this->context->retrieveMessageTemplate( + RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION + ), + $attrCode + ) + ] + ); + return false; + } + return true; + } + /** * @param mixed $attrCode * @param string $type @@ -166,23 +192,15 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) break; case 'select': case 'boolean': + $valid = $this->validateOption($attrCode, $attrParams['options'], $rowData[$attrCode]); + break; case 'multiselect': - $values = explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]); - $valid = true; + $values = $this->context->parseMultiselectValues($rowData[$attrCode]); foreach ($values as $value) { - $valid = $valid && isset($attrParams['options'][strtolower($value)]); - } - if (!$valid) { - $this->_addMessages( - [ - sprintf( - $this->context->retrieveMessageTemplate( - RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION - ), - $attrCode - ) - ] - ); + $valid = $this->validateOption($attrCode, $attrParams['options'], $value); + if (!$valid) { + break; + } } break; case 'datetime': diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Edit/Form.php index a56642d1dda33..fec1684f39f36 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Edit/Form.php @@ -86,6 +86,16 @@ protected function _prepareForm() 'values' => $this->_formatFactory->create()->toOptionArray() ] ); + $fieldset->addField( + \Magento\ImportExport\Model\Export::FIELDS_ENCLOSURE, + 'checkbox', + [ + 'name' => \Magento\ImportExport\Model\Export::FIELDS_ENCLOSURE, + 'label' => __('Fields Enclosure'), + 'title' => __('Fields Enclosure'), + 'value' => 1, + ] + ); $form->setUseContainer(true); $this->setForm($form); diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php index 1511f58d4b039..435a663923306 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -174,6 +174,16 @@ protected function _prepareForm() 'value' => Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, ] ); + $fieldsets[$behaviorCode]->addField( + $behaviorCode . \Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE, + 'checkbox', + [ + 'name' => \Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE, + 'label' => __('Fields enclosure'), + 'title' => __('Fields enclosure'), + 'value' => 1, + ] + ); } // fieldset for file uploading diff --git a/app/code/Magento/ImportExport/Model/Export.php b/app/code/Magento/ImportExport/Model/Export.php index 2785327326e89..3b833cd4ab883 100644 --- a/app/code/Magento/ImportExport/Model/Export.php +++ b/app/code/Magento/ImportExport/Model/Export.php @@ -20,6 +20,11 @@ class Export extends \Magento\ImportExport\Model\AbstractModel const FILTER_ELEMENT_SKIP = 'skip_attr'; + /** + * Allow multiple values wrapping in double quotes for additional attributes. + */ + const FIELDS_ENCLOSURE = 'fields_enclosure'; + /** * Filter fields types. */ diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 157887ec62c52..89b5f88f0e9c2 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -78,6 +78,11 @@ class Import extends \Magento\ImportExport\Model\AbstractModel */ const FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR = '_import_multiple_value_separator'; + /** + * Allow multiple values wrapping in double quotes for additional attributes. + */ + const FIELDS_ENCLOSURE = 'fields_enclosure'; + /**#@-*/ /** diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml index b8acac6a1ab64..ce4f61cef4feb 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml @@ -80,6 +80,9 @@ require([ var oldAction = form.action; var url = oldAction + ((oldAction.slice(-1) != '/') ? '/' : '') + 'entity/' + $F('entity') + '/file_format/' + $F('file_format'); + if ($F('fields_enclosure')) { + url += '/fields_enclosure/' + $F('fields_enclosure'); + } form.action = url; form.submit(); form.action = oldAction; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php index 2c9d6ca91e9f9..963de3e46fea6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php @@ -40,7 +40,7 @@ 'option_1' => ['Option 1'], 'option_2' => ['Option 2'], 'option_3' => ['Option 3'], - 'option_4' => ['Option 4 "!@#$%^&*'], + 'option_4' => ['Option 4 "!@#$%^&*'] ], 'order' => [ 'option_1' => 1, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_incorrect_values.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_incorrect_values.php new file mode 100644 index 0000000000000..1ab2f9ef65bff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_incorrect_values.php @@ -0,0 +1,55 @@ +create( + \Magento\Catalog\Setup\CategorySetup::class +); +/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +$attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class +); +$attribute->setData( + [ + 'attribute_code' => 'multiselect_attribute', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'multiselect', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Multiselect Attribute'], + 'backend_type' => 'varchar', + 'backend_model' => \Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend::class, + 'option' => [ + 'value' => [ + 'option_1' => ['Opt|,=ion 1'], + 'option_2' => ['Opt||,ion 2'], + 'option_3' => ['Option 3 "!@#$%^&*, "|"'] + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + ], + ], + ] +); +$attribute->save(); + +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId()); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 9b06764ce264c..0afede93546f2 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -206,4 +206,28 @@ public function testExceptionInGetExportData() $data = $model->setWriter($exportAdapter)->export(); $this->assertEmpty($data); } + + /** + * Verify if fields wrapping works correct when "Fields Enclosure" option enabled + * + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php + */ + public function testExportWithFieldsEnclosure() + { + $this->model->setParameters([ + \Magento\ImportExport\Model\Export::FIELDS_ENCLOSURE => 1 + ]); + + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + $exportData = $this->model->export(); + + $this->assertContains('""Option 2""', $exportData); + $this->assertContains('""Option 3""', $exportData); + $this->assertContains('""Option 4 """"!@#$%^&*"""', $exportData); + $this->assertContains('text_attribute=""!@#$%^&*()_+1234567890-=|\:;"""', $exportData); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index a5c1f07c16415..9ab7db147d811 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1380,4 +1380,55 @@ function(ProductInterface $item) { array_values($actualProductSkus) ); } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute_with_incorrect_values.php + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testProductWithWrappedAdditionalAttributes() + { + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + \Magento\ImportExport\Model\Import\Source\Csv::class, + [ + 'file' => __DIR__ . '/_files/products_to_import_with_additional_attributes.csv', + 'directory' => $directory + ] + ); + $errors = $this->_model->setParameters( + [ + 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product', + \Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE => 1 + ] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + + $this->_model->importData(); + + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Api\ProductRepositoryInterface::class + ); + + /** @var \Magento\Eav\Api\AttributeOptionManagementInterface $multiselectOptions */ + $multiselectOptions = $this->objectManager->get(\Magento\Eav\Api\AttributeOptionManagementInterface::class) + ->getItems(\Magento\Catalog\Model\Product::ENTITY, 'multiselect_attribute'); + + $product1 = $productRepository->get('simple1'); + $this->assertEquals('\'", =|', $product1->getData('text_attribute')); + $this->assertEquals(implode(',', [$multiselectOptions[3]->getValue(), $multiselectOptions[2]->getValue()]), + $product1->getData('multiselect_attribute')); + + $product2 = $productRepository->get('simple2'); + $this->assertEquals('', $product2->getData('text_attribute')); + $this->assertEquals(implode(',', [$multiselectOptions[1]->getValue(), $multiselectOptions[2]->getValue()]), + $product2->getData('multiselect_attribute')); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_additional_attributes.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_additional_attributes.csv new file mode 100644 index 0000000000000..eb882be4c6bb1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_additional_attributes.csv @@ -0,0 +1,3 @@ +sku,product_type,name,price,attribute_set_code,categories,additional_attributes +simple1,simple,"simple 1",25,Default,"Default Category/Category 1","text_attribute=""'"""", =|"",multiselect_attribute=""Option 3 """"!@#$%^&*, """"|""""""|""Opt||,ion 2""" +simple2,simple,"simple 2",34,Default,"Default Category/Category 1","multiselect_attribute=""Opt|,=ion 1""|""Opt||,ion 2"",text_attribute=""""" diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Block/Adminhtml/Export/Edit/FormTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Block/Adminhtml/Export/Edit/FormTest.php index 52fbe5ed7b6af..a463a426dd647 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Block/Adminhtml/Export/Edit/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Block/Adminhtml/Export/Edit/FormTest.php @@ -30,7 +30,11 @@ class FormTest extends \PHPUnit_Framework_TestCase * * @var array */ - protected $_expectedFields = ['base_fieldset' => ['entity' => 'entity', 'file_format' => 'file_format']]; + protected $_expectedFields = ['base_fieldset' => [ + 'entity' => 'entity', + 'file_format' => 'file_format', + 'fields_enclosure' => 'fields_enclosure' + ]]; protected function setUp() { diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/ExportTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/ExportTest.php index 9decf617d50ae..de9379d1494d3 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/ExportTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/ExportTest.php @@ -84,6 +84,6 @@ public function testIndexAction() $body = $this->getResponse()->getBody(); $this->assertSelectCount('fieldset#base_fieldset', 1, $body); - $this->assertSelectCount('fieldset#base_fieldset div.field', 2, $body); + $this->assertSelectCount('fieldset#base_fieldset div.field', 3, $body); } }