From 4cdff1bbd47fc5bab9e3c791f358e11e9c396048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Grygierek?= Date: Fri, 7 Feb 2025 02:38:38 +0100 Subject: [PATCH] allow preset id --- .github/workflows/tests.yml | 18 +-- README.md | 28 ++++- UPGRADE.md | 108 ++++++------------ src/Controller/BaseImportControllerTrait.php | 1 - src/Form/Type/MatrixRecordType.php | 15 +++ src/Model/Matrix/Matrix.php | 20 +++- src/Model/Matrix/MatrixFactory.php | 3 +- src/Model/Matrix/MatrixRecord.php | 2 +- .../Controller/ImportControllerTraitTest.php | 22 ++++ tests/Fixtures/Resources/test.xls | Bin 0 -> 8704 bytes tests/Fixtures/Resources/test.xlsx | Bin 0 -> 6520 bytes .../test_import_with_rows_without_cells.xlsx | Bin 0 -> 8813 bytes .../Fixtures/Resources/test_updated_data.csv | 4 +- tests/Model/FileImportTest.php | 16 +-- tests/Model/Matrix/MatrixFactoryTest.php | 34 +++++- 15 files changed, 154 insertions(+), 117 deletions(-) create mode 100644 tests/Fixtures/Resources/test.xls create mode 100644 tests/Fixtures/Resources/test.xlsx create mode 100644 tests/Fixtures/Resources/test_import_with_rows_without_cells.xlsx diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b0cedbe..56c999c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,30 +2,14 @@ name: Tests on: push: - branches: - - '**' pull_request: - branches: - - master + types: [ opened, reopened ] jobs: - pre_job: - runs-on: ubuntu-latest - outputs: - should_skip: ${{ steps.skip_check.outputs.should_skip }} - steps: - - id: skip_check - uses: fkirc/skip-duplicate-actions@v5 - with: - concurrent_skipping: always - skip_after_successful_duplicate: true - do_not_skip: '["pull_request"]' tests: runs-on: ubuntu-latest name: Tests - needs: pre_job - if: needs.pre_job.outputs.should_skip != 'true' strategy: fail-fast: false diff --git a/README.md b/README.md index 0e90d7d..224f87a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Importing entities with preview and edit features for Symfony. * [Controller-specific templates](#controller-specific-templates) * [Main layout](#main-layout) * [Additional data](#additional-data) +* [Updating entities](#updating-entities) * [Importing data to array field](#importing-data-to-array-field) * [Full example of CSV file](#full-example-of-csv-file) @@ -360,6 +361,25 @@ protected function prepareMatrixEditView(FormInterface $form, Matrix $matrix, bo } ``` +## Updating entities + +If you want to update your entities: +- Set `allowOverrideEntity` to `true` in your import configuration file. +- Then in your import file: + - Add `entity_id` in header and: + - Add entity ID to row + - Leave it empty (if you want to set it manually or import it as new record) + - Or if you don't want to add `entity_id` header, you can still manually set each entity to override. + +#### CSV file + +```csv +entity_id,user_name +2,user_1 +,user_2 +10,user_3 +``` + ## Importing data to array field If your entity has an array field, and you want to import data from CSV file to it, this is how you can do it. @@ -434,8 +454,8 @@ user_3,SUPER_ADMIN ## Full example of CSV file ```csv -user_name,age,email,roles,country:en,name:pl -user_1,21,user_1@test.com,USER&ADMIN&SUPER_ADMIN,Poland,Polska -user_2,34,user_2@test.com,USER,England,Anglia -user_3,56,user_3@test.com,SUPER_ADMIN,Germany,Niemcy +entity_id,user_name,age,email,roles,country:en,name:pl +1,user_1,21,user_1@test.com,USER&ADMIN&SUPER_ADMIN,Poland,Polska +3, user_2,34,user_2@test.com,USER,England,Anglia +,user_3,56,user_3@test.com,SUPER_ADMIN,Germany,Niemcy ``` diff --git a/UPGRADE.md b/UPGRADE.md index a96cc5f..246b4b9 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,70 +1,34 @@ -UPGRADE TO 3.6 -======================= +# UPGRADE TO 3.7 -Import data to array --------------- +### Preset entity id before updating - [go to the documentation](README.md#updating-entities) -* By default, allowed file extensions are set to `'csv', 'xls', 'xlsx', 'ods'`. -However, if you want to change it, you can override this method in your import configuration. +# UPGRADE TO 3.6 -```php -public function getAllowedFileExtensions(): array -{ - return ['csv', 'xls', 'xlsx', 'ods']; -} -``` +### Set allowed file extensions - [go to the documentation](README.md#set-allowed-file-extensions) -UPGRADE TO 3.5 -======================= - -Import data to array --------------- -* If your entity has an array field, and you want to import data from CSV file to it, it is now possible. - -```php -use JG\BatchEntityImportBundle\Form\Type\ArrayTextType; -use JG\BatchEntityImportBundle\Model\Form\FormFieldDefinition; - -public function getFieldsDefinitions(): array -{ - return [ - 'roles' => new FormFieldDefinition( - ArrayTextType::class, - [ - 'separator' => '&', - ] - ), - ]; -} -``` +# UPGRADE TO 3.5 +### Import data to array - [go to the documentation](README.md#importing-data-to-array-field) -UPGRADE TO 3.1 -======================= +# UPGRADE TO 3.1 -CSV File --------------- -* Now CSV file can contain spaces and dashes as a header name, for example "my column name" or "my-column-name". +## CSV File +- Now CSV file can contain spaces and dashes as a header name, for example "my column name" or "my-column-name". -Import Configuration class --------------- -* When header name contains spaces we should use underscores instead of spaces when defining fields names in fields definitions and in constraints. +## Import Configuration class +- When header name contains spaces we should use underscores instead of spaces when defining fields names in fields definitions and in constraints. -UPGRADE TO 3.0 -======================= +# UPGRADE TO 3.0 -Controller --------------- +## Controller * Passing configuration class by `getSubscribedServices()` was removed. Now it is only possible by autoconfiguration. -UPGRADE TO 2.5 -======================= +# UPGRADE TO 2.5 -Import Configuration class --------------- -* Added new validator to check matrix record data uniqueness in database. +## Import Configuration class +- Added new validator to check matrix record data uniqueness in database. ```php use JG\BatchEntityImportBundle\Validator\Constraints\DatabaseEntityUnique; @@ -76,12 +40,10 @@ public function getMatrixConstraints(): array } ``` -UPGRADE TO 2.4 -======================= +# UPGRADE TO 2.4 -Import Configuration class --------------- -* Added new validator to check matrix record data uniqueness. +## Import Configuration class +- Added new validator to check matrix record data uniqueness. ```php use JG\BatchEntityImportBundle\Validator\Constraints\MatrixRecordUnique; @@ -93,33 +55,27 @@ public function getMatrixConstraints(): array } ``` -Controller --------------- -* List of options passed to form in `createMatrixForm()` method, should contain new `constraints` element: +## Controller +- List of options passed to form in `createMatrixForm()` method, should contain new `constraints` element: `'constraints' => $importConfiguration->getMatrixConstraints()` -UPGRADE TO 2.3 -======================= +# UPGRADE TO 2.3 -Controller --------------- -* Passing configuration class by `getSubscribedServices()` method is not needed anymore and will be removed in the future. -* To make sure that configuration class will be injected automatically: - * Interface `JG\BatchEntityImportBundle\Controller\ImportConfigurationAutoInjectInterface` should be implemented. - * Trait `JG\BatchEntityImportBundle\Controller\ImportConfigurationAutoInjectTrait` should be used to add needed methods. +## Controller +- Passing configuration class by `getSubscribedServices()` method is not needed anymore and will be removed in the future. +- To make sure that configuration class will be injected automatically: + - Interface `JG\BatchEntityImportBundle\Controller\ImportConfigurationAutoInjectInterface` should be implemented. + - Trait `JG\BatchEntityImportBundle\Controller\ImportConfigurationAutoInjectTrait` should be used to add needed methods. -UPGRADE TO 2.2 -======================= +# UPGRADE TO 2.2 -Import Configuration class --------------- -* Now configuration class should be always registered as a service: +## Import Configuration class +- Now configuration class should be always registered as a service: ```yaml services: App\Model\ImportConfiguration\UserImportConfiguration: ~ ``` -Controller --------------- -* Entity Manager is no longer passed as an argument of actions. +## Controller +- Entity Manager is no longer passed as an argument of actions. diff --git a/src/Controller/BaseImportControllerTrait.php b/src/Controller/BaseImportControllerTrait.php index 1fc9c15..dc9963a 100644 --- a/src/Controller/BaseImportControllerTrait.php +++ b/src/Controller/BaseImportControllerTrait.php @@ -89,7 +89,6 @@ protected function prepareMatrixEditView(FormInterface $form, Matrix $matrix, bo $this->getMatrixEditTemplateName(), [ 'header_info' => $matrix->getHeaderInfo($configuration->getEntityClassName()), - 'data' => $matrix->getRecords(), 'form' => $form->createView(), 'importConfiguration' => $configuration, ] diff --git a/src/Form/Type/MatrixRecordType.php b/src/Form/Type/MatrixRecordType.php index c76faaa..38c13f2 100644 --- a/src/Form/Type/MatrixRecordType.php +++ b/src/Form/Type/MatrixRecordType.php @@ -19,6 +19,8 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\Exception\AccessException; use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -101,4 +103,17 @@ private function addField(array $fieldDefinitions, string $columnName, FormEvent ? $event->getForm()->add($columnName, $definition->getClass(), $definition->getOptions()) : $event->getForm()->add($columnName, TextType::class); } + + public function finishView(FormView $view, FormInterface $form, array $options): void + { + /** @var MatrixRecord $entity */ + $entity = $form->getData(); + $selectedValue = $entity->entityId; + + foreach ($view['entity']->vars['choices'] ?? [] as $index => $choice) { + if ($choice->value === $selectedValue) { + $view['entity']->vars['choices'][$index]->attr['selected'] = 'selected'; + } + } + } } diff --git a/src/Model/Matrix/Matrix.php b/src/Model/Matrix/Matrix.php index 8d6d7e7..dc431d8 100644 --- a/src/Model/Matrix/Matrix.php +++ b/src/Model/Matrix/Matrix.php @@ -12,6 +12,7 @@ class Matrix { private const RESERVED_ENTITY_COLUMN_NAME = 'entity'; + private const RESERVED_ENTITY_ID_COLUMN_NAME = 'entity_id'; #[Assert\All([ new Assert\NotBlank(), new Assert\Type('string'), @@ -33,9 +34,9 @@ public function __construct(array $header = [], array $recordsData = []) $this->header = $this->clearHeader($header); foreach ($recordsData as $data) { - $data = $this->clearRecordData($data); - if ($data) { - $this->records[] = new MatrixRecord($data); + $clearedData = $this->clearRecordData($data); + if ($clearedData) { + $this->records[] = new MatrixRecord($clearedData, $this->getEntityIdValue($data)); } } } @@ -65,6 +66,17 @@ public function getHeaderInfo(string $className): array return $info; } + private function getEntityIdValue(array $data): int|string|null + { + foreach ($data as $name => $value) { + if (self::RESERVED_ENTITY_ID_COLUMN_NAME === $name) { + return $value; + } + } + + return null; + } + private function clearHeader(array $header): array { $header = array_values( @@ -81,6 +93,6 @@ private function clearRecordData(array $data): array private function isColumnNameValid(?string $name): bool { - return !empty(trim((string) $name)) && self::RESERVED_ENTITY_COLUMN_NAME !== $name; + return !empty(trim((string) $name)) && !\in_array($name, [self::RESERVED_ENTITY_COLUMN_NAME, self::RESERVED_ENTITY_ID_COLUMN_NAME], true); } } diff --git a/src/Model/Matrix/MatrixFactory.php b/src/Model/Matrix/MatrixFactory.php index 9f74ee7..5699e58 100644 --- a/src/Model/Matrix/MatrixFactory.php +++ b/src/Model/Matrix/MatrixFactory.php @@ -8,6 +8,7 @@ use JG\BatchEntityImportBundle\Service\CsvDelimiterDetector; use PhpOffice\PhpSpreadsheet\Reader\BaseReader; use PhpOffice\PhpSpreadsheet\Reader\Csv; +use PhpOffice\PhpSpreadsheet\Reader\Xls; use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use Symfony\Component\HttpFoundation\File\UploadedFile; @@ -55,7 +56,7 @@ private static function getReader(UploadedFile $file): BaseReader if ($reader instanceof Csv) { $detectedDelimiter = (new CsvDelimiterDetector())->detect($file->getContent()); $reader->setDelimiter($detectedDelimiter->value); - } elseif ($reader instanceof Xlsx) { + } elseif ($reader instanceof Xls || $reader instanceof Xlsx) { $reader->setIgnoreRowsWithNoCells(true); } diff --git a/src/Model/Matrix/MatrixRecord.php b/src/Model/Matrix/MatrixRecord.php index c633671..4c5ea53 100644 --- a/src/Model/Matrix/MatrixRecord.php +++ b/src/Model/Matrix/MatrixRecord.php @@ -9,7 +9,7 @@ class MatrixRecord private ?object $entity = null; private array $data = []; - public function __construct(array $data = []) + public function __construct(array $data = [], public readonly int|string|null $entityId = null) { foreach ($data as $name => $value) { if (!empty(\trim((string) $name))) { diff --git a/tests/Controller/ImportControllerTraitTest.php b/tests/Controller/ImportControllerTraitTest.php index 7bb1601..a6b4326 100644 --- a/tests/Controller/ImportControllerTraitTest.php +++ b/tests/Controller/ImportControllerTraitTest.php @@ -66,6 +66,28 @@ public function testUpdateExistingRecord( $this->assertEntityValues($expectedDefaultValues, $updatedEntityId); $this->submitSelectFileForm(__DIR__ . '/../Fixtures/Resources/test_updated_data.csv'); + + $this->assertSame( + '2', + $this->client->getCrawler()->filterXpath('//select[@name="matrix[records][0][entity]"]/option[@selected]')->attr('value'), + ); + $this->assertSame( + 'test', + $this->client->getCrawler()->filterXpath('//input[@name="matrix[records][0][test_private_property]"]')->attr('value'), + ); + $this->assertSame( + 'lorem ipsum', + $this->client->getCrawler()->filterXpath('//input[@name="matrix[records][0][test-private-property2]"]')->attr('value'), + ); + $this->assertSame( + 'qwerty', + $this->client->getCrawler()->filterXpath('//input[@name="matrix[records][0][test_public_property]"]')->attr('value'), + ); + $this->assertSame( + 'arr_val_1000|array_val_1001', + $this->client->getCrawler()->filterXpath('//input[@name="matrix[records][0][test_array_field]"]')->attr('value'), + ); + $this->client->submitForm('btn-submit', [ 'matrix' => [ 'records' => [ diff --git a/tests/Fixtures/Resources/test.xls b/tests/Fixtures/Resources/test.xls new file mode 100644 index 0000000000000000000000000000000000000000..8986abdfb5accf5c8019eda7bd492f5ebc2385dd GIT binary patch literal 8704 zcmeHMTWnlM8UD}OPS;JEv7HNPLcbF+(g)>ldrC+US2av@DAl#9D@*2Q$~HJeRK zq#)dsK2&NYP#<`ygev6)fuIPfNPtB0#sdh1RH^Wk@>W1-i;7UCCd>ED?QF(tWfMi9 zR-VSkUS&!~7$iEvooPD~Oqn!gY_IKK|Q>|9~s5_1L~^8T-I8_T9_ayO-Es%3w_`N`vQ~#SU}sU0h|(eT$vuyl(L}b6&sL)p*?a z?sM36ZQ(;1E3#HPU3&rNA^a{SS!9tDFDCMLc}(zpt{*oyv#P$y(v8>CqndL;R@b?L zeuJldR-Q@FboL_Fk7Orh>)VJ8atF%T>198J{2!5?_vDwIkHbENyg|$$rWUHRPZet= zoGS~ZYVGVsYaeuWoUL8M)j8_i!kLrh+3B3OqaT{5iq&fI?9|L`seB3>5t{PozFeu6 z=5n(Or_apg@>n?Ec$$6W9YaS99T@s95wtkgKVfidP7;}K&#FvcBYB4SJg#sv0wM&2Q8 z=&#Xt2)jNf@Q^3vV^TgPr`Ov1unz_ z7gB)>Lk+ymOvyfkUcS9fXEz|D;1c4QNg2NK*sW2|HCv-K2%@JDT)CO(H378;Ve`Eo zzWt}eCy(u)GWJFD;$$1z&LEG7`wXsJ>fCK=>@mmlhPz(2%QlhzL;c%M%+8fg=Z=(~ z&Kz zccT34;2Y-$->C23)l_~3L+ZwDCgyiu29klq*Z1c{9zok#`Iga9EhAy$uh2lQ=kdAG1tJuaj>)1x$jBSYJOKj9C=26GkMg?PA zZwniD0x@r03mbQ=F^_8}wqYNz#D;yu5*v3Yaosyx*fzJYZE0cqa0?qY155hY8riOx zZymzj6Ou&<5FN^*j9L61bpT=9<{CMQUgZ$$(Wh81!3t33-HM1_s>~M_i^!x0Swtp% z$0GJXuOVVj%6y5ji1p|x7O@_EgovY1=3AXbWb&RYB9k6r5qqF75OKW9yir-idYpfY zSdTML#Fe4A|8s~;&bGZqCg+)m>qQA?*dj7Hw-%AfStX(dN;sbuk;$2~h)m8Q5%p2R z*|UgDMhuI{%sE8ulzcQGG7)eMlQW^@V-9g0D*1RoWNr_L%zlTcxsn|Lk-0Mt`WsU6}RD=7p-=5Rn{ zjyS~mS27w9nPUNw$#-k2%Lt)lA|Nt984#KGI7Giu@~MEx+#3*?`y8TADcK(onUevL zdB7q1o05Y8k$ET}GVgVWzNqj`%Z_@ZQOPqmnt8w9U8A2Wc_1LR`RRbz=7SE=hm|}O z5SgC|h|JGAME_QDI3O~Q1VrXhhv@rCjs-;K=K>=0xI z7a5QcDeLrF#K<~VOCsHMdT5+7a=WR=I*dpAGJWzI!;SF?&%TT8SztOq@d>=5KEWPc z!XAy^#fTUMR#GbkD4zeO55bM%#EjZJucQyQ0tsfh1IvSV*^|u}7rAJTaz7)^;o0eG z<#c7Hmb>qp)1`85Vr=k4@nor7E}1RB!P%J^J{oR1!T)ThH`&5ftZ(kF;zYl`{M--z z_3qKH_5A32S=qDW=YMAOIfu+(!s{G3cM_Axj1Y|8+yyW?@)>^$nNRQqWcKjZ6)}4b zmf6-MvQLw~fb(+YCQ2wSH-7e!dHwgVzkK(Wo|nFl@ejQFlY3$7ifoL?n~-@w%#5h4 z%dswJ$K{Z2<^MmX|Hy|zM@qq?4Qpe_RZ98RqiR667*Y^k_9($(|U!TaZ) zi>V7ciZi(XIsEIv|Nb7tzkoAlPH3s&-;fF{%cq^Le?F<(y?^TUW7lLCc5D@yHe)E6 ee;{vcJmWWU|6Kny*Z-|J-KhS}|G)je`~NSU(OaAV literal 0 HcmV?d00001 diff --git a/tests/Fixtures/Resources/test.xlsx b/tests/Fixtures/Resources/test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f0eb0c0f372d7ab2922c25d0d7bec91aa13ab925 GIT binary patch literal 6520 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 literal 0 HcmV?d00001 diff --git a/tests/Fixtures/Resources/test_import_with_rows_without_cells.xlsx b/tests/Fixtures/Resources/test_import_with_rows_without_cells.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..31f2e8fa582613c5019c63c24555345b810ee931 GIT binary patch literal 8813 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

l7N?W%^(}EA3VFkb_mO*~ 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 SJv20Y)aN>Cn^IsnyZbNRi81>C literal 0 HcmV?d00001 diff --git a/tests/Fixtures/Resources/test_updated_data.csv b/tests/Fixtures/Resources/test_updated_data.csv index 3b02cd0..26f72ba 100644 --- a/tests/Fixtures/Resources/test_updated_data.csv +++ b/tests/Fixtures/Resources/test_updated_data.csv @@ -1,2 +1,2 @@ -test_private_property,test-private-property2,test public property,test_array_field -test,lorem ipsum,qwerty,arr_val_1000|array_val_1001 +test_private_property,test-private-property2,test public property,test_array_field,entity_id +test,lorem ipsum,qwerty,arr_val_1000|array_val_1001,2 diff --git a/tests/Model/FileImportTest.php b/tests/Model/FileImportTest.php index 7e9d986..2fd4ca5 100644 --- a/tests/Model/FileImportTest.php +++ b/tests/Model/FileImportTest.php @@ -62,7 +62,9 @@ public function testEmptyFileError(): void $fileImport = new FileImport(['csv', 'xls', 'xlsx', 'ods']); $this->setUploadedFile($fileImport, 'csv', false); - self::assertNotEmpty($this->getErrors($fileImport)); + $errors = $this->getErrors($fileImport); + self::assertCount(1, $errors); + self::assertSame('An empty file is not allowed.', $errors[0]->getMessage()); } /** @@ -73,7 +75,9 @@ public function testInvalidExtensionError(string $extension, array $allowedExten $fileImport = new FileImport($allowedExtensions); $this->setUploadedFile($fileImport, $extension); - self::assertNotEmpty($this->getErrors($fileImport)); + $errors = $this->getErrors($fileImport); + self::assertCount(1, $errors); + self::assertSame('validation.file.extension', $errors[0]->getMessage()); } public static function invalidExtensionsProvider(): Generator @@ -85,14 +89,6 @@ public static function invalidExtensionsProvider(): Generator yield ['csv', []]; } - public function testEmptyContentError(): void - { - $fileImport = new FileImport(['csv', 'xls', 'xlsx', 'ods']); - $fileImport->setFile($this->createUploadedFile('csv', false)); - - self::assertNotEmpty($this->getErrors($fileImport)); - } - private function setUploadedFile(FileImport $fileImport, string $fileExtension, bool $withContent = true): void { $fileImport->setFile($this->createUploadedFile($fileExtension, $withContent)); diff --git a/tests/Model/Matrix/MatrixFactoryTest.php b/tests/Model/Matrix/MatrixFactoryTest.php index c0ec15c..b2c2f31 100644 --- a/tests/Model/Matrix/MatrixFactoryTest.php +++ b/tests/Model/Matrix/MatrixFactoryTest.php @@ -18,13 +18,45 @@ class MatrixFactoryTest extends TestCase { + /** + * @dataProvider importFilesDataProvider + */ + public function testCreateFromRealUploadedFileSuccess(string $file, array $expectedHeader, int $expectedRecordNumber): void + { + $uploadedFile = new UploadedFile($file, $file); + $matrix = MatrixFactory::createFromUploadedFile($uploadedFile); + + $this->assertSame($expectedHeader, $matrix->getHeader()); + $this->assertCount($expectedRecordNumber, $matrix->getRecords()); + } + + public static function importFilesDataProvider(): Generator + { + yield [ + __DIR__ . '/../../Fixtures/Resources/test.csv', + ['test_private_property', 'test-private-property2', 'test_public_property', 'test_array_field'], + 30, + ]; + yield [ + __DIR__ . '/../../Fixtures/Resources/test.xls', + ['test_private_property', 'test-private-property2', 'test_public_property', 'test_array_field'], + 30, + ]; + yield [ + __DIR__ . '/../../Fixtures/Resources/test.xlsx', + ['test_private_property', 'test-private-property2', 'test_public_property', 'test_array_field'], + 30, + ]; + yield [__DIR__ . '/../../Fixtures/Resources/test_import_with_rows_without_cells.xlsx', ['Domain', 'Another', 'Comment'], 24]; + } + /** * @dataProvider dataProvider * * @throws SpreadsheetException * @throws Exception */ - public function testCreateFromUploadedFileSuccess(string $fileExtension, CsvDelimiterEnum $delimiter = CsvDelimiterEnum::COMMA): void + public function testCreateFromGeneratedUploadFileSuccess(string $fileExtension, CsvDelimiterEnum $delimiter = CsvDelimiterEnum::COMMA): void { foreach ($this->contentProvider() as $data) { $file = $this->createFile($fileExtension, $delimiter, \array_merge([$data['header']], $data['records']));