├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── php.yml ├── .gitignore ├── .semver ├── .valet-sh.yml ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── RoboFile.php ├── UPGRADE-1.0.0.md ├── UPGRADE-1.0.1.md ├── UPGRADE-1.0.2.md ├── UPGRADE-1.0.3.md ├── UPGRADE-1.1.0.md ├── UPGRADE-1.1.1.md ├── UPGRADE-2.0.0.md ├── UPGRADE-3.0.0.md ├── UPGRADE-3.1.0.md ├── UPGRADE-3.2.0.md ├── UPGRADE-3.2.1.md ├── UPGRADE-3.3.0.md ├── UPGRADE-3.3.1.md ├── UPGRADE-3.4.0.md ├── UPGRADE-3.4.1.md ├── UPGRADE-3.5.0.md ├── UPGRADE-3.5.1.md ├── UPGRADE-3.5.2.md ├── UPGRADE-3.6.0.md ├── UPGRADE-3.6.1.md ├── UPGRADE-3.6.2.md ├── UPGRADE-3.6.3.md ├── UPGRADE-3.7.0.md ├── UPGRADE-3.7.1.md ├── UPGRADE-3.7.2.md ├── UPGRADE-3.7.3.md ├── UPGRADE-3.7.4.md ├── UPGRADE-3.8.0.md ├── UPGRADE-3.8.1.md ├── UPGRADE-3.8.10.md ├── UPGRADE-3.8.11.md ├── UPGRADE-3.8.12.md ├── UPGRADE-3.8.13.md ├── UPGRADE-3.8.14.md ├── UPGRADE-3.8.15.md ├── UPGRADE-3.8.16.md ├── UPGRADE-3.8.17.md ├── UPGRADE-3.8.18.md ├── UPGRADE-3.8.19.md ├── UPGRADE-3.8.2.md ├── UPGRADE-3.8.20.md ├── UPGRADE-3.8.21.md ├── UPGRADE-3.8.22.md ├── UPGRADE-3.8.23.md ├── UPGRADE-3.8.24.md ├── UPGRADE-3.8.25.md ├── UPGRADE-3.8.26.md ├── UPGRADE-3.8.27.md ├── UPGRADE-3.8.3.md ├── UPGRADE-3.8.4.md ├── UPGRADE-3.8.5.md ├── UPGRADE-3.8.6.md ├── UPGRADE-3.8.7.md ├── UPGRADE-3.8.8.md ├── UPGRADE-3.8.9.md ├── UPGRADE-4.0.0.md ├── UPGRADE-5.0.0.md ├── behat.yml ├── bin ├── import-simple └── import-simple.php ├── bootstrap.php ├── composer.json ├── composer.lock ├── patches ├── 2.2.0.lock ├── 2.2.0 │ └── 01 │ │ ├── import-product-media-ee.patch │ │ ├── import-product-media.patch │ │ ├── import-product.patch │ │ └── import.patch └── 3.8.5 │ └── import-configuration-jms.patch ├── phpcs.xml ├── phpmd.xml ├── phpstan.neon ├── phpunit.xml ├── stub.php └── tests ├── acceptance ├── README.md ├── app │ └── etc │ │ └── configuration │ │ └── loggers.json ├── bin │ ├── docker-chrome-headless.sh │ └── google-chrome-macosx.sh ├── bootstrap │ ├── .env │ ├── AppKernel.php │ ├── Contexts │ │ ├── AttributeFeatureContext.php │ │ ├── AttributeSetFeatureContext.php │ │ ├── CategoryFeatureContext.php │ │ ├── ConsoleContext.php │ │ ├── CustomerAddressFeatureContext.php │ │ ├── CustomerFeatureContext.php │ │ ├── DockerEnvironment.php │ │ ├── FeatureContext.php │ │ ├── MsiFeatureContext.php │ │ ├── ProductFeatureContext.php │ │ └── TierPriceFeatureContext.php │ ├── DependencyInjection │ │ └── ImportExtension.php │ ├── ImportBundle.php │ └── Resources │ │ └── config │ │ ├── config.yml │ │ ├── config_dev.yml │ │ ├── config_test.yml │ │ └── services.xml └── features │ ├── attribute-set │ ├── add-update.feature │ ├── delete.feature │ └── replace.feature │ ├── attribute │ ├── add-update.feature │ ├── delete.feature │ └── replace.feature │ ├── category │ ├── add-update.feature │ ├── delete.feature │ └── replace.feature │ ├── customer-address │ └── add-update.feature │ ├── customer │ ├── add-update.feature │ ├── delete.feature │ └── replace.feature │ ├── msi │ └── add-update.feature │ ├── product │ ├── add-update.feature │ ├── delete.feature │ └── replace.feature │ └── tier-price │ ├── add-update.feature │ ├── delete.feature │ └── replace.feature ├── integration ├── AbstractIntegrationTest.php ├── Connection │ ├── PDOConnectionWrapper.php │ └── TestConnectionFactory.php ├── Product │ └── AddUpdateOperationTest.php ├── UrlRewrite │ └── AddUpdateOperationTest.php └── _files │ └── ce │ └── 2.3.x │ └── conf │ ├── product │ └── add-update-operation │ │ └── configuration.json │ ├── services.xml │ └── url-rewrite │ └── add-update-operation │ └── configuration.json └── unit └── .gitempty /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: techdivision/import-cli-simple 2 | 3 | on: 4 | push: 5 | branches: [ '**' ] 6 | pull_request: 7 | branches: [ '**' ] 8 | 9 | jobs: 10 | 11 | run: 12 | name: Build ${{ matrix.operating-system }} > ${{ matrix.php-versions }} 13 | runs-on: ${{ matrix.operating-system }} 14 | services: 15 | magento: 16 | image: docker://techdivision/magento2-ce:2.3.5 17 | env: 18 | MAGENTO_BASE_URL: magento.test 19 | ports: 20 | - 80:80 21 | - 443:443 22 | - 3306:3306 23 | strategy: 24 | matrix: 25 | operating-system: [ ubuntu-latest ] 26 | php-versions: [ '7.3', '7.4' ] 27 | 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v2 31 | 32 | - name: Setup PHP 33 | uses: shivammathur/setup-php@v2 34 | with: 35 | php-version: ${{ matrix.php-versions }} 36 | extensions: pdo, pdo-mysql, zip, xdebug-2.9.8 37 | 38 | - name: Validate Composer Files 39 | run: composer validate 40 | 41 | - name: Install Dependencies 42 | run: composer install --prefer-dist --no-progress --no-suggest 43 | 44 | - name: Prepare Docker Container 45 | run: vendor/bin/robo prepare:docker magento.test ${{ job.services.magento.id }} 46 | 47 | - name: Update local DNS configuration 48 | run: sudo echo "127.0.0.1 magento.test" | sudo tee -a /etc/hosts 49 | 50 | - name: Run Acceptance Tests 51 | run: MAGENTO_INSTALL_DIR=/var/www/dist MAGENTO_CONTAINER_NAME=${{ job.services.magento.id }} vendor/bin/robo run:tests-acceptance 52 | 53 | - name: Run Integration Tests 54 | run: MAGENTO_CONTAINER_NAME=${{ job.services.magento.id }} vendor/bin/robo run:tests-integration 55 | 56 | - name: Run Build 57 | run: vendor/bin/robo build 58 | 59 | - name: Download Scrutinizer-CI Binary 60 | run: wget https://scrutinizer-ci.com/ocular.phar 61 | 62 | - name: Post Coverage Data to Scrutinizer-CI 63 | run: php ocular.phar code-coverage:upload --format=php-clover target/reports/unit/clover.xml 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.dbeaver 3 | /.project 4 | /.settings 5 | /.buildpath 6 | /var 7 | /app 8 | /builds 9 | /dist 10 | /target 11 | /vendor 12 | /projects 13 | /instances 14 | /symfony/logs 15 | /symfony/cache 16 | /tests/integration/tmp 17 | /projects/.gitignore 18 | /projects/sample-data/tmp 19 | /projects/sample-data/magento2-sample-data 20 | /composer-pro.json 21 | .DS_Store 22 | /.phpunit.result.cache 23 | /magento2-docker-imgen 24 | -------------------------------------------------------------------------------- /.semver: -------------------------------------------------------------------------------- 1 | --- 2 | :major: 5 3 | :minor: 0 4 | :patch: 0 5 | :special: '' 6 | :metadata: '' 7 | -------------------------------------------------------------------------------- /.valet-sh.yml: -------------------------------------------------------------------------------- 1 | services: 2 | composer: 3 | version: 2 4 | php: 5 | version: 8.1 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 TechDivision GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pacemaker - Simple Console Tool 2 | 3 | [![Latest Stable Version](https://img.shields.io/packagist/v/techdivision/import-cli-simple.svg?style=flat-square)](https://packagist.org/packages/techdivision/import-cli-simple) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/techdivision/import-cli-simple.svg?style=flat-square)](https://packagist.org/packages/techdivision/import-cli-simple) 5 | [![License](https://img.shields.io/packagist/l/techdivision/import-cli-simple.svg?style=flat-square)](https://packagist.org/packages/techdivision/import-cli-simple) 6 | [![Build Status](https://github.com/techdivision/import-cli-simple/actions/workflows/php.yml/badge.svg)](https://github.com/techdivision/import-cli-simple/actions/workflows/php.yml) 7 | [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/techdivision/import-cli-simple/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/techdivision/import-cli-simple/?branch=master) 8 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/techdivision/import-cli-simple/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/techdivision/import-cli-simple/?branch=master) 9 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftechdivision%2Fimport-cli-simple.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftechdivision%2Fimport-cli-simple?ref=badge_shield) 10 | 11 | Please visit the Pacemaker [website](https://pacemaker.techdivision.com) or our [documentation (version 3.8.x)](https://docs.met.tdintern.de/pacemaker/1.3/) for additional information. 12 | Documentation for version 4.x coming soon. 13 | -------------------------------------------------------------------------------- /UPGRADE-1.0.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.0.0-beta71 to 1.0.0 2 | 3 | Updating from 1.0.0-beta71 to 1.0.0 doesn't have any impacts. Please read the apropriate UPGRADE-1.x.x files for updates lower as [1.0.0-beta70](UPGRADE-1.0.0-beta70.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-1.0.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.0.0 to 1.0.1 2 | 3 | Updating from 1.0.0 to 1.0.1 doesn't have any impacts. Please read the apropriate UPGRADE-1.x.x files for updates lower as [1.0.0](UPGRADE-1.0.0.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-1.0.2.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.0.1 to 1.0.2 2 | 3 | Updating from 1.0.1 to 1.0.2 doesn't have any impacts. Please read the apropriate UPGRADE-1.x.x files for updates lower as [1.0.1](UPGRADE-1.0.1.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-1.0.3.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.0.2 to 1.0.3 2 | 3 | Updating from 1.0.2 to 1.0.3 doesn't have any impacts. Please read the apropriate UPGRADE-1.x.x files for updates lower as [1.0.2](UPGRADE-1.0.2.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-1.1.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.0.3 to 1.1.0 2 | 3 | Updating from 1.0.3 to 1.1.0 doesn't have any impacts. Please read the apropriate UPGRADE-1.x.x files for updates lower as [1.0.3](UPGRADE-1.0.3.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-1.1.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.1.0 to 1.1.1 2 | 3 | Updating from 1.1.0 to 1.1.1 doesn't have any impacts. Please read the apropriate UPGRADE-1.x.x files for updates lower as [1.1.0](UPGRADE-1.1.0.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-2.0.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.1.1 to 2.0.0 2 | 3 | ## Configuration 4 | 5 | As a result of database schema changes between Magento 2.1.x to Magento 2.2.x, when upgrading from 1.1.1 to 2.0.0 the 6 | configuration needs the minor updates for the `replace` and the `add-update` operation. 7 | 8 | > When using the EE, the DI IDs below has to be suffixed with `_ee`, e. g. `import_product_variant.observer.variant.super.link` 9 | > has to be named `import_product_variant_ee.observer.variant.super.link`. 10 | 11 | ### Operation: *replace* 12 | 13 | For the replace operation the DI ID for the variant observer changes from `import_product_variant.observer.variant` 14 | to `import_product_variant.observer.variant.super.link`. Additionally a new observer, that adds the product relation 15 | has to be added. The DI ID is `import_product_variant.observer.variant.product.relation`. The configuration for the 16 | product variants should look something like 17 | 18 | ```json 19 | ... 20 | { 21 | "id": "import_product_variant.subject.variant", 22 | "prefix": "variants", 23 | "observers": [ 24 | { 25 | "pre-import": [ 26 | "import.observer.attribute.set" 27 | ] 28 | }, 29 | { 30 | "import": [ 31 | "import_product_variant.observer.variant.super.link", 32 | "import_product_variant.observer.variant.super.attribute", 33 | "import_product_variant.observer.variant.product.relation" 34 | ] 35 | } 36 | ] 37 | } 38 | ... 39 | ``` 40 | 41 | Beside the configuration for the variants, also the one for the bundles has to be changed. A new observer, that adds 42 | the product relation has to be added. The DI ID is `import_product_bundle.observer.bundle.product.relation`. The 43 | configuration for the product bundles should look something like 44 | 45 | ```json 46 | ... 47 | { 48 | "id": "import_product_bundle.subject.bundle", 49 | "prefix": "bundles", 50 | "observers": [ 51 | { 52 | "import": [ 53 | "import_product_bundle.observer.bundle.option", 54 | "import_product_bundle.observer.bundle.option.value", 55 | "import_product_bundle.observer.bundle.selection", 56 | "import_product_bundle.observer.bundle.selection.price", 57 | "import_product_bundle.observer.bundle.product.relation" 58 | ] 59 | } 60 | ] 61 | } 62 | ... 63 | ``` 64 | 65 | ### Operation: *add-update* 66 | 67 | For the replace operation the DI ID for the variant observer changes from `import_product_variant.observer.variant.update` 68 | to `import_product_variant.observer.variant.super.link.update`. Additionally a new observer, that adds/update the product 69 | relation has to be added. The DI ID is `import_product_variant.observer.variant.product.relation.update`. The configuration 70 | for the product variants should look something like 71 | 72 | ```json 73 | ... 74 | { 75 | "id": "import_product_variant.subject.variant", 76 | "prefix": "variants", 77 | "observers": [ 78 | { 79 | "pre-import": [ 80 | "import.observer.attribute.set" 81 | ] 82 | }, 83 | { 84 | "import": [ 85 | "import_product_variant.observer.variant.super.link.update", 86 | "import_product_variant.observer.variant.super.attribute.update", 87 | "import_product_variant.observer.variant.product.relation.update" 88 | ] 89 | } 90 | ] 91 | } 92 | ... 93 | ``` 94 | 95 | Beside the configuration for the variants, also the one for the bundles has to be changed. A new observer, that adds 96 | the product relation has to be added. The DI ID is `import_product_bundle.observer.bundle.product.relation.update`. 97 | The configuration for the product bundles should look something like 98 | 99 | ```json 100 | ... 101 | { 102 | "id": "import_product_bundle.subject.bundle", 103 | "prefix": "bundles", 104 | "observers": [ 105 | { 106 | "import": [ 107 | "import_product_bundle.observer.bundle.option.update", 108 | "import_product_bundle.observer.bundle.option.value.update", 109 | "import_product_bundle.observer.bundle.selection.update", 110 | "import_product_bundle.observer.bundle.selection.price.update", 111 | "import_product_bundle.observer.bundle.product.relation.update" 112 | ] 113 | } 114 | ] 115 | } 116 | ... 117 | ``` 118 | -------------------------------------------------------------------------------- /UPGRADE-3.0.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 2.0.0 to 3.0.0 2 | 3 | ## Composer 4 | 5 | As version 3.x is compatible with Magento 2.3.x, some composer dependencies changed. 6 | -------------------------------------------------------------------------------- /UPGRADE-3.1.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.0.1 to 3.1.0 2 | 3 | ## Category Import 4 | 5 | This version fixed the following issues from Github 6 | 7 | * [#21](https://github.com/techdivision/import-product-variant/issues/21) 8 | * [#50](https://github.com/techdivision/import-category/issues/50) 9 | * [#51](https://github.com/techdivision/import-category/issues/51) 10 | * [#52](https://github.com/techdivision/import-category/issues/52) 11 | * [#55](https://github.com/techdivision/import-category/issues/55) 12 | * [#56](https://github.com/techdivision/import-category/issues/56) 13 | 14 | As a result, the configuration file has to be updated with the following changes. 15 | 16 | ### Magento CE/EE 2.2 + 2.3 17 | 18 | The observer `import_category.observer.category.copy` has been added which copies the categories that has to be imported 19 | to a new CSV file before they have been removed with the `import_category.observer.clear.category` in the `replace` mode. 20 | 21 | So the configuration for the `replace` operation has to be extended with the new observer like 22 | 23 | ```json 24 | "subjects" : [ 25 | ..., 26 | { 27 | "id": "import_category_ee.subject.bunch", 28 | "identifier": "files", 29 | "file-resolver": { 30 | "prefix": "category-import" 31 | }, 32 | "observers": [ 33 | { 34 | "import": [ 35 | "import_category.observer.category.copy", 36 | "import_category.observer.clear.category" 37 | ] 38 | } 39 | ] 40 | }, 41 | ... 42 | ] 43 | ``` 44 | 45 | ### Magento EE 2.2 + 2.3 46 | 47 | #### Remove Existing Categories 48 | 49 | A new observer `import_category_ee.observer.clear.category` to remove categories has been added. This replaces the old 50 | one from the CE named `import_category.observer.clear.category` in the `delete` and `replace` operation, e. g. 51 | 52 | ```json 53 | "subjects" : [ 54 | ..., 55 | { 56 | "id": "import_category_ee.subject.bunch", 57 | "identifier": "files", 58 | "file-resolver": { 59 | "prefix": "category-import" 60 | }, 61 | "observers": [ 62 | { 63 | "import": [ 64 | "import_category.observer.category.copy", 65 | "import_category_ee.observer.clear.category" 66 | ] 67 | } 68 | ] 69 | }, 70 | ... 71 | ] 72 | ``` 73 | 74 | #### URL Key and Path Handling 75 | 76 | The second new observer `import_category_ee.observer.url.key.and.path` also replaces the old one from the CE named 77 | `import_category.observer.url.key.and.path`. This observer has to be replaced in the `replace` and `add-update` 78 | operations. For example, your configuration should look like this for the `replace` operation 79 | 80 | ```json 81 | ..., 82 | "observers" : [ 83 | { 84 | "pre-import": [ 85 | "import_category_ee.observer.url.key.and.path", 86 | "import.observer.attribute.set", 87 | "import.observer.additional.attribute", 88 | "import_category.observer.file.upload" 89 | ] 90 | }, 91 | { 92 | "import": [ 93 | "import_category_ee.observer.category", 94 | "import_category_ee.observer.category.attribute", 95 | "import_category.observer.category.url.rewrite" 96 | ] 97 | }, 98 | { 99 | "post-import": [ 100 | "import_category_ee.observer.clean.up" 101 | ] 102 | } 103 | ] 104 | ] 105 | ``` 106 | 107 | #### URL Rewrite Handling 108 | 109 | Finally the new observers `import_category_ee.observer.url.rewrite` and ` `import_category_ee.observer.url.rewrite` replaces 110 | the old one from the CE named `import_category.observer.url.rewrite` and `import_category.observer.url.rewrite.update`. As 111 | the observer above, this has to be replaced in the `replace` as well as the `add-update` operations, e. g. for the `add-update` 112 | operation your configuration should look like this 113 | 114 | ```json 115 | "subjects" : [ 116 | ..., 117 | { 118 | "id": "import_category_ee.subject.bunch", 119 | "identifier": "files", 120 | "file-resolver": { 121 | "prefix": "url-rewrite" 122 | }, 123 | "observers": [ 124 | { 125 | "import": [ 126 | "import_category_ee.observer.url.rewrite.update" 127 | ] 128 | } 129 | ] 130 | } 131 | ... 132 | ] 133 | ``` 134 | 135 | ## Configuration 136 | 137 | ### Commandline Option --params 138 | 139 | Up from version 3.1.0 it is possible to pass global params with the `--params` commandline option, e. g. 140 | 141 | ```sh 142 | vendor/bin/import-cli import:products \ 143 | --configuration=projects/sampele-data/ce/2.3.x/conf/products/techdivision-import.json \ 144 | --params='{ "params": [ { "website-country-mapping": { "DE": [ "de_de" ], "AT": [ "at_de", "at_de" ] } } ] }' 145 | ``` 146 | 147 | The params has to be JSON encoded and needs the same format as necessary to specifiy them in the main 148 | section of the configuration file. 149 | 150 | The params can then be loaded, for example, in an observer with the following code 151 | 152 | ```php 153 | class MyObserver extends AbstractObserver 154 | { 155 | 156 | /** 157 | * Example implementation to load a param from the global section of the configuration file. 158 | * 159 | * @param string $name The param name to load 160 | * 161 | * @return mixed The value 162 | */ 163 | protected loadParams($name) 164 | { 165 | return $this->getSubject()->getConfiguration()->getConfiguration()->getParam($name); 166 | } 167 | } 168 | ``` 169 | 170 | ### Dynamic Attribute Option Value/Swatch Creation 171 | 172 | Up from version 3.1.0 it is possible to have attribute option values + swatches to be created automatically. Therefore 173 | it is necessary to add additional callbacks to the product import configuration. This can be done with the 174 | `frontend-input-callbacks` configuration option like 175 | 176 | ```json 177 | "subjects" : [ 178 | ..., 179 | { 180 | "id": "import_product.subject.bunch", 181 | "identifier": "files", 182 | "prefix": "product-import", 183 | "filesystem-adapter" : { 184 | "id" : "import.adapter.filesystem.factory.league", 185 | "adapter" : { 186 | "type" : "League\\Flysystem\\Adapter\\Local" 187 | } 188 | }, 189 | "params" : [ 190 | { 191 | "copy-images" : false, 192 | "media-directory" : "projects/project-name/tmp" 193 | } 194 | ], 195 | "frontend-input-callbacks": [ 196 | { 197 | "select": [ 198 | "import_attribute.callback.create.select.option.value", 199 | "import_product.callback.select" 200 | ], 201 | "multiselect": [ 202 | "import_attribute.callback.create.multiselect.option.value", 203 | "import_product.callback.multiselect" 204 | ] 205 | } 206 | ], 207 | "observers": [ 208 | { 209 | "pre-import": [ 210 | "import_product.observer.pre.load.entity.id", 211 | "import_product_url_rewrite.observer.clear.url.rewrite", 212 | "import_product.observer.clear.product", 213 | "import.observer.attribute.set", 214 | "import.observer.additional.attribute", 215 | "import_product_ee.observer.url.key", 216 | "import_product.observer.file.upload", 217 | "import_product.observer.quality.and.stock.status" 218 | ] 219 | }, 220 | { 221 | "import": [ 222 | "import_product_ee.observer.product", 223 | "import_product.observer.product.website", 224 | "import_product.observer.category.product", 225 | "import_product.observer.product.inventory", 226 | "import_product_ee.observer.product.attribute", 227 | "import_product_url_rewrite.observer.product.url.rewrite", 228 | "import_product_variant.observer.product.variant", 229 | "import_product_bundle.observer.product.bundle", 230 | "import_product_media.observer.product.media", 231 | "import_product_link.observer.product.link" 232 | ] 233 | }, 234 | { 235 | "post-import": [ 236 | "import_product_ee.observer.clean.up" 237 | ] 238 | } 239 | ] 240 | }, 241 | ... 242 | ] 243 | ``` 244 | 245 | The callback configuration is the same for the CE as well as the EE. 246 | 247 | ### Default Attribute Value Observer 248 | 249 | Up to version 3.1.0 the default value for a selected option has been stored 1:1 into the database. This issue has been 250 | fixed with a new observer `import_attribute.observer.attribute.option.default` that has to be registered in the subject's 251 | configuration for the attribute import like 252 | 253 | ```json 254 | "subjects" : [ 255 | ..., 256 | { 257 | "id": "import_attribute.subject.option", 258 | "identifier": "files", 259 | "file-resolver": { 260 | "prefix": "option-import" 261 | }, 262 | "observers": [ 263 | { 264 | "import": [ 265 | "import_attribute.observer.attribute.option", 266 | "import_attribute.observer.attribute.option.value", 267 | "import_attribute.observer.attribute.option.swatch", 268 | "import_attribute.observer.attribute.option.default" 269 | ] 270 | } 271 | ] 272 | }, 273 | ... 274 | ] 275 | ``` 276 | 277 | Do not forget to register it for the `replace` as well as the `add-update` operation. 278 | 279 | ### Date + Number Converter 280 | 281 | The date + number conversion from a source date/number to the expected Magento 2 target format has been refactored. 282 | Therefore a DateConverter and a NumberConverter class has been introduced which replaces the `source-date-format` 283 | option in the configuration file. 284 | 285 | source and number conversion has to be configured on subject level, e. g. 286 | 287 | ```json 288 | "subjects" : [ 289 | { 290 | "id": "import.subject.move.files", 291 | "identifier": "move-files", 292 | "prefix": "product-import", 293 | "ok-file-needed": true,, 294 | "number-converter": { 295 | "locale": "de_DE" 296 | }, 297 | "date-converter": { 298 | "source-date-format": "Y-m-d H:i:s" 299 | } 300 | } 301 | ] 302 | ``` 303 | 304 | > Whith overriding the ID values of each node, it is possible to replace the converter implementation. 305 | 306 | ### File Resolver 307 | 308 | To make configuration of import file + OK file handling more flexible, the new FileResolver class has been added. 309 | 310 | Instead of defining the pattern of the files that has to be imported as well as their appropriate OK file on the 311 | subject level, each subject now has a `file-resolver` node that accepts the following new configuration options 312 | 313 | * prefix 314 | * filename 315 | * counter 316 | * suffix 317 | * ok-file-suffix 318 | * element-separator 319 | * pattern-elements 320 | 321 | These options allows a very flexible configuration for the pattern which decides which files has to be imported 322 | or not. You can find more detailed information in the documentation on the M2IF [website](http://www.m2if.com). 323 | 324 | It is important that the `prefix` option for all subjects which, until version < 3.1.*, looks like 325 | 326 | ```json 327 | "subjects" : [ 328 | { 329 | "id": "import.subject.move.files", 330 | "identifier": "move-files", 331 | "prefix": "product-import", 332 | "ok-file-needed": true 333 | } 334 | ] 335 | ``` 336 | 337 | has to be replaced with 338 | 339 | ```json 340 | "subjects" : [ 341 | { 342 | "id": "import.subject.move.files", 343 | "identifier": "move-files", 344 | "file-resolver": { 345 | "prefix": "product-import" 346 | }, 347 | "ok-file-needed": true 348 | } 349 | ] 350 | ``` 351 | 352 | A correct subject configuration up from version 3.1.* will look like 353 | 354 | ```json 355 | { 356 | ... 357 | "operations" : [ 358 | { 359 | "name" : "delete", 360 | "plugins" : [ 361 | { 362 | "id": "import.plugin.global.data" 363 | }, 364 | { 365 | "id": "import.plugin.subject", 366 | "subjects" : [ 367 | { 368 | "id": "import.subject.move.files", 369 | "identifier": "move-files", 370 | "file-resolver": { 371 | "prefix": "product-import" 372 | }, 373 | "ok-file-needed": true 374 | }, 375 | ... 376 | ] 377 | } 378 | ] 379 | } 380 | ] 381 | ... 382 | } 383 | ``` 384 | -------------------------------------------------------------------------------- /UPGRADE-3.2.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.1.0 to 3.2.0 2 | 3 | ## Configuration Grouped Product 4 | 5 | for the functionality to import grouped products, the new libraries [techdivision/import-product-grouped](https://github.com/techdivision/import-product-grouped) and [techdivision/import-product-grouped-ee](https://github.com/techdivision/import-product-grouped-ee) has been implemented. Additional major customizations in the library [techdivision/import-product-link](https://github.com/techdivision/import-product-link) has been necessary. 6 | 7 | The changes also affects the configuration for the CE, as well as for the EE. For the EE, checkout the diff of [PR48](https://github.com/techdivision/import-product-ee/pull/48/files) to see the necessary configuration changes. For the CE, have a look at [PR134](https://github.com/techdivision/import-product/pull/134/files) and [PR135](https://github.com/techdivision/import-product/pull/135/files). -------------------------------------------------------------------------------- /UPGRADE-3.2.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.2.0 to 3.2.1 2 | 3 | Updating from 3.2.0 to 3.2.1 doesn't have any impacts. Please read the apropriate UPGRADE-3.2.x files for updates lower as [3.2.0](UPGRADE-3.2.0.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-3.3.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.2.1 to 3.3.0 2 | 3 | ## MSI 4 | 5 | This version adds the functionality to import MSI inventory data. Either the data can be imported seperately from a CSV file in 6 | the [Magento 2 default format](https://github.com/magento-engcom/msi/wiki/MSI-Import-and-Export-Product-Data#csv-file-contents) 7 | or from a column containing the MSI inventory data in a serialized format. 8 | 9 | The MSI import is **NOT** activated by default. Actually there are two options to import the MSI inventory data. 10 | 11 | 1. Start the import process on the commandline by invoking the operation `import:products:inventory:msi` with the default MSI configuration 12 | 2. Add the column `inventory_source_items` to the default CSV file with the product import data and use the `--configuration` option 13 | the specify the [MSI example configuration](projects/sample-data/ce/2.3.x/conf/products/techdivision-import-inventory-msi.json) 14 | 15 | You can find more information about the MSI import on the M2IF [website](https://www.m2if.com). 16 | 17 | ## Tier Prices 18 | 19 | The option to import tier prices is a contribution of Klaas-Tido Rühl from REFUSION GmbH. 20 | 21 | The functionality has been fully integrated into the core and can be used as add on up from this version. As the MSI functionality, 22 | the tier price import is **NOT** activated by default. Actually there are also two options to import the product tier prices. 23 | 24 | 1. Start the import process on the commandline by invoking the operation `import:products:price:tier` with the default tier price configuration 25 | 2. Add the column `tier_prices` to the default CSV file with the product import data and use the `--configuration` option 26 | the specify the [tier price example configuration](projects/sample-data/ce/2.3.x/conf/products/techdivision-import-price-tier.json) 27 | 28 | You can find more information about the tier price import on the M2IF [website](https://www.m2if.com). 29 | 30 | ## Configuration 31 | 32 | ### Passing Serial 33 | 34 | With the new `--serial` parameter, it is possible to pass the unique serial, which will be used for the import 35 | process, as commandline option. Instead of generating an UUID, the passed serial will be used to generate the 36 | temporary import directory that contains the import artefacts as well as persisting the temporary import data 37 | during the import process. 38 | 39 | ### Events 40 | 41 | This release comes with additional events as well as the possiblity to register events on operation level. 42 | 43 | #### New Events 44 | 45 | The new events `app.set.up` and `app.tear.down` has been added. The events will be fired when the 46 | main application, that processes the import, will be started and shutdown. 47 | 48 | The event `app.tear.down` will also be invoked when an error occurs and the import process has 49 | been stopped unexpected. 50 | 51 | #### Events on Operation Level 52 | 53 | Beside to possiblity to register global events, up with this version it is possible to register events on 54 | operation level. This avoids execution of events that only provide functionality for a dedicated operation 55 | e. g. a listener that exports products only in add-update operation like 56 | 57 | ```json 58 | { 59 | ..., 60 | "operations" : [ 61 | { 62 | "name" : "add-update", 63 | "listeners" : [ 64 | { 65 | "subject.artefact.process.success" : [ 66 | "my_library.listeners.export.something" 67 | ] 68 | } 69 | ] 70 | }, 71 | ... 72 | ] 73 | } 74 | ``` 75 | 76 | ### New Listeners 77 | 78 | The functionality to initialize the registry with the basic import information has been extracted from the main 79 | application into generic listeners. Therefore it is mandatory to add these listeners to the configuration file, 80 | e. g. 81 | 82 | ```json 83 | { 84 | ..., 85 | "listeners" : [ 86 | { 87 | "app.set.up" : [ 88 | "import.listener.render.ansi.art", 89 | "import.listener.initialize.registry" 90 | ] 91 | }, 92 | { 93 | "app.tear.down" : [ 94 | "import.listener.clear.registry" 95 | ] 96 | } 97 | ], 98 | ... 99 | } 100 | ``` 101 | 102 | ### Composite Observers 103 | 104 | To make configuration less complex, this version comes with a set of composite observers that combines 105 | the default observers. Configuration of the composite observers will take place in the DI configuration 106 | wheras in in most scenarios the configuration doesn't need to be touched. 107 | 108 | As the composition of the observers didn't change since version 3.2.1, it's not mandatory to update the 109 | configuration file, but it's recommended. 110 | 111 | Have a look at the sample configuration files to see the necessary modifications. -------------------------------------------------------------------------------- /UPGRADE-3.3.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.3.0 to 3.3.1 2 | 3 | Updating from 3.3.0 to 3.3.1 doesn't have any impacts. Please read the apropriate UPGRADE-3.3.x files for updates lower as [3.3.0](UPGRADE-3.3.0.md) to this version. -------------------------------------------------------------------------------- /UPGRADE-3.4.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.3.1 to 3.4.0 2 | 3 | ## Configuration 4 | 5 | With version 3.4.0 configuration has been extended. 6 | 7 | ### Events 8 | 9 | This release comes with additional events as well as the possiblity to register events on operation level. 10 | 11 | Beside to possiblity to register global events, up with this version it is possible to register events on 12 | plugin and subject level. This avoids execution of events that only provide functionality for a dedicated 13 | plugin or subject. 14 | 15 | #### Plug-In Level 16 | 17 | On Plug-In Level, the following events has been added 18 | 19 | * plugin.process.start 20 | * plugin.process.success 21 | * plugin.process.failure 22 | 23 | The listeners will only be executed before, after or on failure of the plugin, for which the event has been 24 | configured for. Configuration will look like 25 | 26 | ```json 27 | { 28 | ... 29 | "operations" : [{ 30 | "name" : "add-update", 31 | "plugins" : [ 32 | { 33 | "id": "import.plugin.cache.warmer" 34 | }, 35 | { 36 | "id": "import.plugin.global.data" 37 | }, 38 | { 39 | "id": "import.plugin.subject", 40 | "listeners" : [ 41 | { 42 | "plugin.process.success" : [ 43 | "import_product_tier_price.listener.delete.obsolete.tier_prices" 44 | ] 45 | } 46 | ] 47 | ... 48 | } 49 | ] 50 | } 51 | ... 52 | ] 53 | } 54 | ``` 55 | 56 | #### Subject Level 57 | 58 | On Subject Level, the following events has been added 59 | 60 | * subject.import.start 61 | * subject.import.success 62 | * subject.import.failure 63 | * subject.export.start 64 | * subject.export.success 65 | * subject.export.failure 66 | 67 | As subjects are responsible for importing **AND** exporting artefacts, events for both steps has been added. 68 | 69 | The listeners will only be executed before, after or on failure of the subject, for which the event has been 70 | configured for. Configuration will look like 71 | 72 | ```json 73 | { 74 | ... 75 | "operations" : [{ 76 | "name" : "add-update", 77 | "plugins" : [ 78 | { 79 | "id": "import.plugin.cache.warmer" 80 | }, 81 | { 82 | "id": "import.plugin.global.data" 83 | }, 84 | { 85 | "id": "import.plugin.subject", 86 | "listeners" : [ 87 | { 88 | "plugin.process.success" : [ 89 | "import_product_tier_price.listener.delete.obsolete.tier_prices" 90 | ] 91 | } 92 | ] 93 | "subjects": [ 94 | ... 95 | { 96 | "id": "import_product_tier_price.subject.tier_price", 97 | "identifier": "files", 98 | "listeners": [ 99 | { 100 | "subject.import.success" : [ 101 | "import_product.listener.register.sku.to.pk.mapping" 102 | ] 103 | } 104 | ], 105 | "file-resolver": { 106 | "prefix": "product-import-tier-price" 107 | }, 108 | "observers": [ 109 | { 110 | "import": [ 111 | "import_product_tier_price.observer.tier_price.update" 112 | ] 113 | } 114 | ] 115 | }, 116 | }, 117 | ... 118 | ] 119 | ] 120 | } 121 | ... 122 | ] 123 | } 124 | ``` 125 | 126 | ## Cache 127 | 128 | Additional this version comes with a cache refactoring. Up from now, all classes with cache functionality, 129 | uses a PSR-6 compatible cache adapter. This cache adapter is a singleton implementation for each import, 130 | therefore each cache item has to use an unique identifier to avoid caching issues. 131 | 132 | -------------------------------------------------------------------------------- /UPGRADE-3.4.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.4.0 to 3.4.1 2 | 3 | Updating from 3.4.0 to 3.4.1 doesn't have any impacts. Please read the apropriate UPGRADE-3.4.x files for updates lower as [3.4.0](UPGRADE-3.4.0.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.5.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.4.1 to 3.5.0 2 | 3 | ## Configuration 4 | 5 | Version 3.5.0 comes with two new features. The first feature are aliases, that can be used to override classes that 6 | has been configured by the DI, the second one is a more convenient cache configuration. 7 | 8 | ### Aliases 9 | 10 | Aliases provides the possiblity to override DI values with a custom implementation. For example, the cache configuration 11 | uses the alias `cache.adapter` to define which cache implementation should be used. If the cache implementation should be 12 | replaced for some circumstances, the new implementation can be defined by overriding the alias target with 13 | 14 | ```json 15 | { 16 | ... 17 | "aliases": [ 18 | { 19 | "id": "cache.adapter", 20 | "target": "import_parallel.cache.adapter.generic" 21 | } 22 | ], 23 | ... 24 | } 25 | ``` 26 | 27 | By default, the cache uses a local adapter implementation that holds the cached data in an array. In the example above, 28 | the default cache implementation will be replace with a PSR-6 compatible adapter implementation that allows e. g. the 29 | usage of Redis. 30 | 31 | ### Cache 32 | 33 | Up from version 3.5.0, it is possible to configure the caches. 34 | 35 | It is important to know, that M2IF uses two different types of cache. First, a `static` cache adapter with the DI alias `cache.static` 36 | that can **NOT** be enabled or disabled. Second, the `configurable` cache type with the DI alias `cache.configurable` that **CAN** be 37 | enabled or disabled. Both of them supports TTL configuration whereas it is important that the TTL for the static cache type will be 38 | higher than the time the longest import runs in seconds, as the static cache contains the core data that'll be necessary to run the 39 | import and will always be enabled. 40 | 41 | To disable the `configurable` cache and set the TTL of the `static` cache type to 1440 seconds, add the following lines to 42 | the configuration 43 | 44 | ```json 45 | { 46 | ... 47 | "caches": [ 48 | { 49 | "type": "cache.static", 50 | "time": 1440 51 | }, 52 | { 53 | "type": "cache.configurable", 54 | "enable": false 55 | } 56 | ], 57 | ... 58 | } 59 | ``` 60 | 61 | > The registry uses the `static` cache type, whereas the repositories for example uses the `configurable` one. 62 | -------------------------------------------------------------------------------- /UPGRADE-3.5.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.5.0 to 3.5.1 2 | 3 | Updating from 3.5.0 to 3.5.1 doesn't have any impacts. Please read the apropriate UPGRADE-3.4.x files for updates lower as [3.5.0](UPGRADE-3.5.0.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.5.2.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.5.1 to 3.5.2 2 | 3 | Updating from 3.5.1 to 3.5.2 doesn't have any impacts. Please read the apropriate UPGRADE-3.4.x files for updates lower as [3.5.1](UPGRADE-3.5.1.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.6.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.5.2 to 3.6.0 2 | 3 | Updating from 3.5.2 to 3.6.0 doesn't have any impacts. Please read the apropriate UPGRADE-3.6.x files for updates lower as [3.5.2](UPGRADE-3.5.2.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.6.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.6.0 to 3.6.1 2 | 3 | Updating from 3.6.0 to 3.6.1 doesn't have any impacts. Please read the apropriate UPGRADE-3.6.x files for updates lower as [3.6.0](UPGRADE-3.6.0.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.6.2.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.6.1 to 3.6.2 2 | 3 | Updating from 3.6.1 to 3.6.2 doesn't have any impacts. Please read the apropriate UPGRADE-3.6.x files for updates lower as [3.6.1](UPGRADE-3.6.1.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.6.3.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.6.2 to 3.6.3 2 | 3 | Updating from 3.6.2 to 3.6.3 doesn't have any impacts. Please read the apropriate UPGRADE-3.6.x files for updates lower as [3.6.2](UPGRADE-3.6.2.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.7.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.6.3 to 3.7.0 2 | 3 | ## Configuration 4 | 5 | To make configuration more generic, the following DI identifiers for Magento 2 CE have been renamed 6 | 7 | * import_product.observer.composite.msi.delete > import_product_msi.observer.composite.delete 8 | * import_product.observer.composite.variant.replace > import_product_variant.observer.composite.replace 9 | * import_product.observer.composite.bundle.replace > import_product_bundle.observer.composite.replace 10 | * import_product.observer.composite.media.replace > import_product_media.observer.composite.replace 11 | * import_product.observer.composite.link.replace" class="import_product_link.observer.composite.replace 12 | * import_product.observer.composite.msi.replace > import_product_msi.observer.composite.replace 13 | * import_product.observer.composite.variant.add_update > import_product_variant.observer.composite.add_update 14 | * import_product.observer.composite.bundle.add_update > import_product_bundle.observer.composite.add_update 15 | * import_product.observer.composite.media.add_update > import_product_media.observer.composite.add_update 16 | * import_product.observer.composite.link.add_update" class="import_product_link.observer.composite.add_update 17 | * import_product.observer.composite.msi.add_update > import_product_msi.observer.composite.add_update 18 | 19 | For Magento 2 EE the following DI identifiers for the CE have been renamed 20 | 21 | * import_product_ee.observer.composite.variant.replace > import_product_variant_ee.observer.composite.replace 22 | * import_product_ee.observer.composite.bundle.replace > import_product_bundle_ee.observer.composite.replace 23 | * import_product_ee.observer.composite.media.replace > import_product_media_ee.observer.composite.replace 24 | * import_product_ee.observer.composite.link.replace > import_product_link_ee.observer.composite.replace 25 | * import_product_ee.observer.composite.variant.add_update > import_product_variant_ee.observer.composite.add_update 26 | * import_product_ee.observer.composite.bundle.add_update > import_product_bundle_ee.observer.composite.add_update 27 | * import_product_ee.observer.composite.media.add_update > import_product_media_ee.observer.composite.add_update 28 | * import_product_ee.observer.composite.link.add_update > import_product_link_ee.observer.composite.add_update 29 | 30 | > ATTENTION: Aliases has been created to ensure backwards compatibility. Those has been marked deprecated and will be removed with version 3.8.0 -------------------------------------------------------------------------------- /UPGRADE-3.7.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.7.0 to 3.7.1 2 | 3 | Updating from 3.7.0 to 3.7.1 doesn't have any impacts. Please read the apropriate UPGRADE-3.7.x files for updates lower as [3.7.0](UPGRADE-3.7.0.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.7.2.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.7.1 to 3.7.2 2 | 3 | Updating from 3.7.1 to 3.7.2 doesn't have any impacts. Please read the apropriate UPGRADE-3.7.x files for updates lower as [3.7.1](UPGRADE-3.7.1.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.7.3.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.7.2 to 3.7.3 2 | 3 | Updating from 3.7.2 to 3.7.3 doesn't have any impacts. Please read the apropriate UPGRADE-3.7.x files for updates lower as [3.7.2](UPGRADE-3.7.2.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.7.4.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.7.2 to 3.7.3 2 | 3 | Updating from 3.7.2 to 3.7.3 doesn't have any impacts. Please read the apropriate UPGRADE-3.7.x files for updates lower as [3.7.2](UPGRADE-3.7.2.md) to this version. 4 | 5 | -------------------------------------------------------------------------------- /UPGRADE-3.8.1.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.0 to 3.8.1 2 | 3 | Updating from 3.8.0 to 3.8.1 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.0](UPGRADE-3.8.0.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.10.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.9 to 3.8.10 2 | 3 | Updating from 3.8.9 to 3.8.10 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.9](UPGRADE-3.8.9.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.11.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.9 to 3.8.10 2 | 3 | Updating from 3.8.9 to 3.8.10 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.9](UPGRADE-3.8.9.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.12.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.11 to 3.8.12 2 | 3 | Updating from 3.8.11 to 3.8.12 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.11](UPGRADE-3.8.11.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.13.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.12 to 3.8.13 2 | 3 | Updating from 3.8.12 to 3.8.13 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.12](UPGRADE-3.8.12.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.14.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.13 to 3.8.14 2 | 3 | Updating from 3.8.13 to 3.8.14 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.13](UPGRADE-3.8.13.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.15.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.14 to 3.8.15 2 | 3 | Updating from 3.8.14 to 3.8.15 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.14](UPGRADE-3.8.14.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.16.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.15 to 3.8.16 2 | 3 | Updating from 3.8.15 to 3.8.16 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.15](UPGRADE-3.8.15.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.17.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.16 to 3.8.17 2 | 3 | Updating from 3.8.16 to 3.8.17 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.16](UPGRADE-3.8.16.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.18.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.17 to 3.8.18 2 | 3 | Updating from 3.8.17 to 3.8.18 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.17](UPGRADE-3.8.17.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.19.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.18 to 3.8.19 2 | 3 | Updating from 3.8.18 to 3.8.19 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.18](UPGRADE-3.8.18.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.2.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.1 to 3.8.2 2 | 3 | Updating from 3.8.1 to 3.8.2 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.1](UPGRADE-3.8.1.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.20.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.19 to 3.8.20 2 | 3 | Updating from 3.8.19 to 3.8.20 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.19](UPGRADE-3.8.19.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.21.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.20 to 3.8.21 2 | 3 | Updating from 3.8.20 to 3.8.21 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.20](UPGRADE-3.8.20.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.22.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.21 to 3.8.22 2 | 3 | Updating from 3.8.21 to 3.8.22 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.21](UPGRADE-3.8.21.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.23.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.22 to 3.8.23 2 | 3 | Updating from 3.8.22 to 3.8.23 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.22](UPGRADE-3.8.22.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.24.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.23 to 3.8.24 2 | 3 | Updating from 3.8.23 to 3.8.24 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.23](UPGRADE-3.8.23.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.25.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.24 to 3.8.25 2 | 3 | Updating from 3.8.24 to 3.8.25 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.24](UPGRADE-3.8.24.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.26.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.25 to 3.8.26 2 | 3 | Updating from 3.8.25 to 3.8.26 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.25](UPGRADE-3.8.25.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.27.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.26 to 3.8.27 2 | 3 | Updating from 3.8.26 to 3.8.27 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.26](UPGRADE-3.8.26.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.3.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.2 to 3.8.3 2 | 3 | Updating from 3.8.2 to 3.8.3 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.2](UPGRADE-3.8.2.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.4.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.3 to 3.8.4 2 | 3 | Updating from 3.8.3 to 3.8.4 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.3](UPGRADE-3.8.3.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.5.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.4 to 3.8.5 2 | 3 | Updating from 3.8.4 to 3.8.5 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.4](UPGRADE-3.8.4.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.6.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.5 to 3.8.6 2 | 3 | Updating from 3.8.5 to 3.8.6 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.5](UPGRADE-3.8.5.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.7.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.6 to 3.8.7 2 | 3 | Updating from 3.8.6 to 3.8.7 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.6](UPGRADE-3.8.6.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.8.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.7 to 3.8.8 2 | 3 | Updating from 3.8.7 to 3.8.8 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.7](UPGRADE-3.8.7.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-3.8.9.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 3.8.8 to 3.8.9 2 | 3 | Updating from 3.8.8 to 3.8.9 doesn't have any impacts. Please read the apropriate UPGRADE-3.8.x files for updates lower as [3.8.8](UPGRADE-3.8.8.md) to this version. 4 | -------------------------------------------------------------------------------- /UPGRADE-5.0.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 4.2.* to 5.0.0 2 | 3 | This is mostly a cleanup release offering stronger type guarantees for integrators with the changes, but there is no big new feature for end users. 4 | * The minimum supported PHP version is now 8.1.0. 5 | 6 | * Updating from 4.2.6 to 5.0.0 doesn't have any impacts. Please read the apropriate UPGRADE-5.0.0 files for updates lower as [5.0.0](UPGRADE-5.0.0.md) to this version. 7 | 8 | ## Upgrade composer dependencies 9 | replace dependency `"behat/symfony2-extension"` and `"behat/mink-goutte-driver"` with `"friends-of-behat/mink-browserkit-driver"` and 10 | `"friends-of-behat/symfony-extension"` 11 | ```json 12 | "doctrine/dbal" from "2.5.*" to "^4.0.4", 13 | "pdepend/pdepend" from "^2.5.2" to "^2.16.2", 14 | "phpmd/phpmd" from "^2.11.0" to "^2.15.0", 15 | "phpunit/phpunit" from "^6.5.0|^8.0.0|~9.5.0" to "~9.5.0|~10.5.0|~11.2.5", 16 | "sebastian/phpcpd" from "~3.0|~4.0|~5.0|~6.0" to "~6.0", 17 | "squizlabs/php_codesniffer" from "~3.4.0|~3.6.0" to "^3.10.1", 18 | "consolidation/robo" from "~1.0" to "^4.0.2", 19 | "mikey179/vfsstream" from "~1.0" to "~1.6.11", 20 | "symfony/dotenv" from "~3.0|~4.0" to "~6.0.19", 21 | "symfony/http-kernel" from "~2.0|~3.0|~4.0" to "~4.4.51", 22 | "behat/mink-extension" from "2.3.*" to "^2.3.1", 23 | "dmore/chrome-mink-driver" from "2.7.*" to "^2.9.3", 24 | "dmore/behat-chrome-extension" from "1.3.*" to "^1.4.0", 25 | "behat/mink-goutte-driver" from "1.1.*" to "dev-master, 26 | "phpcompatibility/php-compatibility" from "*" to "^9.3.5", 27 | 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /behat.yml: -------------------------------------------------------------------------------- 1 | default: 2 | extensions: 3 | DMore\ChromeExtension\Behat\ServiceContainer\ChromeExtension: ~ 4 | Behat\Symfony2Extension: 5 | kernel: 6 | class: TechDivision\Import\Cli\Simple\AppKernel 7 | Behat\MinkExtension: 8 | browser_name: chrome 9 | sessions: 10 | default: 11 | goutte: 12 | guzzle_parameters: 13 | verify: false 14 | production: 15 | chrome: 16 | api_url: http://localhost:9222 17 | suites: 18 | customer: 19 | paths: 20 | features: tests/acceptance/features/customer-address 21 | contexts: 22 | - TechDivision\Import\Cli\Simple\Contexts\CustomerAddressFeatureContext: ~ 23 | - TechDivision\Import\Cli\Simple\Contexts\FeatureContext: ~ 24 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 25 | customer_address: 26 | paths: 27 | features: tests/acceptance/features/customer 28 | contexts: 29 | - TechDivision\Import\Cli\Simple\Contexts\CustomerFeatureContext: ~ 30 | - TechDivision\Import\Cli\Simple\Contexts\FeatureContext: ~ 31 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 32 | attribute_set: 33 | paths: 34 | features: tests/acceptance/features/attribute-set 35 | contexts: 36 | - TechDivision\Import\Cli\Simple\Contexts\AttributeSetFeatureContext: ~ 37 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 38 | attribute: 39 | paths: 40 | features: tests/acceptance/features/attribute 41 | contexts: 42 | - TechDivision\Import\Cli\Simple\Contexts\AttributeFeatureContext: ~ 43 | - TechDivision\Import\Cli\Simple\Contexts\AttributeSetFeatureContext: ~ 44 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 45 | category: 46 | paths: 47 | features: tests/acceptance/features/category 48 | contexts: 49 | - TechDivision\Import\Cli\Simple\Contexts\CategoryFeatureContext: ~ 50 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 51 | product: 52 | paths: 53 | features: tests/acceptance/features/product 54 | contexts: 55 | - TechDivision\Import\Cli\Simple\Contexts\AttributeSetFeatureContext: ~ 56 | - TechDivision\Import\Cli\Simple\Contexts\AttributeFeatureContext: ~ 57 | - TechDivision\Import\Cli\Simple\Contexts\CategoryFeatureContext: ~ 58 | - TechDivision\Import\Cli\Simple\Contexts\ProductFeatureContext: ~ 59 | - TechDivision\Import\Cli\Simple\Contexts\FeatureContext: ~ 60 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 61 | tier_price: 62 | paths: 63 | features: tests/acceptance/features/tier-price 64 | contexts: 65 | - TechDivision\Import\Cli\Simple\Contexts\TierPriceFeatureContext: ~ 66 | - TechDivision\Import\Cli\Simple\Contexts\AttributeSetFeatureContext: ~ 67 | - TechDivision\Import\Cli\Simple\Contexts\AttributeFeatureContext: ~ 68 | - TechDivision\Import\Cli\Simple\Contexts\CategoryFeatureContext: ~ 69 | - TechDivision\Import\Cli\Simple\Contexts\ProductFeatureContext: ~ 70 | - TechDivision\Import\Cli\Simple\Contexts\FeatureContext: ~ 71 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 72 | msi: 73 | paths: 74 | features: tests/acceptance/features/msi 75 | contexts: 76 | - TechDivision\Import\Cli\Simple\Contexts\MsiFeatureContext: ~ 77 | - TechDivision\Import\Cli\Simple\Contexts\AttributeSetFeatureContext: ~ 78 | - TechDivision\Import\Cli\Simple\Contexts\AttributeFeatureContext: ~ 79 | - TechDivision\Import\Cli\Simple\Contexts\CategoryFeatureContext: ~ 80 | - TechDivision\Import\Cli\Simple\Contexts\ProductFeatureContext: ~ 81 | - TechDivision\Import\Cli\Simple\Contexts\FeatureContext: ~ 82 | - TechDivision\Import\Cli\Simple\Contexts\ConsoleContext: ~ 83 | -------------------------------------------------------------------------------- /bin/import-simple: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 9 | * @copyright 2021 TechDivision GmbH 10 | * @license https://opensource.org/licenses/MIT 11 | * @link https://github.com/techdivision/import-cli-simple 12 | * @link http://www.techdivision.com 13 | */ 14 | 15 | // initialize the actual directory 16 | $actualDirectory = dirname(__DIR__); 17 | 18 | // initialize the possible vendor directories 19 | $possibleVendorDirs = array( 20 | array($actualDirectory, sprintf('%s/techdivision/import-cli-simple/bootstrap.php', $actualDirectory)), // when installed as library in vendor/bin/pacemaker (QUICKFIX: this should never happen, because files has been copied and not symlinked) 21 | array($actualDirectory . '/vendor', sprintf('%s/bootstrap.php', $actualDirectory)), // when called directly from the root directory of this library 22 | array(dirname(dirname($actualDirectory)), sprintf('%s/bootstrap.php', $actualDirectory)) // when installed as library in directory vendor/techdivision/import-cli-simple/bin/pacemaker 23 | ); 24 | 25 | // try to locate the actual vendor directory 26 | $loaded = false; 27 | foreach ($possibleVendorDirs as $possibleVendorDir) { 28 | // list vendor directory and related boostrap file 29 | list ($vendorDir, $boostrapFile) = $possibleVendorDir; 30 | // if the autoload.php exsists, we've found the vendor directory 31 | if (file_exists($file = sprintf('%s/autoload.php', $vendorDir))) { 32 | require $file; 33 | $loaded = true; 34 | break; 35 | } 36 | } 37 | 38 | // stop processing, if NO vendor directory has been detected 39 | if (!$loaded) { 40 | die( 41 | 'You need to set up the project dependencies using the following commands:' . PHP_EOL . 42 | 'wget http://getcomposer.org/composer.phar' . PHP_EOL . 43 | 'php composer.phar install' . PHP_EOL 44 | ); 45 | } 46 | 47 | // bootstrap and run the application 48 | require $boostrapFile; 49 | -------------------------------------------------------------------------------- /bootstrap.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2016 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | // import the used classes 22 | use Symfony\Component\Config\FileLocator; 23 | use Symfony\Component\DependencyInjection\ContainerBuilder; 24 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; 25 | use TechDivision\Import\Cli\Utils\DependencyInjectionKeys; 26 | 27 | // initialize the DI container and set the vendor directory 28 | $container = new ContainerBuilder(); 29 | $container->setParameter(DependencyInjectionKeys::CONFIGURATION_VENDOR_DIR, $vendorDir); 30 | $container->setParameter(DependencyInjectionKeys::CONFIGURATION_BASE_DIR, dirname(__FILE__)); 31 | 32 | // initialize the default loader and load the DI configuration for the this library 33 | $defaultLoader = new XmlFileLoader($container, new FileLocator($vendorDir)); 34 | $defaultLoader->load(implode(DIRECTORY_SEPARATOR, array($vendorDir,'techdivision', 'import-cli', 'symfony', 'Resources', 'config', 'services.xml'))); 35 | 36 | // initialize and run the application 37 | $statusCode = $container->get(DependencyInjectionKeys::APPLICATION) 38 | ->run( 39 | $container->get(DependencyInjectionKeys::INPUT), 40 | $container->get(DependencyInjectionKeys::OUTPUT) 41 | ); 42 | 43 | // stop and render the status code 44 | exit($statusCode); 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "techdivision/import-cli-simple", 3 | "description": "CLI providing single-threaded Magento 2 importing functionality based on Pacemaker", 4 | "license": "MIT", 5 | "require": { 6 | "php": "^8.1", 7 | "techdivision/import": "^18.0.0", 8 | "techdivision/import-cli": "^13.0.0", 9 | "techdivision/import-app-simple": "^19.0.0", 10 | "techdivision/import-configuration-jms": "^18.0.1", 11 | "techdivision/import-ee": "^17.0.0", 12 | "techdivision/import-attribute": "^23.0.0", 13 | "techdivision/import-attribute-set": "^18.0.0", 14 | "techdivision/import-customer": "^18.0.0", 15 | "techdivision/import-customer-address": "^18.0.0", 16 | "techdivision/import-category": "^22.0.0", 17 | "techdivision/import-category-ee": "^23.0.0", 18 | "techdivision/import-product": "^26.1.0", 19 | "techdivision/import-product-msi": "^21.0.0", 20 | "techdivision/import-product-tier-price": "^19.0.0", 21 | "techdivision/import-product-url-rewrite": "^26.0.0", 22 | "techdivision/import-product-link": "^26.0.0", 23 | "techdivision/import-product-media": "^28.0.0", 24 | "techdivision/import-product-bundle": "^26.0.0", 25 | "techdivision/import-product-variant": "^26.0.0", 26 | "techdivision/import-product-grouped": "^20.0.0", 27 | "techdivision/import-product-ee": "^27.1.0", 28 | "techdivision/import-product-link-ee": "^28.0.0", 29 | "techdivision/import-product-media-ee": "^29.0.0", 30 | "techdivision/import-product-bundle-ee": "^28.0.0", 31 | "techdivision/import-product-variant-ee": "^28.0.0", 32 | "techdivision/import-product-grouped-ee": "^22.0.0", 33 | "techdivision/import-cache": "^2.0.0", 34 | "techdivision/import-dbal": "^2.0.0", 35 | "techdivision/import-serializer": "^2.0.0", 36 | "techdivision/import-converter": "^12.0.0", 37 | "techdivision/import-converter-ee": "^12.0.0", 38 | "techdivision/import-converter-product-category": "^11.0.0", 39 | "techdivision/import-converter-product-attribute": "^11.0.0", 40 | "techdivision/import-converter-customer-attribute": "^4.0.0", 41 | "techdivision/import-serializer-csv": "^2.0.0", 42 | "techdivision/import-dbal-collection": "^2.0.0", 43 | "techdivision/import-cache-collection": "^2.0.0", 44 | "techdivision/import-configuration": "^6.0.0", 45 | "egulias/email-validator": "^3.2.0" 46 | }, 47 | "require-dev": { 48 | "doctrine/dbal": "^4.0.4", 49 | "pdepend/pdepend": "^2.16.2", 50 | "phpmd/phpmd": "^2.15.0", 51 | "phpunit/phpunit": "~9.5.0|~10.5.0|~11.2.5", 52 | "sebastian/phpcpd": "~6.0", 53 | "squizlabs/php_codesniffer": "^3.10.1", 54 | "consolidation/robo": "~4.0.2", 55 | "mikey179/vfsstream": "~1.6.11", 56 | "symfony/dotenv": "~6.0.19", 57 | "symfony/http-kernel": "~4.4.51", 58 | "behat/mink-extension": "^2.3.1", 59 | "dmore/chrome-mink-driver": "^2.9.3", 60 | "dmore/behat-chrome-extension": "^1.4.0", 61 | "friends-of-behat/mink-browserkit-driver": "^1.5.0", 62 | "friends-of-behat/symfony-extension": "^2.3.0", 63 | "phpcompatibility/php-compatibility": "^9.3.5", 64 | "techdivision/import-sample-data": "^4.3.5" 65 | }, 66 | "authors": [ 67 | { 68 | "name": "Tim Wagner", 69 | "email": "t.wagner@techdivision.com" 70 | }, 71 | { 72 | "name": "Martin Eisenführer", 73 | "email": "m.eisenfuehrer@techdivision.com" 74 | }, 75 | { 76 | "name": "Kenza Yamlahi", 77 | "email": "k.yamlahi@techdivision.com" 78 | }, 79 | { 80 | "name": "Vadim Justus", 81 | "email": "v.justus@techdivision.com" 82 | }, 83 | { 84 | "name": "Marcus Döllerer", 85 | "email": "m.doellerer@techdivision.com" 86 | }, 87 | { 88 | "name": "David Führ", 89 | "email": "d.fuer@techdivision.com" 90 | } 91 | ], 92 | "autoload-dev": { 93 | "psr-4": { 94 | "TechDivision\\Import\\Cli\\Simple\\": [ 95 | "tests/integration/", 96 | "tests/acceptance/bootstrap/" 97 | ] 98 | } 99 | }, 100 | "bin": [ 101 | "bin/import-simple", 102 | "bin/import-simple.php" 103 | ], 104 | "config": { 105 | "allow-plugins": { 106 | "composer/package-versions-deprecated": false 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /patches/2.2.0/01/import-product-media-ee.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/Observers/EeMediaGalleryUpdateObserver.php b/src/Observers/EeMediaGalleryUpdateObserver.php 2 | index eaf339e..1489ca6 100644 3 | --- a/src/Observers/EeMediaGalleryUpdateObserver.php 4 | +++ b/src/Observers/EeMediaGalleryUpdateObserver.php 5 | @@ -34,6 +34,29 @@ use TechDivision\Import\Product\Media\Ee\Utils\MemberNames; 6 | class EeMediaGalleryUpdateObserver extends EeMediaGalleryObserver 7 | { 8 | 9 | + /** 10 | + * Initialize the product media gallery with the passed attributes and returns an instance. 11 | + * 12 | + * @param array $attr The product media gallery attributes 13 | + * 14 | + * @return array The initialized product media gallery 15 | + */ 16 | + protected function initializeProductMediaGallery(array $attr) 17 | + { 18 | + 19 | + // load the value and the attribute ID 20 | + $value = $attr[MemberNames::VALUE]; 21 | + $attributeId = $attr[MemberNames::ATTRIBUTE_ID]; 22 | + 23 | + // query whether the product media gallery entity already exists or not 24 | + if ($entity = $this->loadProductMediaGallery($attributeId, $value)) { 25 | + return $this->mergeEntity($entity, $attr); 26 | + } 27 | + 28 | + // simply return the attributes 29 | + return $attr; 30 | + } 31 | + 32 | /** 33 | * Initialize the product media gallery value to entity with the passed attributes and returns an instance. 34 | * 35 | @@ -57,6 +80,19 @@ class EeMediaGalleryUpdateObserver extends EeMediaGalleryObserver 36 | return $attr; 37 | } 38 | 39 | + /** 40 | + * Load's the product media gallery with the passed attribute ID + value. 41 | + * 42 | + * @param integer $attributeId The attribute ID of the product media gallery to load 43 | + * @param string $value The value of the product media gallery to load 44 | + * 45 | + * @return array The product media gallery 46 | + */ 47 | + protected function loadProductMediaGallery($attributeId, $value) 48 | + { 49 | + return $this->getProductMediaProcessor()->loadProductMediaGallery($attributeId, $value); 50 | + } 51 | + 52 | /** 53 | * Load's the product media gallery with the passed value/entity ID. 54 | * 55 | diff --git a/src/Observers/EeMediaGalleryValueObserver.php b/src/Observers/EeMediaGalleryValueObserver.php 56 | index 7e7850f..cc05a0e 100644 57 | --- a/src/Observers/EeMediaGalleryValueObserver.php 58 | +++ b/src/Observers/EeMediaGalleryValueObserver.php 59 | @@ -58,6 +58,9 @@ class EeMediaGalleryValueObserver extends MediaGalleryValueObserver 60 | // load the image label 61 | $imageLabel = $this->getValue(ColumnKeys::IMAGE_LABEL); 62 | 63 | + // load the flag that decides whether or not an image should be hidden on product page 64 | + $hideFromProductPage = $this->getValue(ColumnKeys::HIDE_FROM_PRODUCT_PAGE); 65 | + 66 | // prepare the media gallery value 67 | return $this->initializeEntity( 68 | array( 69 | @@ -66,7 +69,7 @@ class EeMediaGalleryValueObserver extends MediaGalleryValueObserver 70 | MemberNames::ROW_ID => $parentId, 71 | MemberNames::LABEL => $imageLabel, 72 | MemberNames::POSITION => $position, 73 | - MemberNames::DISABLED => 0 74 | + MemberNames::DISABLED => $hideFromProductPage 75 | ) 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /patches/2.2.0/01/import-product.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/Observers/FileUploadObserver.php b/src/Observers/FileUploadObserver.php 2 | index 2bbbc92..f49be2d 100644 3 | --- a/src/Observers/FileUploadObserver.php 4 | +++ b/src/Observers/FileUploadObserver.php 5 | @@ -48,6 +48,9 @@ class FileUploadObserver extends AbstractProductImportObserver 6 | if ($this->getSubject()->getConfiguration()->hasParam(ConfigurationKeys::COPY_IMAGES)) { 7 | // query whether or not we've to upload the image files 8 | if ($this->getSubject()->getConfiguration()->getParam(ConfigurationKeys::COPY_IMAGES)) { 9 | + // load the subject 10 | + $subject = $this->getSubject(); 11 | + 12 | // initialize the array for the actual images 13 | $actualImageNames = array(); 14 | 15 | @@ -64,26 +67,33 @@ class FileUploadObserver extends AbstractProductImportObserver 16 | continue; 17 | } 18 | 19 | - // upload the file and set the new image path 20 | - $imagePath = $this->getSubject()->uploadFile($imageName); 21 | - 22 | - // log a message that the image has been copied 23 | - $this->getSubject() 24 | - ->getSystemLogger() 25 | - ->debug( 26 | - sprintf( 27 | - 'Successfully copied image type %s with name %s => %s', 28 | - $imageColumnName, 29 | - $imageName, 30 | - $imagePath 31 | - ) 32 | - ); 33 | - 34 | - // override the image path with the new one 35 | - $this->setValue($imageColumnName, $imagePath); 36 | - 37 | + try { 38 | + // upload the file and set the new image path 39 | + $imagePath = $this->getSubject()->uploadFile($imageName); 40 | + // override the image path with the new one 41 | + $this->setValue($imageColumnName, $imagePath); 42 | // add the image to the list with processed images 43 | - $actualImageNames[$imageName] = $imagePath; 44 | + $actualImageNames[$imageName] = $imagePath; 45 | + 46 | + // log a message that the image has been copied 47 | + $this->getSubject() 48 | + ->getSystemLogger() 49 | + ->debug( 50 | + sprintf( 51 | + 'Successfully copied image type %s with name %s => %s', 52 | + $imageColumnName, 53 | + $imageName, 54 | + $imagePath 55 | + ) 56 | + ); 57 | + } catch (\Exception $e) { 58 | + // query whether or not debug mode has been enabled 59 | + if ($subject->isDebugMode()) { 60 | + $subject->getSystemLogger()->warning($subject->appendExceptionSuffix($e->getMessage())); 61 | + } else { 62 | + throw $subject->wrapException(array($imageColumnName), $e); 63 | + } 64 | + } 65 | } 66 | 67 | // query whether or not, we've additional images 68 | @@ -99,25 +109,32 @@ class FileUploadObserver extends AbstractProductImportObserver 69 | continue; 70 | } 71 | 72 | - // upload the file and set the new image path 73 | - $imagePath = $this->getSubject()->uploadFile($additionalImageName); 74 | - 75 | - // log a message that the image has been copied 76 | - $this->getSubject() 77 | - ->getSystemLogger() 78 | - ->debug( 79 | - sprintf( 80 | - 'Successfully copied additional image wth name %s => %s', 81 | - $additionalImageName, 82 | - $imagePath 83 | - ) 84 | - ); 85 | - 86 | - // override the image path 87 | - $additionalImages[$key] = $imagePath; 88 | - 89 | - // add the image to the list with processed images 90 | - $actualImageNames[$additionalImageName] = $imagePath; 91 | + try { 92 | + // upload the file and set the new image path 93 | + $imagePath = $this->getSubject()->uploadFile($additionalImageName); 94 | + // override the image path 95 | + $additionalImages[$key] = $imagePath; 96 | + // add the image to the list with processed images 97 | + $actualImageNames[$additionalImageName] = $imagePath; 98 | + 99 | + // log a message that the image has been copied 100 | + $this->getSubject() 101 | + ->getSystemLogger() 102 | + ->debug( 103 | + sprintf( 104 | + 'Successfully copied additional image wth name %s => %s', 105 | + $additionalImageName, 106 | + $imagePath 107 | + ) 108 | + ); 109 | + } catch (\Exception $e) { 110 | + // query whether or not debug mode has been enabled 111 | + if ($subject->isDebugMode()) { 112 | + $subject->getSystemLogger()->warning($subject->appendExceptionSuffix($e->getMessage())); 113 | + } else { 114 | + throw $subject->wrapException(array(ColumnKeys::ADDITIONAL_IMAGES), $e); 115 | + } 116 | + } 117 | } 118 | 119 | // override the image paths with the new one 120 | -------------------------------------------------------------------------------- /patches/2.2.0/01/import.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/Repositories/SqlStatementRepository.php b/src/Repositories/SqlStatementRepository.php 2 | index fbbc7a8..92af0c9 100644 3 | --- a/src/Repositories/SqlStatementRepository.php 4 | +++ b/src/Repositories/SqlStatementRepository.php 5 | @@ -419,7 +419,9 @@ class SqlStatementRepository extends AbstractSqlStatementRepository 6 | is_default = :is_default 7 | WHERE website_id = :website_id', 8 | SqlStatementKeys::IMAGE_TYPES => 9 | - 'SELECT main_table.attribute_code 10 | + 'SELECT main_table.attribute_code, 11 | + main_table.attribute_id, 12 | + main_table.is_user_defined 13 | FROM eav_attribute AS main_table 14 | INNER JOIN eav_entity_type AS entity_type 15 | ON main_table.entity_type_id = entity_type.entity_type_id 16 | @@ -429,7 +431,11 @@ class SqlStatementRepository extends AbstractSqlStatementRepository 17 | ON main_table.attribute_id = additional_table.attribute_id 18 | WHERE (entity_type_code = \'catalog_product\') 19 | AND (frontend_input = \'media_image\') 20 | - GROUP BY main_table.attribute_code' 21 | + GROUP BY main_table.attribute_code, 22 | + main_table.attribute_id, 23 | + main_table.is_user_defined 24 | + ORDER BY main_table.is_user_defined, 25 | + main_table.attribute_id' 26 | ); 27 | 28 | /** 29 | diff --git a/src/Subjects/FileUploadSubjectInterface.php b/src/Subjects/FileUploadSubjectInterface.php 30 | index ff099bd..fbd25dd 100644 31 | --- a/src/Subjects/FileUploadSubjectInterface.php 32 | +++ b/src/Subjects/FileUploadSubjectInterface.php 33 | @@ -32,6 +32,52 @@ namespace TechDivision\Import\Subjects; 34 | interface FileUploadSubjectInterface extends FilesystemSubjectInterface 35 | { 36 | 37 | + /** 38 | + * Adds the mapping from the filename => new filename. 39 | + * 40 | + * @param string $filename The filename 41 | + * @param string $newFilename The new filename 42 | + * 43 | + * @return string The mapped filename 44 | + */ 45 | + public function addImageMapping($filename, $newFilename); 46 | + 47 | + /** 48 | + * Returns the mapped filename (which is the new filename). 49 | + * 50 | + * @param string $filename The filename to map 51 | + * 52 | + * @return string The mapped filename 53 | + */ 54 | + public function getImageMapping($filename); 55 | + 56 | + /** 57 | + * Returns TRUE, if the passed filename has already been mapped. 58 | + * 59 | + * @param string $filename The filename to query for 60 | + * 61 | + * @return boolean TRUE if the filename has already been mapped, else FALSE 62 | + */ 63 | + public function imageHasBeenMapped($filename); 64 | + 65 | + /** 66 | + * Returns TRUE, if the passed filename has NOT been mapped yet. 67 | + * 68 | + * @param string $filename The filename to query for 69 | + * 70 | + * @return boolean TRUE if the filename has NOT been mapped yet, else FALSE 71 | + */ 72 | + public function imageHasNotBeenMapped($filename); 73 | + 74 | + /** 75 | + * Returns the original filename for passed one (which is the new filename). 76 | + * 77 | + * @param string $newFilename The new filename to return the original one for 78 | + * 79 | + * @return string The original filename 80 | + */ 81 | + public function getInversedImageMapping($newFilename); 82 | + 83 | /** 84 | * Return's the flag to copy images or not. 85 | * 86 | @@ -54,9 +100,9 @@ interface FileUploadSubjectInterface extends FilesystemSubjectInterface 87 | public function getImagesFileDir(); 88 | 89 | /** 90 | - * Get new file name if the same is already exists. 91 | + * Get new file name, if a filename with the same name already exists. 92 | * 93 | - * @param string $targetFilename The name of the exisising files 94 | + * @param string $targetFilename The name of target file 95 | * 96 | * @return string The new filename 97 | */ 98 | diff --git a/src/Subjects/FileUploadTrait.php b/src/Subjects/FileUploadTrait.php 99 | index 83cbfdf..03a6913 100644 100 | --- a/src/Subjects/FileUploadTrait.php 101 | +++ b/src/Subjects/FileUploadTrait.php 102 | @@ -46,6 +46,13 @@ trait FileUploadTrait 103 | */ 104 | protected $imagesFileDir; 105 | 106 | + /** 107 | + * Contains the mappings for the image names that has been uploaded (old => new image name). 108 | + * 109 | + * @var array 110 | + */ 111 | + protected $imageMappings = array(); 112 | + 113 | /** 114 | * The flag whether to copy the images or not. 115 | * 116 | @@ -120,9 +127,84 @@ trait FileUploadTrait 117 | } 118 | 119 | /** 120 | - * Get new file name if the same is already exists. 121 | + * Adds the mapping from the filename => new filename. 122 | + * 123 | + * @param string $filename The filename 124 | + * @param string $newFilename The new filename 125 | + * 126 | + * @return void 127 | + */ 128 | + public function addImageMapping($filename, $newFilename) 129 | + { 130 | + $this->imageMappings[$filename] = $newFilename; 131 | + } 132 | + 133 | + /** 134 | + * Returns the mapped filename (which is the new filename). 135 | + * 136 | + * @param string $filename The filename to map 137 | + * 138 | + * @return string The mapped filename 139 | + */ 140 | + public function getImageMapping($filename) 141 | + { 142 | + 143 | + // query whether or not a mapping is available, if yes return the mapped name 144 | + if (isset($this->imageMappings[$filename])) { 145 | + return $this->imageMappings[$filename]; 146 | + } 147 | + 148 | + // return the passed filename otherwise 149 | + return $filename; 150 | + } 151 | + 152 | + /** 153 | + * Returns TRUE, if the passed filename has already been mapped. 154 | + * 155 | + * @param string $filename The filename to query for 156 | + * 157 | + * @return boolean TRUE if the filename has already been mapped, else FALSE 158 | + */ 159 | + public function imageHasBeenMapped($filename) 160 | + { 161 | + return isset($this->imageMappings[$filename]); 162 | + } 163 | + 164 | + /** 165 | + * Returns TRUE, if the passed filename has NOT been mapped yet. 166 | * 167 | - * @param string $targetFilename The name of the exisising files 168 | + * @param string $filename The filename to query for 169 | + * 170 | + * @return boolean TRUE if the filename has NOT been mapped yet, else FALSE 171 | + */ 172 | + public function imageHasNotBeenMapped($filename) 173 | + { 174 | + return !isset($this->imageMappings[$filename]); 175 | + } 176 | + 177 | + /** 178 | + * Returns the original filename for passed one (which is the new filename). 179 | + * 180 | + * @param string $newFilename The new filename to return the original one for 181 | + * 182 | + * @return string The original filename 183 | + */ 184 | + public function getInversedImageMapping($newFilename) 185 | + { 186 | + 187 | + // try to load the original filename 188 | + if ($filename = array_search($newFilename, $this->imageMappings)) { 189 | + return $filename; 190 | + } 191 | + 192 | + // return the new one otherwise 193 | + return $newFilename; 194 | + } 195 | + 196 | + /** 197 | + * Get new file name, if a filename with the same name already exists. 198 | + * 199 | + * @param string $targetFilename The name of target file 200 | * 201 | * @return string The new filename 202 | */ 203 | @@ -134,7 +216,7 @@ trait FileUploadTrait 204 | 205 | // query whether or not, the file exists 206 | if ($this->getFilesystemAdapter()->isFile($targetFilename)) { 207 | - // initialize the incex and the basename 208 | + // initialize the index and the basename 209 | $index = 1; 210 | $baseName = $fileInfo['filename'] . '.' . $fileInfo['extension']; 211 | 212 | @@ -179,23 +261,30 @@ trait FileUploadTrait 213 | 214 | // query whether or not the image file to be imported is available 215 | if (!$this->getFilesystemAdapter()->isFile($sourceFilename)) { 216 | - throw new \Exception(sprintf('Media file %s not available', $sourceFilename)); 217 | + throw new \Exception(sprintf('Media file "%s" is not available', $sourceFilename)); 218 | } 219 | 220 | - // prepare the target filename, if necessary 221 | - $newTargetFilename = $this->getNewFileName($targetFilename); 222 | - $targetFilename = str_replace(basename($targetFilename), $newTargetFilename, $targetFilename); 223 | + // query whether or not, the file has already been processed 224 | + if ($this->imageHasNotBeenMapped($filename)) { 225 | + // load the new filename, e. g. if a file with the same name already exists 226 | + $newTargetFilename = $this->getNewFileName($targetFilename); 227 | + // replace the old filename with the new one 228 | + $targetFilename = str_replace(basename($targetFilename), $newTargetFilename, $targetFilename); 229 | 230 | - // make sure, the target directory exists 231 | - if (!$this->getFilesystemAdapter()->isDir($targetDirectory = dirname($targetFilename))) { 232 | - $this->getFilesystemAdapter()->mkdir($targetDirectory, 0755); 233 | - } 234 | + // make sure, the target directory exists 235 | + if (!$this->getFilesystemAdapter()->isDir($targetDirectory = dirname($targetFilename))) { 236 | + $this->getFilesystemAdapter()->mkdir($targetDirectory, 0755); 237 | + } 238 | + 239 | + // copy the image to the target directory 240 | + $this->getFilesystemAdapter()->copy($sourceFilename, $targetFilename); 241 | 242 | - // copy the image to the target directory 243 | - $this->getFilesystemAdapter()->copy($sourceFilename, $targetFilename); 244 | + // add the mapping and return the mapped filename 245 | + $this->addImageMapping($filename, str_replace($mediaDir, '', $targetFilename)); 246 | + } 247 | 248 | - // return the new target filename 249 | - return str_replace($mediaDir, '', $targetFilename); 250 | + // simply return the mapped filename 251 | + return $this->getImageMapping($filename); 252 | } 253 | 254 | /** 255 | @@ -217,7 +306,7 @@ trait FileUploadTrait 256 | 257 | // query whether or not the image file to be deleted is available 258 | if (!$this->getFilesystemAdapter()->isFile($targetFilename)) { 259 | - throw new \Exception(sprintf('Media file %s not available', $targetFilename)); 260 | + throw new \Exception(sprintf('Media file "%s" is not available', $targetFilename)); 261 | } 262 | 263 | // delte the image from the target directory 264 | -------------------------------------------------------------------------------- /patches/3.8.5/import-configuration-jms.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/Parsers/JsonParser.php b/src/Parsers/JsonParser.php 2 | index ef88ec5..a9a0c04 100644 3 | --- a/src/Parsers/JsonParser.php 4 | +++ b/src/Parsers/JsonParser.php 5 | @@ -137,6 +137,11 @@ 6 | array_unshift($files, $file); 7 | } 8 | 9 | + // sort the files ascending 10 | + usort($files, function ($a, $b) { 11 | + return strcmp($a, $b); 12 | + }); 13 | + 14 | // return the array with the files 15 | return $files; 16 | } 17 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | TechDivision GmbH coding standard 9 | 10 | 11 | */doc/* 12 | */tests/* 13 | */target/* 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /phpmd.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | TechDivision GmbH default PHPMD rule set 11 | 12 | 13 | 14 | 15 | 16 | 17 | */doc/* 18 | */tests/* 19 | */target/* 20 | 21 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 0 3 | paths: 4 | - vendor/techdivision 5 | excludePaths: 6 | - vendor/techdivision/*/tests/* 7 | 8 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tests/unit 5 | vendor/techdivision/*/tests/unit 6 | 7 | 8 | tests/integration 9 | 10 | 11 | 12 | 13 | src 14 | vendor/techdivision/*/src 15 | 16 | 17 | src 18 | src 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /stub.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 11 | * @copyright 2016 TechDivision GmbH 12 | * @license https://opensource.org/licenses/MIT 13 | * @link https://github.com/techdivision/import-cli-simple 14 | * @link http://www.techdivision.com 15 | */ 16 | 17 | // if we're running from phar load the phar autoload, else let the 18 | // script 'robo' search for the autoloader 19 | if (strpos(basename(__FILE__), 'phar')) { 20 | require_once $autoloadFile = 'phar://import-cli-simple.phar/vendor/autoload.php'; 21 | } else { 22 | if (file_exists(__DIR__.'/vendor/autoload.php')) { 23 | require_once $autoloadFile = __DIR__.'/vendor/autoload.php'; 24 | } elseif (file_exists(__DIR__.'/../../autoload.php')) { 25 | require_once $autoloadFile = __DIR__ . '/../../autoload.php'; 26 | } else { 27 | require_once $autoloadFile = 'phar://import-cli-simple.phar/vendor/autoload.php'; 28 | } 29 | } 30 | 31 | // initialize the vendor directory 32 | $vendorDir = dirname($autoloadFile); 33 | 34 | // bootstrap and run the application 35 | require dirname($vendorDir) . '/bootstrap.php'; 36 | 37 | // stop the compiler 38 | __HALT_COMPILER(); ?> -------------------------------------------------------------------------------- /tests/acceptance/README.md: -------------------------------------------------------------------------------- 1 | # Running the behat testsuite local on Mac OS X 2 | 3 | ## Prequisites 4 | 5 | * You've added the domain you want to use for testing, e. g. `127.0.1.1 mage-ce-235.test` to your hosts file under `/etc/hosts` 6 | * You've created an additional local loopback IP as `sudo` by invoking `ifconfig lo0 alias 127.0.1.1;` on your CLI 7 | 8 | ## Create your own Magento Docker container 9 | 10 | If you don't want to use one of our prepared Docker containers from [Docker Hub](https://hub.docker.com/repository/docker/techdivision/magento2-ce), you can create your own one by cloning the repository with the Magento 2 Docker Image Generator by 11 | 12 | ```sh 13 | git clone https://github.com/techdivision/magento2-docker-imgen.git 14 | ``` 15 | 16 | and build the container with 17 | 18 | ```sh 19 | docker build \ 20 | --build-arg MAGENTO_REPO_USERNAME=##YOUR_PUBLIC_ACCESS_KEY## \ 21 | --build-arg MAGENTO_REPO_PASSWORD=##YOUR_PRIVATE_ACCESS_KEY## \ 22 | --build-arg MAGENTO_INSTALL_EDITION=community \ 23 | --build-arg MAGENTO_INSTALL_VERSION=2.3.5 \ 24 | --build-arg MAGENTO_INSTALL_STABILITY=stable \ 25 | --build-arg PHP_VERSION=7.2 . 26 | ``` 27 | 28 | ## Step 1: Start a Magento Docker container 29 | 30 | We recommend to use our own Magento Docker images, prepared with by [Magento 2 Docker Image Generator](https://github.com/techdivision/magento2-docker-imgen) for running the behat testsuite. You'll find them on [Docker Hub](https://hub.docker.com/repository/docker/techdivision/magento2-ce). To create a new Docker container with Magento CE 2.3.5 open the CLI and enter 31 | 32 | ```sh 33 | docker run --rm -d --name mage-ce-235 \ 34 | -p 127.0.1.1:80:80 \ 35 | -p 127.0.1.1:443:443 \ 36 | -p 127.0.1.1:3306:3306 \ 37 | -e MAGENTO_BASE_URL=mage-ce-235.test \ 38 | techdivision/magento2-ce:2.3.5 39 | ``` 40 | 41 | When the Docker container has been stopped after processing the behat testsuite, it'll be removed because of the `--rm` parameter. Do not add this, if you want to keep the container for further testing runs. To test, if your Docker container is running, open a browser and enter `https://mage-ce-235.test/` or `https://mage-ce-235.test/admin`, if you want to enter the admin area. The credentials are `admin` and `admin123` as password. 42 | 43 | ## Step 2: Prepare Magento Docker container 44 | 45 | When the Docker container is running, you've to prepare the container to allow external access by the behat testsuite. Therfore open the CLI and invoke the Robo taskrunner with 46 | 47 | ```sh 48 | vendor/bin/robo prepare:docker mage-ce-235.test mage-ce-235 49 | ``` 50 | 51 | Do not forget, that you've to change domain and container name, when you're using a different Magento version. 52 | 53 | ## Step 3: Run behat testuite 54 | 55 | When the Docker container is running and has been prepared, you can finally start the behat testsuite, also by invoking the Robo taskrunner, with 56 | 57 | ```sh 58 | MAGENTO_INSTALL_DIR=/var/www/dist \ 59 | MAGENTO_CONTAINER_NAME=mage-ce-235 \ 60 | MAGENTO_BASE_URL=mage-ce-235.test \ 61 | DB_HOST=127.0.1.1 \ 62 | DB_PORT=3306 \ 63 | vendor/bin/behat \ 64 | --tags=@ce&&@2.3&&~@customer&&~@customer-address 65 | ``` 66 | -------------------------------------------------------------------------------- /tests/acceptance/app/etc/configuration/loggers.json: -------------------------------------------------------------------------------- 1 | { 2 | "loggers": { 3 | "system": { 4 | "id": "import.logger.factory.monolog", 5 | "channel-name": "logger/system", 6 | "handlers": [ 7 | { 8 | "id" : "import.logger.handler.null" 9 | } 10 | ] 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /tests/acceptance/bin/docker-chrome-headless.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "${PHP_VERSION}" ]; then 4 | PHP_VERSION=7.1 5 | fi 6 | 7 | IMAGE=registry.gitlab.com/dmore/docker-chrome-headless:${PHP_VERSION} 8 | 9 | docker pull ${IMAGE} 10 | 11 | docker run -it --rm -v $(pwd):/code -e DOCROOT=/code/web ${IMAGE} bash 12 | -------------------------------------------------------------------------------- /tests/acceptance/bin/google-chrome-macosx.sh: -------------------------------------------------------------------------------- 1 | /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --disable-gpu --headless --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/.env: -------------------------------------------------------------------------------- 1 | DB_HOST=127.0.0.1 2 | DB_PORT=3306 3 | DB_NAME=magento 4 | DB_USER=magento 5 | DB_PASSWORD=magento 6 | SOURCE_DIR=$(pwd)/var/pacemaker/import 7 | MAGENTO_INSTALL_EDITION=ce 8 | MAGENTO_INSTALL_VERSION=2.3.5 9 | MAGENTO_BASE_URL=magento.test 10 | MAGENTO_INSTALL_DIR=$(pwd) 11 | MAGENTO_CONTAINER_NAME=magento -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/AppKernel.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple; 22 | 23 | use Symfony\Component\Dotenv\Dotenv; 24 | use Symfony\Component\HttpKernel\Kernel; 25 | use Symfony\Component\Config\Loader\LoaderInterface; 26 | use Symfony\Bundle\FrameworkBundle\FrameworkBundle; 27 | 28 | /** 29 | * The symfony app kernel implementation for the M2IF import acceptance testsuite. 30 | * 31 | * @author Tim Wagner 32 | * @copyright 2019 TechDivision GmbH 33 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 34 | * @link https://github.com/techdivision/import-cli-simple 35 | * @link http://www.techdivision.com 36 | */ 37 | class AppKernel extends Kernel 38 | { 39 | 40 | /** 41 | * Returns an array of bundles to register. 42 | * 43 | * @return iterable|\Symfony\Component\HttpKernel\Bundle\BundleInterface[] An iterable of bundle instances 44 | * @see \Symfony\Component\HttpKernel\KernelInterface::registerBundles() 45 | */ 46 | public function registerBundles() 47 | { 48 | 49 | // load the environment variables 50 | $dotenv = new Dotenv(); 51 | $dotenv->loadEnv(__DIR__.'/.env'); 52 | 53 | // return the registered bundles 54 | return array( 55 | new FrameworkBundle(), 56 | new ImportBundle() 57 | ); 58 | } 59 | 60 | /** 61 | * Loads the container configuration. 62 | * 63 | * @return void 64 | * @see \Symfony\Component\HttpKernel\KernelInterface::registerContainerConfiguration() 65 | */ 66 | public function registerContainerConfiguration(LoaderInterface $loader) 67 | { 68 | $loader->load(__DIR__.'/Resources/config/config_'.$this->getEnvironment().'.yml'); 69 | } 70 | } -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/AttributeFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use Behat\Behat\Context\Context; 24 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 25 | use TechDivision\Import\Utils\CommandNames; 26 | 27 | /** 28 | * Defines attribute features from the specific context. 29 | * 30 | * @author Tim Wagner 31 | * @copyright 2019 TechDivision GmbH 32 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 33 | * @link https://github.com/techdivision/import-cli-simple 34 | * @link http://www.techdivision.com 35 | */ 36 | class AttributeFeatureContext implements Context 37 | { 38 | 39 | /** 40 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 41 | */ 42 | private $consoleContext; 43 | 44 | /** @BeforeScenario */ 45 | public function before(BeforeScenarioScope $scope) 46 | { 47 | 48 | // load the environment 49 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 50 | $environment = $scope->getEnvironment(); 51 | 52 | // make the console context available 53 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 54 | } 55 | 56 | /** 57 | * @Given attributes have been imported 58 | */ 59 | public function attributesHaveBeenImported() 60 | { 61 | $this->filesWithAttributesToBeUpdatedAreAvailable(); 62 | $this->theAttributeImportProcessHasBeenStarted(); 63 | } 64 | 65 | /** 66 | * @Given files with attributes to be updated are available 67 | * @Given files with attributes to be replaced are available 68 | * @Given files with attributes to be deleted are available 69 | */ 70 | public function filesWithAttributesToBeUpdatedAreAvailable() 71 | { 72 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 73 | 'vendor/techdivision/import-sample-data/generic/data/attributes/add-update/attribute-import_20170428-124902_01.csv' 74 | ); 75 | } 76 | 77 | /** 78 | * @Given the attribute(s) import process has been started 79 | */ 80 | public function theAttributeImportProcessHasBeenStarted() 81 | { 82 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 83 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_ATTRIBUTES)); 84 | } 85 | 86 | /** 87 | * @Given the attribute(s) deletion process has been started 88 | */ 89 | public function theAttributeDeletionProcessHasBeenStarted() 90 | { 91 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 92 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_ATTRIBUTES)); 93 | } 94 | 95 | /** 96 | * @Given the attribute(s) replacement process has been started 97 | */ 98 | public function theAttributeReplacementProcessHasBeenStarted() 99 | { 100 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 101 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_ATTRIBUTES)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/AttributeSetFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use Behat\Behat\Context\Context; 24 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 25 | use TechDivision\Import\Utils\CommandNames; 26 | 27 | /** 28 | * Defines attribute set features from the specific context. 29 | * 30 | * @author Tim Wagner 31 | * @copyright 2019 TechDivision GmbH 32 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 33 | * @link https://github.com/techdivision/import-cli-simple 34 | * @link http://www.techdivision.com 35 | */ 36 | class AttributeSetFeatureContext implements Context 37 | { 38 | 39 | /** 40 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 41 | */ 42 | private $consoleContext; 43 | 44 | /** @BeforeScenario */ 45 | public function before(BeforeScenarioScope $scope) 46 | { 47 | 48 | // load the environment 49 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 50 | $environment = $scope->getEnvironment(); 51 | 52 | // make the console context available 53 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 54 | } 55 | 56 | /** 57 | * @Given attribute sets have been imported 58 | */ 59 | public function attributeSetsHaveBeenImported() 60 | { 61 | $this->filesWithAttributeSetsToBeUpdatedAreAvailable(); 62 | $this->theAttributeSetImportProcessHasBeenStarted(); 63 | } 64 | 65 | /** 66 | * @Given files with attribute sets to be updated are available 67 | * @Given files with attribute sets to be deleted are available 68 | * @Given files with attribute sets to be replaced are available 69 | */ 70 | public function filesWithAttributeSetsToBeUpdatedAreAvailable() 71 | { 72 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 73 | 'vendor/techdivision/import-sample-data/generic/data/attributes-set/add-update/attribute-set-import_20190104-114000_01.csv' 74 | ); 75 | } 76 | 77 | /** 78 | * @Given the attribute set import process has been started 79 | */ 80 | public function theAttributeSetImportProcessHasBeenStarted() 81 | { 82 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 83 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_ATTRIBUTES_SET)); 84 | } 85 | 86 | /** 87 | * @Given the attribute set deletion process has been started 88 | */ 89 | public function theAttributeSetDeletionProcessHasBeenStarted() 90 | { 91 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 92 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_ATTRIBUTES_SET)); 93 | } 94 | 95 | /** 96 | * @Given the attribute set replacement process has been started 97 | */ 98 | public function theAttributeSetReplacementProcessHasBeenStarted() 99 | { 100 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 101 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_ATTRIBUTES_SET)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/CategoryFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use Behat\Behat\Context\Context; 24 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 25 | use TechDivision\Import\Utils\CommandNames; 26 | 27 | /** 28 | * Defines category features from the specific context. 29 | * 30 | * @author Tim Wagner 31 | * @copyright 2019 TechDivision GmbH 32 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 33 | * @link https://github.com/techdivision/import-cli-simple 34 | * @link http://www.techdivision.com 35 | */ 36 | class CategoryFeatureContext implements Context 37 | { 38 | 39 | /** 40 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 41 | */ 42 | private $consoleContext; 43 | 44 | /** @BeforeScenario */ 45 | public function before(BeforeScenarioScope $scope) 46 | { 47 | 48 | // load the environment 49 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 50 | $environment = $scope->getEnvironment(); 51 | 52 | // make the console context available 53 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 54 | } 55 | 56 | /** 57 | * @Given categories have been imported 58 | */ 59 | public function categoriesHaveBeenImported() 60 | { 61 | $this->filesWithCategoriesToBeUpdatedAreAvailable(); 62 | $this->theCategoryImportProcessHasBeenStarted(); 63 | } 64 | 65 | /** 66 | * @Given files with categories to be updated are available 67 | * @Given files with categories to be deleted are available 68 | * @Given files with categories to be replaced are available 69 | */ 70 | public function filesWithCategoriesToBeUpdatedAreAvailable() 71 | { 72 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 73 | 'vendor/techdivision/import-sample-data/generic/data/categories/add-update/category-import_20161024-194026_01.csv' 74 | ); 75 | } 76 | 77 | /** 78 | * @Given the category import process has been started 79 | */ 80 | public function theCategoryImportProcessHasBeenStarted() 81 | { 82 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 83 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_CATEGORIES)); 84 | } 85 | 86 | /** 87 | * @Given the category deletion process has been started 88 | */ 89 | public function theCategoryDeletionProcessHasBeenStarted() 90 | { 91 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 92 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_CATEGORIES)); 93 | } 94 | 95 | /** 96 | * @Given the category replacement process has been started 97 | */ 98 | public function theCategoryReplacementProcessHasBeenStarted() 99 | { 100 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 101 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_CATEGORIES)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/ConsoleContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use PHPUnit\Framework\Assert; 24 | use Behat\Behat\Context\Context; 25 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 26 | use TechDivision\Import\Adapter\PhpFilesystemAdapter; 27 | use Behat\Symfony2Extension\Context\KernelDictionary; 28 | use Behat\Symfony2Extension\Context\KernelAwareContext; 29 | 30 | /** 31 | * Defines console features from the specific context. 32 | * 33 | * @author Tim Wagner 34 | * @copyright 2019 TechDivision GmbH 35 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 36 | * @link https://github.com/techdivision/import-cli-simple 37 | * @link http://www.techdivision.com 38 | */ 39 | class ConsoleContext implements Context, KernelAwareContext 40 | { 41 | 42 | /** 43 | * @var \Behat\Symfony2Extension\Context\KernelDictionary 44 | */ 45 | use KernelDictionary; 46 | 47 | /** 48 | * The Magento 2 installation directory. 49 | * 50 | * @var string 51 | */ 52 | private $sourceDir; 53 | 54 | /** 55 | * The output of the last executed command. 56 | * 57 | * @var array 58 | */ 59 | private $output = array(); 60 | 61 | /** 62 | * The return value of the last executed command. 63 | * 64 | * @var integer 65 | */ 66 | private $exitCode = 0; 67 | 68 | private $dbHost = ''; 69 | private $dbPort = ''; 70 | private $dbUser = ''; 71 | private $dbName = ''; 72 | private $dbPassword = ''; 73 | private $magentoEdition = ''; 74 | private $magentoVersion = ''; 75 | 76 | /** 77 | * The environment used to execute the Magento commands. 78 | * 79 | * @var \TechDivision\Import\Cli\Simple\Contexts\DockerEnvironment 80 | */ 81 | private $env; 82 | 83 | /** @BeforeScenario */ 84 | public function before(BeforeScenarioScope $scope) 85 | { 86 | 87 | // initialize the environment and the source directory 88 | /** @var \TechDivision\Import\Cli\Simple\Contexts\DockerEnvironment */ 89 | $this->env = $this->getContainer()->get('environment.docker'); 90 | $this->sourceDir = $this->getContainer()->getParameter('source.dir'); 91 | $this->dbHost = $this->getContainer()->getParameter('db.host'); 92 | $this->dbPort = $this->getContainer()->getParameter('db.port'); 93 | $this->dbUser = $this->getContainer()->getParameter('db.user'); 94 | $this->dbName = $this->getContainer()->getParameter('db.name'); 95 | $this->dbPassword = $this->getContainer()->getParameter('db.password'); 96 | $this->magentoEdition = $this->getContainer()->getParameter('magento.install_edition'); 97 | $this->magentoVersion = $this->getContainer()->getParameter('magento.install_version'); 98 | 99 | // create a new filesystem 100 | $filesystemAdapter = new PhpFilesystemAdapter(); 101 | 102 | // query whether or not the source directory has to be created 103 | if (is_dir($this->sourceDir) === false) { 104 | mkdir($this->sourceDir, 0755, true); 105 | } 106 | 107 | // clean-up the source directory, if not empty 108 | foreach (glob(sprintf('%s/*', $this->sourceDir)) as $file) { 109 | if (is_file($file)) { 110 | $filesystemAdapter->delete($file); 111 | } else { 112 | $filesystemAdapter->removeDir($file, true); 113 | } 114 | } 115 | } 116 | 117 | /** 118 | * Return's the exit code of the las executed command. 119 | * 120 | * @return integer The exit code 121 | */ 122 | public function getExitCode() 123 | { 124 | return $this->exitCode; 125 | } 126 | 127 | /** 128 | * Return's the output of the last executed command. 129 | * 130 | * @return array The output of the last executed command 131 | */ 132 | public function getOutput() 133 | { 134 | return $this->output; 135 | } 136 | 137 | /** 138 | * @Given a third party system has copied the file :arg1 into the import folder 139 | */ 140 | public function aThirdPartySystemHasCopiedTheFileIntoTheImportFolder($arg1) 141 | { 142 | 143 | // copy the passed file to the source directory 144 | if (copy($arg1, $dest = sprintf('%s/%s', $this->sourceDir, basename($arg1)))) { 145 | return; 146 | } 147 | 148 | // throw an exception if it is NOT possible to copy the file 149 | throw new \Exception(sprintf('Can\'t copy file %s to %s', $arg1, $dest)); 150 | } 151 | 152 | /** 153 | * @When the simple command :arg1 has been executed 154 | */ 155 | public function theSimpleCommandHasBeenExecuted($arg1) 156 | { 157 | 158 | // execute the simple command and assert that the exit code is NOT one 159 | exec($this->appendGenericConfig($arg1), $this->output, $this->exitCode); 160 | if ($this->exitCode <> 0) { 161 | var_export($this->output); 162 | } 163 | $this->assertExitCode(); 164 | } 165 | 166 | /** 167 | * @When the command :arg1 has been executed 168 | */ 169 | public function theCommandHasBeenExecuted($arg1) 170 | { 171 | $this->theSimpleCommandHasBeenExecuted($this->appendDbConnection($this->appendGenericConfig($arg1))); 172 | } 173 | 174 | /** 175 | * @When the magento command :arg1 has been executed 176 | */ 177 | public function theMagentoCommandHasBeenExecuted($arg1) 178 | { 179 | $this->env->executeMagentoCommand($arg1); 180 | } 181 | 182 | /** 183 | * @Given the magento index has been updated 184 | */ 185 | public function theMagentoIndexHasBeenUpdated() 186 | { 187 | $this->env->updateMagentoIndex(); 188 | } 189 | 190 | /** 191 | * @Given the magento customer grid index has been updated 192 | * 193 | * @return void 194 | */ 195 | public function theMagentoCustomerGridIndexHasBeenUpdated() 196 | { 197 | $this->env->updateMagentoIndex(array('customer_grid')); 198 | } 199 | 200 | /** 201 | * @Then a success message has to be rendered 202 | */ 203 | public function assertSuccessMessage() 204 | { 205 | $this->assertMessage('/Successfully executed command \w+:\w+:?\w+?:?\w+? with serial \w+-\w+-\w+-\w+-\w+ in \d+:\d+:\d+ s/'); 206 | } 207 | 208 | /** 209 | * @When the process has been finished 210 | * @When the import process has been finished 211 | * @When the deletion process has been finished 212 | * @When the replacement process has been finished 213 | * @When the attribute import process has been finished 214 | * @When the attribute deletion process has been finished 215 | * @When the attribute replacement process has been finished 216 | * @When the attribute set import process has been finished 217 | * @When the attribute set deletion process has been finished 218 | * @When the attribute set replacement process has been finished 219 | * @When the category import process has been finished 220 | * @When the category deletion process has been finished 221 | * @When the category replacement process has been finished 222 | * @When the product import process has been finished 223 | * @When the product deletion process has been finished 224 | * @When the product replacement process has been finished 225 | * @When the tier price import process has been finished 226 | * @When the tier price deletion process has been finished 227 | * @When the tier price replacement process has been finished 228 | * @When the customer import process has been finished 229 | * @When the customer deletion process has been finished 230 | * @When the customer replacement process has been finished 231 | * @When the customer address import process has been finished 232 | * @When the customer address deletion process has been finished 233 | * @When the customer address replacement process has been finished 234 | */ 235 | public function assertExitCode() 236 | { 237 | Assert::assertSame(0, $this->exitCode); 238 | } 239 | 240 | /** 241 | * @Then a message :arg1 has to be rendered 242 | */ 243 | public function assertMessage($arg1) 244 | { 245 | Assert::assertRegExp($arg1, array_pop($this->output)); 246 | } 247 | 248 | /** 249 | * Append the database connection. 250 | * 251 | * @param string $cmd The command to execute 252 | * 253 | * @return string The command with the appended DB connection string 254 | */ 255 | protected function appendDbConnection($cmd) 256 | { 257 | return sprintf( 258 | '%s --db-username=%s --db-password=%s --db-pdo-dsn="mysql:host=%s;port=%d;dbname=%s;charset=utf8"', 259 | $cmd, 260 | $this->dbUser, 261 | $this->dbPassword, 262 | $this->dbHost, 263 | $this->dbPort, 264 | $this->dbName 265 | ); 266 | } 267 | 268 | /** 269 | * Append the generic configuration. 270 | * 271 | * @param string $cmd The command to execute 272 | * 273 | * @return string The command with the appended generic configuration 274 | */ 275 | protected function appendGenericConfig($cmd) 276 | { 277 | return sprintf( 278 | '%s --custom-configuration-dir=tests/acceptance/app/etc/configuration --magento-edition=%s --magento-version=%s --source-dir=%s', 279 | $cmd, 280 | $this->magentoEdition, 281 | $this->magentoVersion, 282 | $this->sourceDir 283 | ); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/CustomerAddressFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use Behat\Behat\Context\Context; 24 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 25 | use TechDivision\Import\Utils\CommandNames; 26 | 27 | /** 28 | * Defines customer address features from the specific context. 29 | * 30 | * @author Tim Wagner 31 | * @copyright 2019 TechDivision GmbH 32 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 33 | * @link https://github.com/techdivision/import-cli-simple 34 | * @link http://www.techdivision.com 35 | */ 36 | class CustomerAddressFeatureContext implements Context 37 | { 38 | 39 | /** 40 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 41 | */ 42 | private $consoleContext; 43 | 44 | /** @BeforeScenario */ 45 | public function before(BeforeScenarioScope $scope) 46 | { 47 | 48 | // load the environment 49 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 50 | $environment = $scope->getEnvironment(); 51 | 52 | // make the console context available 53 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 54 | } 55 | 56 | /** 57 | * @Given customer addresses have been imported 58 | */ 59 | public function customerAddressesHaveBeenImported() 60 | { 61 | $this->filesWithCustomerAddressesToBeUpdatedAreAvailable(); 62 | $this->theCustomerAddressImportProcessHasBeenStarted(); 63 | } 64 | 65 | /** 66 | * @Given files with customer addresses to be updated are available 67 | * @Given files with customer addresses to be deleted are available 68 | * @Given files with customer addresses to be replaced are available 69 | */ 70 | public function filesWithCustomerAddressesToBeUpdatedAreAvailable() 71 | { 72 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 73 | 'vendor/techdivision/import-sample-data/generic/data/customers-address/add-update/customer-address-import_20181217-085523_01.csv' 74 | ); 75 | } 76 | 77 | /** 78 | * @Given the customer address import process has been started 79 | */ 80 | public function theCustomerAddressImportProcessHasBeenStarted() 81 | { 82 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 83 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_CUSTOMERS_ADDRESS)); 84 | } 85 | 86 | /** 87 | * @Given the customer address deletion process has been started 88 | */ 89 | public function theCustomerAddressDeletionProcessHasBeenStarted() 90 | { 91 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 92 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_CUSTOMERS_ADDRESS)); 93 | } 94 | 95 | /** 96 | * @Given the customer address replacement process has been started 97 | */ 98 | public function theCustomerAddressReplacementProcessHasBeenStarted() 99 | { 100 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 101 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_CUSTOMERS_ADDRESS)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/CustomerFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use Behat\Behat\Context\Context; 24 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 25 | use TechDivision\Import\Utils\CommandNames; 26 | 27 | /** 28 | * Defines customer features from the specific context. 29 | * 30 | * @author Tim Wagner 31 | * @copyright 2019 TechDivision GmbH 32 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 33 | * @link https://github.com/techdivision/import-cli-simple 34 | * @link http://www.techdivision.com 35 | */ 36 | class CustomerFeatureContext implements Context 37 | { 38 | 39 | /** 40 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 41 | */ 42 | private $consoleContext; 43 | 44 | /** @BeforeScenario */ 45 | public function before(BeforeScenarioScope $scope) 46 | { 47 | 48 | // load the environment 49 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 50 | $environment = $scope->getEnvironment(); 51 | 52 | // make the console context available 53 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 54 | } 55 | 56 | /** 57 | * @Given customers have been imported 58 | */ 59 | public function customersHaveBeenImported() 60 | { 61 | $this->filesWithCustomersToBeUpdatedAreAvailable(); 62 | $this->theCustomerImportProcessHasBeenStarted(); 63 | } 64 | 65 | /** 66 | * @Given files with customers to be updated are available 67 | * @Given files with customers to be deleted are available 68 | * @Given files with customers to be replaced are available 69 | */ 70 | public function filesWithCustomersToBeUpdatedAreAvailable() 71 | { 72 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 73 | 'vendor/techdivision/import-sample-data/generic/data/customers/add-update/customer-import_20181213-132802_01.csv' 74 | ); 75 | } 76 | 77 | /** 78 | * @Given the customer import process has been started 79 | */ 80 | public function theCustomerImportProcessHasBeenStarted() 81 | { 82 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 83 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_CUSTOMERS)); 84 | } 85 | 86 | /** 87 | * @Given the customer deletion process has been started 88 | */ 89 | public function theCustomerDeletionProcessHasBeenStarted() 90 | { 91 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 92 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_CUSTOMERS)); 93 | } 94 | 95 | /** 96 | * @Given the customer replacement process has been started 97 | */ 98 | public function theCustomerReplacementProcessHasBeenStarted() 99 | { 100 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 101 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_CUSTOMERS)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/DockerEnvironment.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use PHPUnit\Framework\Assert; 24 | 25 | /** 26 | * Defines a Docker specific context. 27 | * 28 | * @author Tim Wagner 29 | * @copyright 2019 TechDivision GmbH 30 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 31 | * @link https://github.com/techdivision/import-cli-simple 32 | * @link http://www.techdivision.com 33 | */ 34 | class DockerEnvironment 35 | { 36 | 37 | /** 38 | * The Magento 2 installation directory. 39 | * 40 | * @var string 41 | */ 42 | private $installDir = '/var/www/dist'; 43 | 44 | /** 45 | * The Docker container name with the Magento 2 installation directory. 46 | * 47 | * @var string 48 | */ 49 | private $containerName = 'magento'; 50 | 51 | /** 52 | * The return value of the last executed command. 53 | * 54 | * @var integer 55 | */ 56 | private $exitCode = 0; 57 | 58 | /** 59 | * The fully prepared command that has to be executed. 60 | * 61 | * @var string 62 | */ 63 | private $cmd; 64 | 65 | /** 66 | * The output of the last executed command. 67 | * 68 | * @var array 69 | */ 70 | private $output = array(); 71 | 72 | /** 73 | * Initialize the Docker environment with the container name and the Magento 74 | * installation directory within the container. 75 | * 76 | * @param string|null $containerName The container name 77 | * @param string|null $installDir The installation directory 78 | */ 79 | public function __construct($containerName = null, $installDir = null) 80 | { 81 | 82 | // set the container name, if passed 83 | if ($containerName !== null) { 84 | $this->containerName = $containerName; 85 | } 86 | 87 | // set the installation directory, if passed 88 | if ($installDir !== null) { 89 | $this->installDir = $installDir; 90 | } 91 | } 92 | 93 | /** 94 | * Return's the exit code of the las executed command. 95 | * 96 | * @return integer The exit code 97 | */ 98 | public function getExitCode() 99 | { 100 | return $this->exitCode; 101 | } 102 | 103 | /** 104 | * Return's the output of the last executed command. 105 | * 106 | * @return array The output of the last executed command 107 | */ 108 | public function getOutput() 109 | { 110 | return $this->output; 111 | } 112 | 113 | /** 114 | * Executes the passed Magento command and asserts that the exit code against the passed one. 115 | * 116 | * @return void 117 | */ 118 | public function executeMagentoCommand($arg1, $exitCode = 0) 119 | { 120 | 121 | // initialize the command 122 | $this->cmd = $this->prependExecutionEnvironment($arg1); 123 | 124 | // executes the prepared command 125 | exec($this->cmd, $this->output, $this->exitCode); 126 | 127 | // render the command and the output if an error occurs 128 | if ($this->exitCode <> $exitCode) { 129 | echo sprintf('Executed command: %s with output %s', $this->cmd, print_r($this->output, true)) . PHP_EOL; 130 | } 131 | 132 | // assert that the exit code matches the passed one 133 | Assert::assertEquals($exitCode, $this->exitCode); 134 | } 135 | 136 | /** 137 | * Updates the Magento index. 138 | * 139 | * @return void 140 | */ 141 | public function updateMagentoIndex(array $indexNames = array()) 142 | { 143 | 144 | // prepare the command to run the Magento indexer 145 | $cmd = trim(sprintf('bin/magento indexer:reindex %s', implode(' ', $indexNames))); 146 | 147 | // execute the Magento command 148 | $this->executeMagentoCommand($cmd); 149 | } 150 | 151 | /** 152 | * Prepends the execution environment to the passed command. 153 | * 154 | * @param string $cmd The Magento command to be executed 155 | * 156 | * @return string The command with the prepended execution environment 157 | */ 158 | protected function prependExecutionEnvironment($cmd) 159 | { 160 | return sprintf('docker exec %s php %s', $this->containerName, $this->prependInstallDir($cmd)); 161 | } 162 | 163 | /** 164 | * Prepends the Magento installation directory to the passed command. 165 | * 166 | * @param string $cmd The Magento command to be executed 167 | * 168 | * @return string The command with the prepended installation directory 169 | */ 170 | protected function prependInstallDir($cmd) 171 | { 172 | return sprintf('%s/%s', $this->installDir, $cmd); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/FeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use Behat\Behat\Context\Context; 24 | use Behat\MinkExtension\Context\MinkContext; 25 | use Behat\Symfony2Extension\Context\KernelAwareContext; 26 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 27 | use Behat\Symfony2Extension\Context\KernelDictionary; 28 | 29 | /** 30 | * Defines application features from the specific context. 31 | * 32 | * @author Tim Wagner 33 | * @copyright 2019 TechDivision GmbH 34 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 35 | * @link https://github.com/techdivision/import-cli-simple 36 | * @link http://www.techdivision.com 37 | */ 38 | class FeatureContext extends MinkContext implements Context, KernelAwareContext 39 | { 40 | 41 | use KernelDictionary; 42 | 43 | /** @BeforeScenario */ 44 | public function before(BeforeScenarioScope $scope) 45 | { 46 | $this->setMinkParameter('base_url', sprintf('http://%s', $this->getContainer()->getParameter('magento.base_url'))); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/MsiFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use Behat\Behat\Context\Context; 24 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 25 | use TechDivision\Import\Utils\CommandNames; 26 | 27 | /** 28 | * Defines MSI features from the specific context. 29 | * 30 | * @author Tim Wagner 31 | * @copyright 2019 TechDivision GmbH 32 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 33 | * @link https://github.com/techdivision/import-cli-simple 34 | * @link http://www.techdivision.com 35 | */ 36 | class MsiFeatureContext implements Context 37 | { 38 | 39 | /** 40 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 41 | */ 42 | private $consoleContext; 43 | 44 | /** 45 | * @var \TechDivision\Import\Cli\Simple\Contexts\FeatureContext 46 | */ 47 | private $featureContext; 48 | 49 | /** 50 | * @var \TechDivision\Import\Cli\Simple\Contexts\ProductFeatureContext 51 | */ 52 | private $productFeatureContext; 53 | 54 | /** @BeforeScenario */ 55 | public function before(BeforeScenarioScope $scope) 56 | { 57 | 58 | // load the environment 59 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 60 | $environment = $scope->getEnvironment(); 61 | 62 | // make console, feature and product feature context available 63 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 64 | $this->featureContext = $environment->getContext(FeatureContext::class); 65 | $this->productFeatureContext = $environment->getContext(ProductFeatureContext::class); 66 | } 67 | 68 | /** 69 | * @Then title, price and status are :arg1 70 | * @Then title, price and status are :arg1, ,( ) 71 | * @Then title, price and status are :arg1, :arg2, 72 | * @Then title, price and status are :arg1, , :arg3 73 | * @Then title, price and status are :arg1, :arg2, :arg3 74 | */ 75 | public function assertTitlePriceAndStatus($arg1, $arg2 = null, $arg3 = null) 76 | { 77 | $this->productFeatureContext->assertTitleAndPrice($arg1, $arg2); 78 | $this->featureContext->assertPageContainsText($arg3); 79 | } 80 | 81 | /** 82 | * @Given MSI has been imported 83 | */ 84 | public function msiHasBeenImported() 85 | { 86 | $this->filesWithMsiToBeUpdatedAreAvailable(); 87 | $this->theMsiImportProcessHasBeenStarted(); 88 | } 89 | 90 | /** 91 | * @Given files with MSI to be updated are available 92 | * @Given files with MSI to be deleted are available 93 | */ 94 | public function filesWithMsiToBeUpdatedAreAvailable() 95 | { 96 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 97 | 'vendor/techdivision/import-sample-data/generic/data/msi/add-update/product-import-inventory-msi_20190423-100739_01.csv' 98 | ); 99 | } 100 | 101 | /** 102 | * @Given files with MSI to be replaced are available 103 | */ 104 | public function filesWithMsiToBeReplacedAreAvailable() 105 | { 106 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 107 | 'vendor/techdivision/import-sample-data/generic/data/msi/replace/product-import-inventory-msi_20190423-100739_01.csv' 108 | ); 109 | } 110 | 111 | /** 112 | * @Given the MSI import process has been started 113 | */ 114 | public function theMsiImportProcessHasBeenStarted() 115 | { 116 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 117 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_PRODUCTS_INVENTORY_MSI)); 118 | } 119 | 120 | /** 121 | * @Given the MSI deletion process has been started 122 | */ 123 | public function theMsiDeletionProcessHasBeenStarted() 124 | { 125 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 126 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_PRODUCTS_INVENTORY_MSI)); 127 | } 128 | 129 | /** 130 | * @Given the MSI replacement process has been started 131 | */ 132 | public function theMsiReplacementProcessHasBeenStarted() 133 | { 134 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 135 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_PRODUCTS_INVENTORY_MSI)); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/ProductFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use PHPUnit\Framework\Assert; 24 | use Behat\Behat\Context\Context; 25 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 26 | use TechDivision\Import\Utils\CommandNames; 27 | 28 | /** 29 | * Defines product features from the specific context. 30 | * 31 | * @author Tim Wagner 32 | * @copyright 2019 TechDivision GmbH 33 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 34 | * @link https://github.com/techdivision/import-cli-simple 35 | * @link http://www.techdivision.com 36 | */ 37 | class ProductFeatureContext implements Context 38 | { 39 | 40 | /** 41 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 42 | */ 43 | private $consoleContext; 44 | 45 | /** 46 | * @var \TechDivision\Import\Cli\Simple\Contexts\FeatureContext 47 | */ 48 | private $featureContext; 49 | 50 | /** @BeforeScenario */ 51 | public function before(BeforeScenarioScope $scope) 52 | { 53 | 54 | // load the environment 55 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 56 | $environment = $scope->getEnvironment(); 57 | 58 | // make the console and the feature context available 59 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 60 | $this->featureContext = $environment->getContext(FeatureContext::class); 61 | } 62 | 63 | /** 64 | * @Then title and price are :arg1,( ) 65 | * @Then title and price are :arg1, :arg2 66 | */ 67 | public function assertTitleAndPrice($arg1, $arg2 = null) 68 | { 69 | 70 | // load and validate the title 71 | /** @var \Behat\Mink\Element\NodeElement $title */ 72 | $title = $this->featureContext->getSession()->getPage()->find('css', 'title'); 73 | Assert::assertEquals($arg1, $title->getText()); 74 | 75 | // validate the price if the page has been loaded successfully 76 | if ($this->featureContext->getSession()->getStatusCode() === 200 && $arg2 !== null) { 77 | /** @var \Behat\Mink\Element\NodeElement $price */ 78 | $price = $this->featureContext->getSession()->getPage()->find('xpath', '//*[@class="price"]'); 79 | Assert::assertEquals($arg2, $price->getText()); 80 | } 81 | } 82 | 83 | /** 84 | * @Given products have been imported 85 | */ 86 | public function productsHaveBeenImported() 87 | { 88 | $this->filesWithProductsToBeUpdatedAreAvailable(); 89 | $this->theProductImportProcessHasBeenStarted(); 90 | } 91 | 92 | /** 93 | * @Given products have been replaced 94 | */ 95 | public function productsHaveBeenReplaced() 96 | { 97 | $this->filesWithProductsToBeReplacedAreAvailable(); 98 | $this->theProductImportProcessHasBeenStarted(); 99 | } 100 | 101 | /** 102 | * @Given files with products to be updated are available 103 | * @Given files with products to be deleted are available 104 | */ 105 | public function filesWithProductsToBeUpdatedAreAvailable() 106 | { 107 | for ($i = 1; $i < 5; $i++) { 108 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 109 | sprintf('vendor/techdivision/import-sample-data/generic/data/products/add-update/product-import_20161021-161909_0%s.csv', $i) 110 | ); 111 | } 112 | } 113 | 114 | /** 115 | * @Given files with products to be replaced are available 116 | */ 117 | public function filesWithProductsToBeReplacedAreAvailable() 118 | { 119 | for ($i = 1; $i < 5; $i++) { 120 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 121 | sprintf('vendor/techdivision/import-sample-data/generic/data/products/replace/product-import_20161021-161909_0%s.csv', $i) 122 | ); 123 | } 124 | } 125 | 126 | /** 127 | * @Given the product import process has been started 128 | */ 129 | public function theProductImportProcessHasBeenStarted() 130 | { 131 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 132 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_PRODUCTS)); 133 | } 134 | 135 | /** 136 | * @Given the product deletion process has been started 137 | */ 138 | public function theProductDeletionProcessHasBeenStarted() 139 | { 140 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 141 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_PRODUCTS)); 142 | } 143 | 144 | /** 145 | * @Given the product replacement process has been started 146 | */ 147 | public function theProductReplacementProcessHasBeenStarted() 148 | { 149 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 150 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_PRODUCTS)); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Contexts/TierPriceFeatureContext.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Contexts; 22 | 23 | use PHPUnit\Framework\Assert; 24 | use Behat\Behat\Context\Context; 25 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 26 | use TechDivision\Import\Utils\CommandNames; 27 | 28 | /** 29 | * Defines tier price features from the specific context. 30 | * 31 | * @author Tim Wagner 32 | * @copyright 2019 TechDivision GmbH 33 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 34 | * @link https://github.com/techdivision/import-cli-simple 35 | * @link http://www.techdivision.com 36 | */ 37 | class TierPriceFeatureContext implements Context 38 | { 39 | 40 | /** 41 | * @var \TechDivision\Import\Cli\Simple\Contexts\ConsoleContext 42 | */ 43 | private $consoleContext; 44 | 45 | /** 46 | * @var \TechDivision\Import\Cli\Simple\Contexts\FeatureContext 47 | */ 48 | private $featureContext; 49 | 50 | /** @BeforeScenario */ 51 | public function before(BeforeScenarioScope $scope) 52 | { 53 | 54 | // load the environment 55 | /** @var \Behat\Behat\Context\Environment\InitializedContextEnvironment $environment */ 56 | $environment = $scope->getEnvironment(); 57 | 58 | // make the console and the feature context available 59 | $this->consoleContext = $environment->getContext(ConsoleContext::class); 60 | $this->featureContext = $environment->getContext(FeatureContext::class); 61 | } 62 | 63 | /** 64 | * @Given tier prices have been imported 65 | */ 66 | public function tierPricesHaveBeenImported() 67 | { 68 | $this->filesWithTierPricesToBeUpdatedAreAvailable(); 69 | $this->theTierPriceImportProcessHasBeenStarted(); 70 | } 71 | 72 | /** 73 | * @Given files with tier prices to be updated are available 74 | * @Given files with tier prices to be deleted are available 75 | */ 76 | public function filesWithTierPricesToBeUpdatedAreAvailable() 77 | { 78 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 79 | 'vendor/techdivision/import-sample-data/generic/data/tier-prices/add-update/product-import-tier-price_20190425-131237_01.csv' 80 | ); 81 | } 82 | 83 | /** 84 | * @Given files with tier prices to be replaced are available 85 | */ 86 | public function filesWithTierPricesToBeReplacedAreAvailable() 87 | { 88 | $this->consoleContext->aThirdPartySystemHasCopiedTheFileIntoTheImportFolder( 89 | 'vendor/techdivision/import-sample-data/generic/data/tier-prices/replace/product-import-tier-price_20190425-131237_01.csv' 90 | ); 91 | } 92 | 93 | /** 94 | * @Given the tier price import process has been started 95 | */ 96 | public function theTierPriceImportProcessHasBeenStarted() 97 | { 98 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 99 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s add-update', CommandNames::IMPORT_PRODUCTS_TIER_PRICE)); 100 | } 101 | 102 | /** 103 | * @Given the tier price deletion process has been started 104 | */ 105 | public function theTierPriceDeletionProcessHasBeenStarted() 106 | { 107 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 108 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s delete', CommandNames::IMPORT_PRODUCTS_TIER_PRICE)); 109 | } 110 | 111 | /** 112 | * @Given the tier price replacement process has been started 113 | */ 114 | public function theTierReplacementProcessHasBeenStarted() 115 | { 116 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s', CommandNames::IMPORT_CREATE_OK_FILE)); 117 | $this->consoleContext->theCommandHasBeenExecuted(sprintf('bin/import-simple %s replace', CommandNames::IMPORT_PRODUCTS_TIER_PRICE)); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/DependencyInjection/ImportExtension.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\DependencyInjection; 22 | 23 | use Symfony\Component\Config\FileLocator; 24 | use Symfony\Component\DependencyInjection\ContainerBuilder; 25 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; 26 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; 27 | 28 | /** 29 | * The symfony extension implementation for the M2IF import acceptance testsuite. 30 | * 31 | * @author Tim Wagner 32 | * @copyright 2019 TechDivision GmbH 33 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 34 | * @link https://github.com/techdivision/import-cli-simple 35 | * @link http://www.techdivision.com 36 | */ 37 | class ImportExtension extends Extension 38 | { 39 | 40 | /** 41 | * Load's the bundles DI configuration. 42 | * 43 | * @param array $configs The array with the configuration 44 | * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container The container instance 45 | */ 46 | public function load(array $configs, ContainerBuilder $container) 47 | { 48 | $loader = new XmlFileLoader($container, new FileLocator(dirname(__DIR__) . '/Resources/config')); 49 | $loader->load('services.xml'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/ImportBundle.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright 2019 TechDivision GmbH 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple; 22 | 23 | use Symfony\Component\HttpKernel\Bundle\Bundle; 24 | 25 | /** 26 | * The symfony bundle implementation for the M2IF import acceptance testsuite. 27 | * 28 | * @author Tim Wagner 29 | * @copyright 2019 TechDivision GmbH 30 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 31 | * @link https://github.com/techdivision/import-cli-simple 32 | * @link http://www.techdivision.com 33 | */ 34 | class ImportBundle extends Bundle 35 | { 36 | } -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Resources/config/config.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: 3683cfb558a25439c8e973a89b1ce9d4 3 | form: false 4 | validation: false 5 | default_locale: en 6 | session: ~ -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Resources/config/config_dev.yml: -------------------------------------------------------------------------------- 1 | 2 | imports: 3 | - { resource: config.yml } 4 | 5 | framework: 6 | profiler: { only_exceptions: false } -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Resources/config/config_test.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: config_dev.yml } 3 | 4 | framework: 5 | test: ~ 6 | session: 7 | storage_id: session.storage.filesystem 8 | -------------------------------------------------------------------------------- /tests/acceptance/bootstrap/Resources/config/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | %env(SOURCE_DIR)% 8 | %env(DB_HOST)% 9 | %env(DB_PORT)% 10 | %env(DB_USER)% 11 | %env(DB_NAME)% 12 | %env(DB_PASSWORD)% 13 | %env(MAGENTO_INSTALL_EDITION)% 14 | %env(MAGENTO_INSTALL_VERSION)% 15 | %env(MAGENTO_BASE_URL)% 16 | %env(MAGENTO_INSTALL_DIR)% 17 | %env(MAGENTO_CONTAINER_NAME)% 18 | 19 | 20 | 21 | 22 | %magento.container_name% 23 | %magento.install_dir% 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/acceptance/features/attribute-set/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @attribute-set @add-update 2 | Feature: Add/Update Attribute-Sets 3 | To import attribute sets into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with attribute sets into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "attribute-set-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update Attribute Sets 12 | Given files with attribute sets to be updated are available 13 | And the attribute set import process has been started 14 | When the attribute set import process has been finished 15 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/attribute-set/delete.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @attribute-set @delete 2 | Feature: Delete Attribute-Sets 3 | To delete attribute sets from into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with attribute sets into a directory on the server and they should be deleted 6 | 7 | Rules: 8 | - Filename starts with "attribute-set-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Delete Attribute-Sets 12 | Given files with attribute sets to be deleted are available 13 | And the attribute set deletion process has been started 14 | When the attribute set deletion process has been finished 15 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/attribute-set/replace.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @attribute-set @replace 2 | Feature: Replace Attribute-Sets 3 | To replace attribute sets into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with attribute sets into a directory on the server and they should be replaced 6 | 7 | Rules: 8 | - Filename starts with "attribute-set-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Replace Attribute-Sets 12 | Given files with attribute sets to be replaced are available 13 | And the attribute set replacement process has been started 14 | When the attribute set replacement process has been finished 15 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/attribute/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @attribute @add-update 2 | Feature: Add/Update Attributes 3 | To import attributes into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with attributes into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "attribute-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update Attributes 12 | Given attribute sets have been imported 13 | And files with attributes to be updated are available 14 | And the attribute import process has been started 15 | When the process has been finished 16 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/attribute/delete.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @attribute @delete 2 | Feature: Delete Attributes 3 | To delete attributes from my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with attributes into a directory on the server and they should be deleted 6 | 7 | Rules: 8 | - Filename starts with "attribute-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Delete Attributes 12 | Given files with attributes to be deleted are available 13 | And the attribute deletion process has been started 14 | When the process has been finished 15 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/attribute/replace.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @attribute @replace 2 | Feature: Replace Attributes 3 | To replace attributes into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with attributes into a directory on the server and they should be replaced 6 | 7 | Rules: 8 | - Filename starts with "attribute-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Replace Attributes 12 | Given attribute sets have been imported 13 | And files with attributes to be replaced are available 14 | And the attribute replacement process has been started 15 | When the process has been finished 16 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/category/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @category @add-update 2 | Feature: Add/Update Categories 3 | To import categories into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with categories into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "category-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update Categories 12 | Given files with categories to be updated are available 13 | And the category import process has been started 14 | When the category import process has been finished 15 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/category/delete.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @category @delete 2 | Feature: Delete Categories 3 | To delete categories from my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with categories into a directory on the server and they should be deleted 6 | 7 | Rules: 8 | - Filename starts with "category-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Delete Categories 12 | Given files with categories to be deleted are available 13 | And the category deletion process has been started 14 | When the category deletion process has been finished 15 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/category/replace.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @category @replace 2 | Feature: Replace Categories 3 | To replace categories into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with categories into a directory on the server and they should be replaced 6 | 7 | Rules: 8 | - Filename starts with "category-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Replace Categories 12 | Given files with categories to be replaced are available 13 | And the category replacement process has been started 14 | When the category replacement process has been finished 15 | Then a success message has to be rendered -------------------------------------------------------------------------------- /tests/acceptance/features/customer-address/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @customer-address @add-update 2 | Feature: Add/Update Customer Addresses 3 | To import customer addresses into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with customer addresses into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "customer-address-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update Customer Addresses 12 | Given files with customer addresses to be updated are available 13 | And the customer address import process has been started 14 | When the customer address import process has been finished 15 | Then a success message has to be rendered 16 | And the magento customer grid index has been updated -------------------------------------------------------------------------------- /tests/acceptance/features/customer/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @customer @add-update 2 | Feature: Add/Update Customers 3 | To import customers into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with customers into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "customer-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update Customers 12 | Given files with customers to be updated are available 13 | And the customer import process has been started 14 | When the customer import process has been finished 15 | Then a success message has to be rendered 16 | And the magento customer grid index has been updated 17 | 18 | @customer-check 19 | Scenario Outline: Check Customer Login 20 | Given I am on the homepage 21 | And I go to "/customer/account" 22 | When I fill in for "email" 23 | And I fill in for "pass" 24 | And I press "send2" 25 | Then the response status code should be 26 | And I should be on "/customer/account/index/" 27 | And I should see "Jon Doe" 28 | 29 | Examples: 30 | | email | password | code | 31 | | "jondoe@example.com" | "appserver.i0" | 200 | -------------------------------------------------------------------------------- /tests/acceptance/features/customer/delete.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @customer @delete 2 | Feature: Delete Customer 3 | To delete customers from into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with customers into a directory on the server and they should be deleted 6 | 7 | Rules: 8 | - Filename starts with "customer-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Delete Customers 12 | Given files with customers to be deleted are available 13 | And the customer deletion process has been started 14 | When the customer deletion process has been finished 15 | Then a success message has to be rendered 16 | And the magento customer grid index has been updated 17 | 18 | @customer-check 19 | Scenario Outline: Check Customer Login 20 | Given I am on the homepage 21 | And I go to "/customer/account" 22 | When I fill in for "email" 23 | And I fill in for "pass" 24 | And I press "send2" 25 | Then the response status code should be 26 | And the url should match "/customer/account/login/referer" 27 | And I should see "Sign In" 28 | 29 | Examples: 30 | | email | password | code | 31 | | "jondoe@example.com" | "appserver.i0" | 200 | -------------------------------------------------------------------------------- /tests/acceptance/features/customer/replace.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @customer @replace 2 | Feature: Replace Customers 3 | To replace customers into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with customers into a directory on the server and they should be replaced 6 | 7 | Rules: 8 | - Filename starts with "customer-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Replace Customers 12 | Given customers have been imported 13 | And files with customers to be replaced are available 14 | And the customer replacement process has been started 15 | When the customer replacement process has been finished 16 | Then a success message has to be rendered 17 | And the magento customer grid index has been updated 18 | 19 | @customer-check 20 | Scenario Outline: Check Customer Login 21 | Given I am on the homepage 22 | And I go to "/customer/account" 23 | When I fill in for "email" 24 | And I fill in for "pass" 25 | And I press "send2" 26 | Then the response status code should be 27 | And I should be on "/customer/account/index/" 28 | And I should see "Jon Doe" 29 | 30 | Examples: 31 | | email | password | code | 32 | | "jondoe@example.com" | "appserver.i0" | 200 | -------------------------------------------------------------------------------- /tests/acceptance/features/msi/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.3 @ce @ee @msi @add-update 2 | Feature: Add/Update MSI 3 | To import MSI into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with MSI into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "product-import-inventory-msi_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update MSI 12 | Given attribute sets have been imported 13 | And attributes have been imported 14 | And categories have been imported 15 | And products have been replaced 16 | And files with MSI to be updated are available 17 | And the MSI import process has been started 18 | When the import process has been finished 19 | Then a success message has to be rendered 20 | And the magento index has been updated 21 | 22 | @product-check 23 | Scenario Outline: Check Products 24 | Given I am on the homepage 25 | When I go to 26 | Then the response status code should be 27 | And title, price and status are , <price>, <status> 28 | 29 | Examples: 30 | | page | title | status | price | code | 31 | | "/joust-duffle-bag.html" | "Joust Duffle Bag" | "IN STOCK" | "33,00 €" | 200 | 32 | | "/fusion-backpack.html" | "Fusion Backpack" | "IN STOCK" | "59,00 €" | 200 | 33 | | "/strive-shoulder-pack.html" | "Strive Shoulder Pack" | "OUT OF STOCK" | "32,00 €" | 200 | -------------------------------------------------------------------------------- /tests/acceptance/features/product/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @product @add-update 2 | Feature: Add/Update Products 3 | To import products into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with products into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "product-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update Products 12 | Given attribute sets have been imported 13 | And attributes have been imported 14 | And categories have been imported 15 | And files with products to be updated are available 16 | And the product import process has been started 17 | When the import process has been finished 18 | Then a success message has to be rendered 19 | And the magento index has been updated 20 | 21 | @product-check 22 | Scenario Outline: Check Products 23 | Given I am on the homepage 24 | When I go to <page> 25 | Then the response status code should be <code> 26 | And title and price are <title>, <price> 27 | 28 | Examples: 29 | | page | title | price | code | 30 | | "/joust-duffle-bag.html" | "404 Not Found" | | 404 | 31 | | "/fusion-backpack.html" | "Fusion Backpack" | "59,00 €" | 200 | 32 | | "/driven-backpack.html" | "Driven Backpack" | "36,00 €" | 200 | -------------------------------------------------------------------------------- /tests/acceptance/features/product/delete.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @product @delete 2 | Feature: Delete Products 3 | To delete products from my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with products into a directory on the server and they should be deleted 6 | 7 | Rules: 8 | - Filename starts with "product-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Delete Products 12 | Given files with products to be deleted are available 13 | And the product deletion process has been started 14 | When the product deletion process has been finished 15 | Then a success message has to be rendered 16 | And the magento index has been updated 17 | 18 | @product-check 19 | Scenario Outline: Check Products 20 | Given I am on the homepage 21 | When I go to <page> 22 | Then the response status code should be <code> 23 | And title and price are <title>, <price> 24 | 25 | Examples: 26 | | page | title | price | code | 27 | | "/fusion-backpack.html" | "404 Not Found" | | 404 | 28 | | "/driven-backpack.html" | "404 Not Found" | | 404 | -------------------------------------------------------------------------------- /tests/acceptance/features/product/replace.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @product @replace 2 | Feature: Replace Products 3 | To replace products into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with product into a directory on the server and they should be replaced 6 | 7 | Rules: 8 | - Filename starts with "product-import_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Replace Products 12 | Given attribute sets have been imported 13 | And attributes have been imported 14 | And categories have been imported 15 | And files with products to be replaced are available 16 | And the product replacement process has been started 17 | When the replacement process has been finished 18 | Then a success message has to be rendered 19 | And the magento index has been updated 20 | 21 | @product-check 22 | Scenario Outline: Check Products 23 | Given I am on the homepage 24 | When I go to <page> 25 | Then the response status code should be <code> 26 | And title and price are <title>, <price> 27 | 28 | Examples: 29 | | page | title | price | code | 30 | | "/joust-duffle-bag.html" | "Joust Duffle Bag" | "33,00 €" | 200 | 31 | | "/fusion-backpack.html" | "Fusion Backpack" | "59,00 €" | 200 | 32 | | "/driven-backpack.html" | "Driven Backpack" | "36,00 €" | 200 | -------------------------------------------------------------------------------- /tests/acceptance/features/tier-price/add-update.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @tier-price @add-update 2 | Feature: Add/Update Tier Prices 3 | To import tier prices into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with tier prices into a directory on the server and they should be imported 6 | 7 | Rules: 8 | - Filename starts with "product-import-tier-price_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Add/Update Tier Prices 12 | Given attribute sets have been imported 13 | And attributes have been imported 14 | And categories have been imported 15 | And products have been replaced 16 | And files with tier prices to be updated are available 17 | And the tier price import process has been started 18 | When the import process has been finished 19 | Then a success message has to be rendered 20 | And the magento index has been updated 21 | 22 | @product-check 23 | Scenario: Check Tier Price for Joust Duffle Bag 24 | Given I am on the homepage 25 | When I go to "/joust-duffle-bag.html" 26 | Then the response status code should be 200 27 | And I should see "Buy 4 for 29,00 € each and save 12%" 28 | 29 | @product-check 30 | Scenario: Check Tier Price for Drive Shoulder Pack 31 | Given I am on the homepage 32 | When I go to "/strive-shoulder-pack.html" 33 | Then the response status code should be 200 34 | And I should see "Buy 3 for 23,04 € each and save 28%" -------------------------------------------------------------------------------- /tests/acceptance/features/tier-price/delete.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @tier-price @delete 2 | Feature: Delete Tier Prices 3 | To delete tier prices into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with tier prices into a directory on the server and they should be deleted 6 | 7 | Rules: 8 | - Filename starts with "product-import-tier-price_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Delete Tier Prices 12 | Given attribute sets have been imported 13 | And attributes have been imported 14 | And categories have been imported 15 | And products have been replaced 16 | And files with tier prices to be updated are available 17 | And the tier price import process has been started 18 | And the import process has been finished 19 | And a success message has to be rendered 20 | And files with tier prices to be deleted are available 21 | And the tier price deletion process has been started 22 | When the deletion process has been finished 23 | Then a success message has to be rendered 24 | And the magento index has been updated 25 | 26 | @product-check 27 | Scenario: Check Tier Price for Joust Duffle Bag 28 | Given I am on the homepage 29 | When I go to "/joust-duffle-bag.html" 30 | Then the response status code should be 200 31 | And I should not see "Buy 4 for 29,00 € each and save 15%" 32 | 33 | @product-check 34 | Scenario: Check Tier Price for Drive Shoulder Pack 35 | Given I am on the homepage 36 | When I go to "/strive-shoulder-pack.html" 37 | Then the response status code should be 200 38 | And I should not see "Buy 3 for 23,04 € each and save 28%" -------------------------------------------------------------------------------- /tests/acceptance/features/tier-price/replace.feature: -------------------------------------------------------------------------------- 1 | @2.2 @2.3 @ce @ee @tier-price @replace 2 | Feature: Replace Tier Prices 3 | To replace tier prices into my Magento 2 instance 4 | As an e-commerce manager 5 | I simply want to put CSV files with tier prices into a directory on the server and they should be replaced 6 | 7 | Rules: 8 | - Filename starts with "product-import-tier-price_*" 9 | - The files are available in folder "var/importexport" 10 | 11 | Scenario: Replace Tier Prices 12 | Given attribute sets have been imported 13 | And attributes have been imported 14 | And categories have been imported 15 | And products have been replaced 16 | And files with tier prices to be updated are available 17 | And the tier price import process has been started 18 | And the import process has been finished 19 | And a success message has to be rendered 20 | And files with tier prices to be replaced are available 21 | And the tier price import process has been started 22 | When the import process has been finished 23 | Then a success message has to be rendered 24 | And the magento index has been updated 25 | 26 | @product-check 27 | Scenario: Check Tier Price for Joust Duffle Bag 28 | Given I am on the homepage 29 | When I go to "/joust-duffle-bag.html" 30 | Then the response status code should be 200 31 | And I should not see "Buy 4 for 29,00 € each and save 15%" 32 | 33 | @product-check 34 | Scenario: Check Tier Price for Drive Shoulder Pack 35 | Given I am on the homepage 36 | When I go to "/strive-shoulder-pack.html" 37 | Then the response status code should be 200 38 | And I should see "Buy 3 for 24,00 € each and save 25%" -------------------------------------------------------------------------------- /tests/integration/Connection/PDOConnectionWrapper.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /** 4 | * TechDivision\Import\Cli\Simple\Connection\PDOConnectionWrapper 5 | * 6 | * NOTICE OF LICENSE 7 | * 8 | * This source file is subject to the Open Software License (OSL 3.0) 9 | * that is available through the world-wide-web at this URL: 10 | * http://opensource.org/licenses/osl-3.0.php 11 | * 12 | * PHP version 5 13 | * 14 | * @author Tim Wagner <t.wagner@techdivision.com> 15 | * @copyright 2016 TechDivision GmbH <info@techdivision.com> 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-product-cli 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Connection; 22 | 23 | use TechDivision\Import\Dbal\Connection\ConnectionInterface; 24 | 25 | /** 26 | * A wrapper for a \PDO connection instance that ignores transaction handling for testing purposes only. 27 | * 28 | * @author Tim Wagner <t.wagner@techdivision.com> 29 | * @copyright 2016 TechDivision GmbH <info@techdivision.com> 30 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 31 | * @link https://github.com/techdivision/import-product-cli 32 | * @link http://www.techdivision.com 33 | */ 34 | class PDOConnectionWrapper implements ConnectionInterface 35 | { 36 | 37 | /** 38 | * The \PDO connection that has to be wrapped. 39 | * 40 | * @var \PDO 41 | */ 42 | protected $connection; 43 | 44 | /** 45 | * Initializes the wrapper with the passed PDO connection. 46 | * 47 | * @param \PDO $connection The PDO connection to wrap 48 | */ 49 | public function __construct(\PDO $connection) 50 | { 51 | $this->connection = $connection; 52 | } 53 | 54 | /** 55 | * Returns the wrapped PDO connection instance. 56 | * 57 | * @return \PDO The wrapped PDO connection 58 | */ 59 | public function getConnection() 60 | { 61 | return $this->connection; 62 | } 63 | 64 | /** 65 | * Turns off autocommit mode. While autocommit mode is turned off, changes made to the database via 66 | * the instance are not committed until you end the transaction by calling commit(). 67 | * 68 | * @return boolean Returns TRUE on success or FALSE on failure 69 | * @link http://php.net/manual/en/pdo.begintransaction.php 70 | */ 71 | public function beginTransaction() 72 | { 73 | return true; 74 | } 75 | 76 | /** 77 | * Commits a transaction, returning the database connection to autocommit mode until the next call 78 | * to beginTransaction() starts a new transaction. 79 | * 80 | * @return void 81 | * @throws \PDOException Throws a PDOException if there is no active transaction. 82 | * @link http://php.net/manual/en/pdo.commit.php 83 | */ 84 | public function commit() 85 | { 86 | } 87 | 88 | /** 89 | * Rolls back the current transaction, as initiated by beginTransaction(). 90 | * 91 | * @return boolean Returns TRUE on success or FALSE on failure 92 | * @throws \PDOException Throws a PDOException if there is no active transaction 93 | */ 94 | public function rollBack() 95 | { 96 | return true; 97 | } 98 | 99 | /** 100 | * Fetch the SQLSTATE associated with the last operation on the database handle. 101 | * 102 | * @return mixed The error code of the last operation of the database handle 103 | * @link http://php.net/manual/en/pdo.errorcode.php 104 | */ 105 | public function errorCode() 106 | { 107 | return $this->connection->errorCode(); 108 | } 109 | 110 | /** 111 | * Fetch extended error information associated with the last operation on the database handle. 112 | * 113 | * @return array Returns an array of error information about the last operation performed by this database handle 114 | * @link http://php.net/manual/en/pdo.errorinfo.php 115 | */ 116 | public function errorInfo() 117 | { 118 | return $this->connection->errorInfo(); 119 | } 120 | 121 | /** 122 | * Executes an SQL statement, returning a result set as a PDOStatement object. 123 | * 124 | * @param string $statement The SQL statement to prepare and execute 125 | * 126 | * @return \PDOStatement|boolean Returns a \PDOStatement object, or FALSE on failure 127 | * @link http://php.net/manual/en/pdo.query.php 128 | */ 129 | public function query($statement) 130 | { 131 | return call_user_func_array(array($this->connection, 'query'), func_get_args()); 132 | } 133 | 134 | /** 135 | * Prepares a statement for execution and returns a statement object. 136 | * 137 | * @param string $statement This must be a valid SQL statement template for the target database server 138 | * @param array $driverOptions This array holds one or more key=>value pairs to set attribute values for the \PDOStatement object that this method returns 139 | * 140 | * @return \PDOStatement If the database server successfully prepares the statement, this method returns a \PDOStatement object 141 | * @throws \PDOException Throws a PDOException if the database server cannot successfully prepare the statement 142 | * @link http://php.net/manual/en/pdo.prepare.php 143 | */ 144 | public function prepare($statement, array $driverOptions = array()) 145 | { 146 | return $this->connection->prepare($statement, $driverOptions); 147 | } 148 | 149 | /** 150 | * Execute an SQL statement and return the number of affected rows. 151 | * 152 | * @param string $statement The SQL statement to prepare and execute 153 | * 154 | * @return integer The number of affected rows 155 | * @link http://php.net/manual/en/pdo.exec.php 156 | */ 157 | public function exec($statement) 158 | { 159 | return $this->connection->exec($statement); 160 | } 161 | 162 | /** 163 | * Returns the ID of the last inserted row or sequence value. 164 | * 165 | * @param string $name Name of the sequence object from which the ID should be returned 166 | * 167 | * @return string A string representing the row ID of the last row that was inserted into the database 168 | * @link http://php.net/manual/en/pdo.lastinsertid.php 169 | */ 170 | public function lastInsertId($name = null) 171 | { 172 | return $this->connection->lastInsertId($name); 173 | } 174 | 175 | /** 176 | * Quotes a string for use in a query. 177 | * 178 | * @param string $string The string to be quoted 179 | * @param integer $parameterType Provides a data type hint for drivers that have alternate quoting styles 180 | * 181 | * @return string|boolean Returns a quoted string that is theoretically safe to pass into an SQL statement or FALSE if the driver does not support quoting in this way 182 | * @link http://php.net/manual/en/pdo.quote.php 183 | */ 184 | public function quote($string, $parameterType = \PDO::PARAM_STR) 185 | { 186 | return $this->connection->quote($string, $parameterType); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /tests/integration/Connection/TestConnectionFactory.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /** 4 | * TechDivision\Import\Cli\Simple\Connection\TestConnectionFactory 5 | * 6 | * NOTICE OF LICENSE 7 | * 8 | * This source file is subject to the Open Software License (OSL 3.0) 9 | * that is available through the world-wide-web at this URL: 10 | * http://opensource.org/licenses/osl-3.0.php 11 | * 12 | * PHP version 5 13 | * 14 | * @author Tim Wagner <t.wagner@techdivision.com> 15 | * @copyright 2016 TechDivision GmbH <info@techdivision.com> 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-cli-simple 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Connection; 22 | 23 | use TechDivision\Import\Configuration\ConfigurationInterface; 24 | 25 | /** 26 | * The test connection factory implementation. 27 | * 28 | * @author Tim Wagner <t.wagner@techdivision.com> 29 | * @copyright 2016 TechDivision GmbH <info@techdivision.com> 30 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 31 | * @link https://github.com/techdivision/import-cli-simple 32 | * @link http://www.techdivision.com 33 | */ 34 | class TestConnectionFactory 35 | { 36 | 37 | /** 38 | * Create's and return's the connection to use. 39 | * 40 | * @param \TechDivision\Import\Configuration\ConfigurationInterface $configuration The configuration with the data to create the connection with 41 | * 42 | * @return \TechDivision\Import\Cli\Simple\Connection\PDOConnectionWrapper The initialized connection 43 | */ 44 | public static function createConnection(ConfigurationInterface $configuration) 45 | { 46 | 47 | // initialize the PDO connection 48 | $dsn = $configuration->getDatabase()->getDsn(); 49 | $username = $configuration->getDatabase()->getUsername(); 50 | $password = $configuration->getDatabase()->getPassword(); 51 | $connection = new \PDO($dsn, $username, $password); 52 | $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); 53 | 54 | // reurn the wrapped PDO connection 55 | return new PDOConnectionWrapper($connection); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/integration/Product/AddUpdateOperationTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | /** 4 | * TechDivision\Import\Cli\Simple\Product\AddUpdateOperationTest 5 | * 6 | * NOTICE OF LICENSE 7 | * 8 | * This source file is subject to the Open Software License (OSL 3.0) 9 | * that is available through the world-wide-web at this URL: 10 | * http://opensource.org/licenses/osl-3.0.php 11 | * 12 | * PHP version 5 13 | * 14 | * @author Tim Wagner <t.wagner@techdivision.com> 15 | * @copyright 2016 TechDivision GmbH <info@techdivision.com> 16 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 17 | * @link https://github.com/techdivision/import-product 18 | * @link http://www.techdivision.com 19 | */ 20 | 21 | namespace TechDivision\Import\Cli\Simple\Product; 22 | 23 | use TechDivision\Import\Utils\OperationKeys; 24 | use TechDivision\Import\Product\Utils\ColumnKeys; 25 | use TechDivision\Import\Product\Utils\MemberNames; 26 | use TechDivision\Import\Cli\Simple\AbstractIntegrationTest; 27 | 28 | /** 29 | * Test class for the product functionality. 30 | * 31 | * @author Tim Wagner <t.wagner@techdivision.com> 32 | * @copyright 2016 TechDivision GmbH <info@techdivision.com> 33 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 34 | * @link https://github.com/techdivision/import-product 35 | * @link http://www.techdivision.com 36 | */ 37 | class AddUpdateOperationTest extends AbstractIntegrationTest 38 | { 39 | 40 | /** 41 | * The operation that has to be tested. 42 | * 43 | * @return string The operation name 44 | */ 45 | protected function getOperationName() 46 | { 47 | return OperationKeys::ADD_UPDATE; 48 | } 49 | 50 | /** 51 | * The configuration used for the test case. 52 | * 53 | * @return string The absolute path to the configuration file 54 | * @see \TechDivision\Import\Cli\Simple\AbstractIntegrationTest::getConfigurationFile() 55 | */ 56 | protected function getConfigurationFile() 57 | { 58 | return sprintf('%s/ce/2.3.x/conf/product/add-update-operation/configuration.json', $this->getFilesDir()); 59 | } 60 | 61 | /** 62 | * The additional DI configuration used for the test case. 63 | * 64 | * @return string The absolute path to the DI configuration file 65 | * @see \TechDivision\Import\Cli\Simple\AbstractIntegrationTest::getDiConfigurationFile() 66 | */ 67 | protected function getDiConfigurationFile() 68 | { 69 | return sprintf('%s/ce/2.3.x/conf/services.xml', $this->getFilesDir()); 70 | } 71 | 72 | /** 73 | * Test's the add-update operation with a simple product without category relation. 74 | * 75 | * @return void 76 | */ 77 | public function testAddUpdateWithSimpleProduct() 78 | { 79 | 80 | // prepare the file we want to import 81 | $filename = $this->prepareFileWithSingleRow(); 82 | 83 | // process the import operation 84 | $this->processImport(); 85 | 86 | // make sure, the flag file for a successfull import exists 87 | $this->assertCount(1, glob(sprintf('%s/*/%s.imported', $this->getTmpDir(), basename($filename)))); 88 | 89 | // initialize the product bunch processor instance 90 | $productBunchProcessor = $this->getProductBunchProcessor(); 91 | 92 | // try to load the imported product by its SKU 93 | $product = $productBunchProcessor->loadProduct($sku = 'TEST-SKU-000001'); 94 | 95 | // assert the expected product entity data 96 | $this->assertArrayHasKey('sku', $product); 97 | $this->assertSame($sku, $product[MemberNames::SKU]); 98 | $this->assertSame(4, (integer) $product[MemberNames::ATTRIBUTE_SET_ID]); 99 | $this->assertSame('simple', $product[MemberNames::TYPE_ID]); 100 | $this->assertSame(0, (integer) $product[MemberNames::HAS_OPTIONS]); 101 | $this->assertSame(0, (integer) $product[MemberNames::REQUIRED_OPTIONS]); 102 | $this->assertSame('2016-10-24 12:36:00', $product[MemberNames::CREATED_AT]); 103 | $this->assertSame('2016-10-24 12:36:00', $product[MemberNames::UPDATED_AT]); 104 | } 105 | 106 | /** 107 | * Test's the add-update operation with a simple product with one category relation. 108 | * 109 | * @return void 110 | */ 111 | public function testAddUpdateWithSimpleProductAndCategoryRelation() 112 | { 113 | 114 | // create a new category 115 | $categoryId = (integer) $this->createCategory('Testcategory'); 116 | 117 | // prepare the file we want to import 118 | $filename = $this->prepareFileWithSingleRow(array(ColumnKeys::CATEGORIES => '"""Default Category""/Testcategory"')); 119 | 120 | // process the import operation 121 | $this->processImport(); 122 | 123 | // make sure, the flag file for a successfull import exists 124 | $this->assertCount(1, glob(sprintf('%s/*/%s.imported', $this->getTmpDir(), basename($filename)))); 125 | 126 | // initialize the product bunch processor instance 127 | $productBunchProcessor = $this->getProductBunchProcessor(); 128 | 129 | // try to load the imported product by its SKU 130 | $product = $productBunchProcessor->loadProduct($sku = 'TEST-SKU-000001'); 131 | 132 | // try to load the category product relations by their SKU and count them 133 | $categoryProducts = iterator_to_array($productBunchProcessor->getCategoryProductsBySku($sku)); 134 | $this->assertCount(1, $categoryProducts); 135 | 136 | // load the second found category product relation 137 | $categoryProduct = array_shift($categoryProducts); 138 | 139 | // assert the values 140 | $this->assertSame($product[MemberNames::ENTITY_ID], $categoryProduct[MemberNames::PRODUCT_ID]); 141 | $this->assertSame($categoryId, (integer) $categoryProduct[MemberNames::CATEGORY_ID]); 142 | } 143 | 144 | /** 145 | * Test's the add-update operation with a simple product with a changed category relation. 146 | * 147 | * @return void 148 | */ 149 | public function testAddUpdateWithSimpleProductAndChangedCategoryRelation() 150 | { 151 | 152 | // create two new categories 153 | $this->createCategory('Testcategory 1'); 154 | $categoryId = (integer) $this->createCategory('Testcategory 2'); 155 | 156 | // initialize the array for for the names of the import files 157 | $filenames = array( 158 | $this->prepareFileWithSingleRow(array(ColumnKeys::CATEGORIES => '"""Default Category""/""Testcategory 1"""')), 159 | $this->prepareFileWithSingleRow(array(ColumnKeys::CATEGORIES => '"""Default Category""/""Testcategory 2"""')) 160 | ); 161 | 162 | // invoke the import operation twice to import both files 163 | $this->processImport(sizeof($filenames)); 164 | 165 | // make sure, the flag file for a successfull import exists 166 | foreach ($filenames as $filename) { 167 | $this->assertCount(1, glob(sprintf('%s/*/%s.imported', $this->getTmpDir(), basename($filename)))); 168 | } 169 | 170 | // initialize the product bunch processor instance 171 | $productBunchProcessor = $this->getProductBunchProcessor(); 172 | 173 | // try to load the imported product by its SKU 174 | $product = $productBunchProcessor->loadProduct($sku = 'TEST-SKU-000001'); 175 | 176 | // try to load the category product relations by their SKU and count them 177 | $categoryProducts = iterator_to_array($productBunchProcessor->getCategoryProductsBySku($sku)); 178 | $this->assertCount(1, $categoryProducts); 179 | 180 | // load the second found category product relation 181 | $categoryProduct = array_shift($categoryProducts); 182 | 183 | // assert the values 184 | $this->assertSame($product[MemberNames::ENTITY_ID], $categoryProduct[MemberNames::PRODUCT_ID]); 185 | $this->assertSame($categoryId, (integer) $categoryProduct[MemberNames::CATEGORY_ID]); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /tests/integration/_files/ce/2.3.x/conf/product/add-update-operation/configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "magento-edition": "CE", 3 | "magento-version": "2.3.3", 4 | "archive-artefacts" : true, 5 | "debug-mode" : true, 6 | "entity-type-code" : "catalog_product", 7 | "system-name" : "system-name/product-import", 8 | "empty-attribute-value-constant": "__EMPTY__VALUE__", 9 | "databases" : [], 10 | "listeners" : [ 11 | { 12 | "app.set.up" : [ 13 | "import.listener.initialize.registry" 14 | ], 15 | "app.tear.down" : [ 16 | "import.listener.clear.registry" 17 | ], 18 | "action.create.success.create.url_rewrite": [ 19 | "import.listener.cache.url.rewrite" 20 | ], 21 | "action.create.success.update.url_rewrite": [ 22 | "import.listener.cache.url.rewrite" 23 | ], 24 | "action.create.success.delete.url_rewrite": [ 25 | "import.listener.cache.url.rewrite" 26 | ] 27 | } 28 | ], 29 | "loggers": { 30 | "system": { 31 | "id": "import.logger.factory.monolog", 32 | "channel-name": "logger/system", 33 | "handlers": [ 34 | { 35 | "id" : "import.logger.handler.null" 36 | } 37 | ] 38 | } 39 | }, 40 | "extension-libraries" : [ 41 | "techdivision/import-app-simple", 42 | "techdivision/import", 43 | "techdivision/import-attribute", 44 | "techdivision/import-category", 45 | "techdivision/import-product", 46 | "techdivision/import-product-bundle", 47 | "techdivision/import-product-link", 48 | "techdivision/import-product-media", 49 | "techdivision/import-product-variant", 50 | "techdivision/import-product-url-rewrite" 51 | ], 52 | "finder-mappings": { 53 | "store": { 54 | "TechDivision\\Import\\Utils\\SqlStatementKeys::STORES": "import.repository.finder.factory.yielded" 55 | }, 56 | "url_rewrite": { 57 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITES": "import.repository.finder.factory.yielded", 58 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITE_BY_REQUEST_PATH_AND_STORE_ID": "import.repository.finder.factory.unique.cached.static", 59 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITES_BY_ENTITY_TYPE_AND_ENTITY_ID": "import.repository.finder.factory.yielded", 60 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITES_BY_ENTITY_TYPE_AND_ENTITY_ID_AND_STORE_ID": "import.repository.finder.factory.yielded" 61 | }, 62 | "catalog_product": { 63 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DATETIMES": "import.repository.finder.factory.yielded", 64 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DECIMALS": "import.repository.finder.factory.yielded", 65 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_INTS": "import.repository.finder.factory.yielded", 66 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_TEXTS": "import.repository.finder.factory.yielded", 67 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHARS": "import.repository.finder.factory.yielded", 68 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCTS": "import.repository.finder.factory.yielded", 69 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT": "import.repository.finder.factory.unique", 70 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DATETIMES_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 71 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DECIMALS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 72 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_INTS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 73 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_TEXTS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 74 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHARS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 75 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHAR_BY_ATTRIBUTE_CODE_AND_ENTITY_TYPE_ID_AND_STORE_ID": "import.repository.finder.factory.yielded", 76 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHAR_BY_ATTRIBUTE_CODE_AND_ENTITY_TYPE_ID_AND_STORE_ID_AND_VALUE" : "import.repository.finder.factory.unique", 77 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHAR_BY_ATTRIBUTE_CODE_AND_ENTITY_TYPE_ID_AND_STORE_ID_AND_PK" : "import.repository.finder.factory.unique" 78 | }, 79 | "product_media_gallery": { 80 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERIES": "import.repository.finder.factory.yielded", 81 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERY": "import.repository.finder.factory.unique", 82 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERIES_BY_SKU": "import.repository.finder.factory.yielded", 83 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERY_VALUE": "import.repository.finder.factory.unique", 84 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERY_VALUES": "import.repository.finder.factory.yielded" 85 | }, 86 | "product_url_rewrite": { 87 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITE_PRODUCT_CATEGORIES": "import.repository.finder.factory.yielded", 88 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITES_BY_SKU": "import.repository.finder.factory.yielded", 89 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITE_PRODUCT_CATEGORY": "import.repository.finder.factory.unique", 90 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITE_PRODUCT_CATEGORIES_BY_SKU": "import.repository.finder.factory.yielded" 91 | } 92 | }, 93 | "shortcuts": { 94 | "ce": { 95 | "catalog_product": { 96 | "add-update": [ 97 | "ce/catalog_product/add-update" 98 | ] 99 | } 100 | } 101 | }, 102 | "operations" : { 103 | "ce": { 104 | "catalog_product" : { 105 | "add-update": { 106 | "plugins" : { 107 | "global-data": { 108 | "id": "import.plugin.global.data" 109 | }, 110 | "subject": { 111 | "id": "import.plugin.subject", 112 | "subjects": [ 113 | { 114 | "id": "import.subject.move.files", 115 | "file-resolver": { 116 | "prefix": "product-import" 117 | }, 118 | "ok-file-needed": false 119 | }, 120 | { 121 | "id": "import_product.subject.bunch", 122 | "file-resolver": { 123 | "prefix": "product-import" 124 | }, 125 | "params" : { 126 | "clean-up-category-product-relations" : true 127 | }, 128 | "observers": [ 129 | { 130 | "import": [ 131 | "import.observer.attribute.set", 132 | "import.observer.additional.attribute", 133 | "import_product.observer.url.key", 134 | "import_product.observer.quality.and.stock.status", 135 | "import_product.observer.product", 136 | "import_product.observer.category.product", 137 | "import_product.observer.product.attribute.update", 138 | "import_product.observer.clean.up" 139 | ] 140 | } 141 | ] 142 | } 143 | ] 144 | } 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /tests/integration/_files/ce/2.3.x/conf/services.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8" ?> 2 | <container xmlns="http://symfony.com/schema/dic/services" 3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 | xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> 5 | 6 | <services> 7 | 8 | <service id="output" class="Symfony\Component\Console\Output\NullOutput"/> 9 | <service id="import.logger.handler.null" class="Monolog\Handler\NullHandler"/> 10 | 11 | <service id="connection" class="TechDivision\Import\Cli\Simple\Connection\PDOConnectionWrapper"> 12 | <factory class="TechDivision\Import\Cli\Simple\Connection\TestConnectionFactory" method="createConnection"/> 13 | <argument type="service" id="configuration"/> 14 | </service> 15 | 16 | </services> 17 | 18 | </container> -------------------------------------------------------------------------------- /tests/integration/_files/ce/2.3.x/conf/url-rewrite/add-update-operation/configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "magento-edition": "CE", 3 | "magento-version": "2.3.0", 4 | "archive-artefacts" : true, 5 | "debug-mode" : true, 6 | "entity-type-code" : "catalog_product", 7 | "system-name" : "system-name/product-import", 8 | "empty-attribute-value-constant": "__EMPTY__VALUE__", 9 | "databases" : [], 10 | "listeners" : [ 11 | { 12 | "app.set.up" : [ 13 | "import.listener.initialize.registry" 14 | ], 15 | "app.tear.down" : [ 16 | "import.listener.clear.registry" 17 | ], 18 | "action.create.success.create.url_rewrite": [ 19 | "import.listener.cache.url.rewrite" 20 | ], 21 | "action.create.success.update.url_rewrite": [ 22 | "import.listener.cache.url.rewrite" 23 | ], 24 | "action.create.success.delete.url_rewrite": [ 25 | "import.listener.cache.url.rewrite" 26 | ] 27 | } 28 | ], 29 | "loggers": { 30 | "system": { 31 | "id": "import.logger.factory.monolog", 32 | "channel-name": "logger/system", 33 | "handlers": [ 34 | { 35 | "id" : "import.logger.handler.null" 36 | } 37 | ] 38 | } 39 | }, 40 | "extension-libraries" : [ 41 | "techdivision/import-app-simple", 42 | "techdivision/import", 43 | "techdivision/import-attribute", 44 | "techdivision/import-category", 45 | "techdivision/import-product", 46 | "techdivision/import-product-bundle", 47 | "techdivision/import-product-link", 48 | "techdivision/import-product-media", 49 | "techdivision/import-product-variant", 50 | "techdivision/import-product-url-rewrite" 51 | ], 52 | "finder-mappings": { 53 | "store": { 54 | "TechDivision\\Import\\Utils\\SqlStatementKeys::STORES": "import.repository.finder.factory.yielded" 55 | }, 56 | "url_rewrite": { 57 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITES": "import.repository.finder.factory.yielded", 58 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITE_BY_REQUEST_PATH_AND_STORE_ID": "import.repository.finder.factory.unique.cached.static", 59 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITES_BY_ENTITY_TYPE_AND_ENTITY_ID": "import.repository.finder.factory.yielded", 60 | "TechDivision\\Import\\Utils\\SqlStatementKeys::URL_REWRITES_BY_ENTITY_TYPE_AND_ENTITY_ID_AND_STORE_ID": "import.repository.finder.factory.yielded" 61 | }, 62 | "catalog_product": { 63 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DATETIMES": "import.repository.finder.factory.yielded", 64 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DECIMALS": "import.repository.finder.factory.yielded", 65 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_INTS": "import.repository.finder.factory.yielded", 66 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_TEXTS": "import.repository.finder.factory.yielded", 67 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHARS": "import.repository.finder.factory.yielded", 68 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCTS": "import.repository.finder.factory.yielded", 69 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT": "import.repository.finder.factory.unique", 70 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DATETIMES_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 71 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_DECIMALS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 72 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_INTS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 73 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_TEXTS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 74 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHARS_BY_PK_AND_STORE_ID": "import.repository.finder.factory.yielded", 75 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHAR_BY_ATTRIBUTE_CODE_AND_ENTITY_TYPE_ID_AND_STORE_ID": "import.repository.finder.factory.yielded", 76 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHAR_BY_ATTRIBUTE_CODE_AND_ENTITY_TYPE_ID_AND_STORE_ID_AND_VALUE" : "import.repository.finder.factory.unique", 77 | "TechDivision\\Import\\Product\\Utils\\SqlStatementKeys::PRODUCT_VARCHAR_BY_ATTRIBUTE_CODE_AND_ENTITY_TYPE_ID_AND_STORE_ID_AND_PK" : "import.repository.finder.factory.unique" 78 | }, 79 | "product_media_gallery": { 80 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERIES": "import.repository.finder.factory.yielded", 81 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERY": "import.repository.finder.factory.unique", 82 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERIES_BY_SKU": "import.repository.finder.factory.yielded", 83 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERY_VALUE": "import.repository.finder.factory.unique", 84 | "TechDivision\\Import\\Product\\Media\\Utils\\SqlStatementKeys::PRODUCT_MEDIA_GALLERY_VALUES": "import.repository.finder.factory.yielded" 85 | }, 86 | "product_url_rewrite": { 87 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITE_PRODUCT_CATEGORIES": "import.repository.finder.factory.yielded", 88 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITES_BY_SKU": "import.repository.finder.factory.yielded", 89 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITE_PRODUCT_CATEGORY": "import.repository.finder.factory.unique", 90 | "TechDivision\\Import\\Product\\UrlRewrite\\Utils\\SqlStatementKeys::URL_REWRITE_PRODUCT_CATEGORIES_BY_SKU": "import.repository.finder.factory.yielded" 91 | } 92 | }, 93 | "shortcuts": { 94 | "ce": { 95 | "catalog_product": { 96 | "add-update": [ 97 | "ce/catalog_product/add-update" 98 | ] 99 | } 100 | } 101 | }, 102 | "operations" : { 103 | "ce": { 104 | "catalog_product": { 105 | "add-update": { 106 | "plugins" : { 107 | "global-data": { 108 | "id": "import.plugin.global.data" 109 | }, 110 | "subject": { 111 | "id": "import.plugin.subject", 112 | "subjects": [ 113 | { 114 | "id": "import.subject.move.files", 115 | "identifier": "move-files", 116 | "file-resolver": { 117 | "prefix": "product-import" 118 | }, 119 | "ok-file-needed": false 120 | }, 121 | { 122 | "id": "import_product.subject.bunch", 123 | "file-resolver": { 124 | "prefix": "product-import" 125 | }, 126 | "observers": [ 127 | { 128 | "import": [ 129 | "import.observer.attribute.set", 130 | "import.observer.additional.attribute", 131 | "import_product.observer.url.key", 132 | "import_product.observer.quality.and.stock.status", 133 | "import_product.observer.product", 134 | "import_product.observer.product.attribute.update", 135 | "import_product_url_rewrite.observer.product.url.rewrite", 136 | "import_product.observer.clean.up" 137 | ] 138 | } 139 | ] 140 | }, 141 | { 142 | "id": "import_product_url_rewrite.subject.url.rewrite", 143 | "file-resolver": { 144 | "prefix": "url-rewrite" 145 | }, 146 | "observers": [ 147 | { 148 | "import": [ 149 | "import_product_url_rewrite.observer.url.rewrite.update" 150 | ] 151 | } 152 | ] 153 | } 154 | ] 155 | } 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /tests/unit/.gitempty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techdivision/import-cli-simple/17332c86aea8b906ec9169e97459f8a5fd84271f/tests/unit/.gitempty --------------------------------------------------------------------------------