├── .codeclimate.yml
├── .github
├── phpunit9-integration.xml
└── workflows
│ └── tests.yml
├── .gitignore
├── LICENSE
├── README.md
├── auth.json
├── composer.json
├── grumphp.yml
├── phpcs.xml
├── phpstan.neon
├── registration.php
├── src
├── Catalog
│ ├── CategoryBuilder.php
│ ├── CategoryFixture.php
│ ├── CategoryFixturePool.php
│ ├── CategoryFixtureRollback.php
│ ├── FulltextIndex.php
│ ├── IndexFailed.php
│ ├── OptionBuilder.php
│ ├── OptionFixture.php
│ ├── OptionFixturePool.php
│ ├── OptionFixtureRollback.php
│ ├── ProductBuilder.php
│ ├── ProductFixture.php
│ ├── ProductFixturePool.php
│ └── ProductFixtureRollback.php
├── Checkout
│ ├── CartBuilder.php
│ └── CustomerCheckout.php
├── Core
│ └── ConfigFixture.php
├── Customer
│ ├── AddressBuilder.php
│ ├── CustomerBuilder.php
│ ├── CustomerFixture.php
│ ├── CustomerFixturePool.php
│ └── CustomerFixtureRollback.php
├── Sales
│ ├── CreditmemoBuilder.php
│ ├── CreditmemoFixture.php
│ ├── CreditmemoFixturePool.php
│ ├── InvoiceBuilder.php
│ ├── InvoiceFixture.php
│ ├── InvoiceFixturePool.php
│ ├── OrderBuilder.php
│ ├── OrderFixture.php
│ ├── OrderFixturePool.php
│ ├── OrderFixtureRollback.php
│ ├── ShipmentBuilder.php
│ ├── ShipmentFixture.php
│ └── ShipmentFixturePool.php
├── Theme
│ └── ThemeFixture.php
└── etc
│ └── module.xml
└── tests
├── Catalog
├── CategoryBuilderTest.php
├── CategoryFixturePoolTest.php
├── IndexerErrorsTest.php
├── OptionBuilderTest.php
├── OptionFixturePoolTest.php
├── OptionFixtureRollbackTest.php
├── ProductBuilderTest.php
├── ProductFixturePoolTest.php
└── ProductFixtureRollbackTest.php
├── Checkout
├── CartBuilderTest.php
└── CustomerCheckoutTest.php
├── Core
└── ConfigFixtureTest.php
├── Customer
├── CustomerBuilderTest.php
├── CustomerFixturePoolTest.php
└── CustomerFixtureRollbackTest.php
├── Sales
├── CreditmemoBuilderTest.php
├── CreditmemoFixturePoolTest.php
├── InvoiceBuilderTest.php
├── InvoiceFixturePoolTest.php
├── OrderBuilderTest.php
├── OrderFixturePoolRollbackTest.php
├── OrderFixturePoolTest.php
├── ShipmentBuilderTest.php
└── ShipmentFixturePoolTest.php
├── Theme
├── ThemeFixtureTest.php
└── _files
│ └── design
│ └── frontend
│ └── Custom
│ └── default
│ ├── registration.php
│ └── theme.xml
├── install-config-mysql.php
└── phpunit.xml.dist
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | ---
2 | engines:
3 | duplication:
4 | enabled: true
5 | fixme:
6 | enabled: true
7 | phpmd:
8 | enabled: true
9 | checks:
10 | CleanCode/StaticAccess:
11 | enabled: false
12 | phan:
13 | enabled: true
14 | config:
15 | file_extensions: "php"
16 | ignore-undeclared: true
17 | ratings:
18 | paths:
19 | - "**.php"
20 | exclude_paths:
21 | - tests/
22 | - vendor/
23 |
--------------------------------------------------------------------------------
/.github/phpunit9-integration.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 | ../../../vendor/%COMPOSER_NAME%/tests
13 |
14 |
15 |
16 |
17 |
18 | ../../../lib/internal/*/*/Test
19 | ../../../lib/internal/*/*/*/Test
20 | ../../../setup/src/*/*/Test
21 | ../../../vendor/*/*/Test
22 |
23 |
24 |
25 |
26 | .
27 | testsuite
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
52 |
53 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | # disabled because MAGENTO_MARKETPLACE_USERNAME and MAGENTO_MARKETPLACE_PASSWORD repository secrets do not work:
11 | ###########
12 | # phpstan:
13 | #
14 | # name: PHPStan Static Analysis
15 | # runs-on: ubuntu-latest
16 | #
17 | # steps:
18 | # - uses: actions/checkout@v2
19 | #
20 | # - name: Setup PHP
21 | # uses: shivammathur/setup-php@v2
22 | # with:
23 | # php-version: '8.1'
24 | #
25 | # - name: Validate composer.json and composer.lock
26 | # run: composer validate --strict
27 | #
28 | # - name: Cache Composer packages
29 | # id: composer-cache
30 | # uses: actions/cache@v2
31 | # with:
32 | # path: vendor
33 | # key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
34 | # restore-keys: |
35 | # ${{ runner.os }}-php-
36 | #
37 | # - name: Install dependencies
38 | # run: composer global config http-basic.repo.magento.com ${{ secrets.MAGENTO_MARKETPLACE_USERNAME }} ${{ secrets.MAGENTO_MARKETPLACE_PASSWORD }} && composer install --prefer-dist --no-progress
39 | #
40 | # - name: Run PHPStan
41 | # run: vendor/bin/phpstan --no-progress
42 |
43 | # cannot parameterize php version because of different ext-dn actions per version
44 | integration-tests-74:
45 | name: Magento 2 Integration Tests (php 7.4)
46 | runs-on: ubuntu-latest
47 |
48 | strategy:
49 | matrix:
50 | magento-version: [2.3, 2.4]
51 | include:
52 | - magento-version: 2.3
53 | magento-patch-version: 2.3.7
54 | - magento-version: 2.4
55 | magento-patch-version: 2.4.4
56 |
57 | services:
58 | mysql:
59 | image: mysql:5.7
60 | env:
61 | MYSQL_ROOT_PASSWORD: root
62 | ports:
63 | - 3306:3306
64 | options: --tmpfs /tmp:rw --tmpfs /var/lib/mysql:rw --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
65 | es:
66 | image: docker.io/wardenenv/elasticsearch:7.8
67 | ports:
68 | - 9200:9200
69 | env:
70 | 'discovery.type': single-node
71 | 'xpack.security.enabled': false
72 | ES_JAVA_OPTS: "-Xms64m -Xmx512m"
73 | options: --health-cmd="curl localhost:9200/_cluster/health?wait_for_status=yellow&timeout=60s" --health-interval=10s --health-timeout=5s --health-retries=3
74 | steps:
75 | - uses: actions/checkout@v2
76 | - name: M2 Integration Tests with Magento 2
77 | uses: extdn/github-actions-m2/magento-integration-tests/7.4@master
78 | with:
79 | module_name: TddWizard_Fixtures
80 | composer_name: tddwizard/magento2-fixtures
81 | composer_version: 2
82 | ce_version: ${{ matrix.magento-patch-version }}
83 | phpunit_file: .github/phpunit9-integration.xml
84 | - name: Upload Integration Test Results
85 | if: always()
86 | uses: actions/upload-artifact@v2
87 | with:
88 | name: Integration Test Results
89 | # filename is defined in phpunit9-integration.xml
90 | # since it's the same every time, only one integration test result is published
91 | path: var/test-results/integration.xml
92 | integration-tests-81:
93 | name: Magento 2 Integration Tests (PHP 8.1)
94 | runs-on: ubuntu-latest
95 |
96 | strategy:
97 | matrix:
98 | magento-version: [2.4]
99 | include:
100 | - magento-version: 2.4
101 | magento-patch-version: 2.4.4
102 |
103 | services:
104 | mysql:
105 | image: mysql:5.7
106 | env:
107 | MYSQL_ROOT_PASSWORD: root
108 | ports:
109 | - 3306:3306
110 | options: --tmpfs /tmp:rw --tmpfs /var/lib/mysql:rw --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
111 | es:
112 | image: docker.io/wardenenv/elasticsearch:7.8
113 | ports:
114 | - 9200:9200
115 | env:
116 | 'discovery.type': single-node
117 | 'xpack.security.enabled': false
118 | ES_JAVA_OPTS: "-Xms64m -Xmx512m"
119 | options: --health-cmd="curl localhost:9200/_cluster/health?wait_for_status=yellow&timeout=60s" --health-interval=10s --health-timeout=5s --health-retries=3
120 | steps:
121 | - uses: actions/checkout@v2
122 | - name: M2 Integration Tests with Magento 2
123 | uses: extdn/github-actions-m2/magento-integration-tests/8.1@master
124 | with:
125 | module_name: TddWizard_Fixtures
126 | composer_name: tddwizard/magento2-fixtures
127 | composer_version: 2
128 | ce_version: ${{ matrix.magento-patch-version }}
129 | phpunit_file: .github/phpunit9-integration.xml
130 | - name: Upload Integration Test Results
131 | if: always()
132 | uses: actions/upload-artifact@v2
133 | with:
134 | name: Integration Test Results
135 | path: var/test-results/integration.xml
136 |
137 | publish-test-results:
138 | name: "Publish Tests Results"
139 | needs:
140 | - integration-tests-74
141 | - integration-tests-81
142 | runs-on: ubuntu-latest
143 | if: always()
144 |
145 | steps:
146 | - name: Download Artifacts
147 | uses: actions/download-artifact@v2
148 | with:
149 | path: artifacts
150 |
151 | - name: Publish Test Results
152 | uses: EnricoMi/publish-unit-test-result-action@v1
153 | with:
154 | files: artifacts/**/*.xml
155 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor
3 | /composer.lock
4 | /tests/phpunit.xml
5 |
6 | #docker
7 | .env
8 | docker-compose.yml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 tddwizard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TddWizard Fixture library
2 |
3 | [![Latest Version on Packagist][ico-version]][link-packagist]
4 | [![Software License][ico-license]](LICENSE.md)
5 | [![Build Status][ico-travis]][link-travis]
6 | [![Coverage Status][ico-scrutinizer]][link-scrutinizer]
7 | [![Quality Score][ico-code-quality]][link-code-quality]
8 | [](https://codeclimate.com/github/tddwizard/magento2-fixtures)
9 |
10 | ---
11 |
12 | Magento 2 Fixtures by Fabian Schmengler
13 |
14 | 🧙🏻♂ https://tddwizard.com/
15 |
16 | ## What is it?
17 |
18 | An alternative to the procedural script based fixtures in Magento 2 integration tests.
19 |
20 | It aims to be:
21 |
22 | - extensible
23 | - expressive
24 | - easy to use
25 |
26 | ## Installation
27 |
28 | Install it into your Magento 2 project with composer:
29 |
30 | composer require --dev tddwizard/magento2-fixtures
31 |
32 | ## Requirements
33 |
34 | - Magento 2.3 or Magento 2.4
35 | - PHP 7.3 or 7.4 *(7.1 and 7.2 is allowed via composer for full Magento 2.3 compatibility but not tested anymore)*
36 |
37 | ## Usage examples:
38 |
39 | ### Customer
40 |
41 | If you need a customer without specific data, this is all:
42 |
43 | ```php
44 | protected function setUp(): void
45 | {
46 | $this->customerFixture = new CustomerFixture(
47 | CustomerBuilder::aCustomer()->build()
48 | );
49 | }
50 | protected function tearDown(): void
51 | {
52 | $this->customerFixture->rollback();
53 | }
54 | ```
55 |
56 | It uses default sample data and a random email address. If you need the ID or email address in the tests, the `CustomerFixture` gives you access:
57 |
58 | ```php
59 | $this->customerFixture->getId();
60 | $this->customerFixture->getEmail();
61 | ```
62 |
63 | You can configure the builder with attributes:
64 |
65 | ```php
66 | CustomerBuilder::aCustomer()
67 | ->withEmail('test@example.com')
68 | ->withCustomAttributes(
69 | [
70 | 'my_custom_attribute' => 42
71 | ]
72 | )
73 | ->build()
74 | ```
75 |
76 | You can add addresses to the customer:
77 |
78 | ```php
79 | CustomerBuilder::aCustomer()
80 | ->withAddresses(
81 | AddressBuilder::anAddress()->asDefaultBilling(),
82 | AddressBuilder::anAddress()->asDefaultShipping(),
83 | AddressBuilder::anAddress()
84 | )
85 | ->build()
86 | ```
87 |
88 | Or just one:
89 |
90 | ```php
91 | CustomerBuilder::aCustomer()
92 | ->withAddresses(
93 | AddressBuilder::anAddress()->asDefaultBilling()->asDefaultShipping()
94 | )
95 | ->build()
96 | ```
97 |
98 | The `CustomerFixture` also has a shortcut to create a customer session:
99 |
100 | ```php
101 | $this->customerFixture->login();
102 | ```
103 |
104 |
105 |
106 | ### Addresses
107 |
108 | Similar to the customer builder you can also configure the address builder with custom attributes:
109 |
110 | ```php
111 | AddressBuilder::anAddress()
112 | ->withCountryId('DE')
113 | ->withCity('Aachen')
114 | ->withPostcode('52078')
115 | ->withCustomAttributes(
116 | [
117 | 'my_custom_attribute' => 42
118 | ]
119 | )
120 | ->asDefaultShipping()
121 | ```
122 |
123 | ### Product
124 |
125 | Product fixtures work similar as customer fixtures:
126 |
127 | ```php
128 | protected function setUp(): void
129 | {
130 | $this->productFixture = new ProductFixture(
131 | ProductBuilder::aSimpleProduct()
132 | ->withPrice(10)
133 | ->withCustomAttributes(
134 | [
135 | 'my_custom_attribute' => 42
136 | ]
137 | )
138 | ->build()
139 | );
140 | }
141 | protected function tearDown(): void
142 | {
143 | $this->productFixture->rollback();
144 | }
145 | ```
146 |
147 | The SKU is randomly generated and can be accessed through `ProductFixture`, just as the ID:
148 |
149 | ```php
150 | $this->productFixture->getSku();
151 | $this->productFixture->getId();
152 | ```
153 |
154 | ### Cart/Checkout
155 |
156 | To create a quote, use the `CartBuilder` together with product fixtures:
157 |
158 | ```php
159 | $cart = CartBuilder::forCurrentSession()
160 | ->withSimpleProduct(
161 | $productFixture1->getSku()
162 | )
163 | ->withSimpleProduct(
164 | $productFixture2->getSku(), 10 // optional qty parameter
165 | )
166 | ->build()
167 | $quote = $cart->getQuote();
168 | ```
169 |
170 | Checkout is supported for logged in customers. To create an order, you can simulate the checkout as follows, given a customer fixture with default shipping and billing addresses and a product fixture:
171 |
172 | ```php
173 | $customerFixture = new CustomerFixture(CustomerBuilder::aCustomer()->withAddresses(
174 | AddressBuilder::anAddress()->asDefaultBilling(),
175 | AddressBuilder::anAddress()->asDefaultShipping()
176 | )->build());
177 | $customerFixture->login();
178 |
179 | $checkout = CustomerCheckout::fromCart(
180 | CartBuilder::forCurrentSession()
181 | ->withProductRequest(ProductBuilder::aVirtualProduct()->build()->getSku())
182 | ->build()
183 | );
184 |
185 | $order = $checkout->placeOrder();
186 | ```
187 |
188 | It will try to select the default addresses and the first available shipping and payment methods.
189 |
190 | You can also select them explicitly:
191 |
192 | ```php
193 | $order = $checkout
194 | ->withShippingMethodCode('freeshipping_freeshipping')
195 | ->withPaymentMethodCode('checkmo')
196 | ->withCustomerBillingAddressId($this->customerFixture->getOtherAddressId())
197 | ->withCustomerShippingAddressId($this->customerFixture->getOtherAddressId())
198 | ->placeOrder();
199 | ```
200 |
201 | ### Order
202 |
203 | The `OrderBuilder` is a shortcut for checkout simulation.
204 |
205 | ```php
206 | $order = OrderBuilder::anOrder()->build();
207 | ```
208 |
209 | Logged-in customer, products, and cart item quantities will be
210 | generated internally unless more control is desired:
211 |
212 | ```php
213 | $order = OrderBuilder::anOrder()
214 | ->withProducts(
215 | // prepare catalog product fixtures
216 | ProductBuilder::aSimpleProduct()->withSku('foo'),
217 | ProductBuilder::aSimpleProduct()->withSku('bar')
218 | )->withCart(
219 | // define cart item quantities
220 | CartBuilder::forCurrentSession()->withSimpleProduct('foo', 2)->withSimpleProduct('bar', 3)
221 | )->build();
222 | ```
223 |
224 | ### Shipment
225 |
226 | Orders can be fully or partially shipped, optionally with tracks.
227 |
228 | ```php
229 | $order = OrderBuilder::anOrder()->build();
230 |
231 | // ship everything
232 | $shipment = ShipmentBuilder::forOrder($order)->build();
233 | // ship only given order items, add tracks
234 | $shipment = ShipmentBuilder::forOrder($order)
235 | ->withItem($fooItemId, $fooQtyToShip)
236 | ->withItem($barItemId, $barQtyToShip)
237 | ->withTrackingNumbers('123-FOO', '456-BAR')
238 | ->build();
239 | ```
240 |
241 | ### Invoice
242 |
243 | Orders can be fully or partially invoiced.
244 |
245 | ```php
246 | $order = OrderBuilder::anOrder()->build();
247 |
248 | // invoice everything
249 | $invoice = InvoiceBuilder::forOrder($order)->build();
250 | // invoice only given order items
251 | $invoice = InvoiceBuilder::forOrder($order)
252 | ->withItem($fooItemId, $fooQtyToInvoice)
253 | ->withItem($barItemId, $barQtyToInvoice)
254 | ->build();
255 | ```
256 |
257 | ### Credit Memo
258 |
259 | Credit memos can be created for either all or some of the items ordered.
260 | An invoice to refund will be created internally.
261 |
262 | ```php
263 | $order = OrderBuilder::anOrder()->build();
264 |
265 | // refund everything
266 | $creditmemo = CreditmemoBuilder::forOrder($order)->build();
267 | // refund only given order items
268 | $creditmemo = CreditmemoBuilder::forOrder($order)
269 | ->withItem($fooItemId, $fooQtyToRefund)
270 | ->withItem($barItemId, $barQtyToRefund)
271 | ->build();
272 | ```
273 |
274 | ### Fixture pools
275 |
276 | To manage multiple fixtures, **fixture pools** have been introduced for all entities:
277 |
278 | Usage demonstrated with the `ProductFixturePool`:
279 | ```
280 | protected function setUp()
281 | {
282 | $this->productFixtures = new ProductFixturePool;
283 | }
284 |
285 | protected function tearDown()
286 | {
287 | $this->productFixtures->rollback();
288 | }
289 |
290 | public function testSomethingWithMultipleProducts()
291 | {
292 | $this->productFixtures->add(ProductBuilder::aSimpleProduct()->build());
293 | $this->productFixtures->add(ProductBuilder::aSimpleProduct()->build(), 'foo');
294 | $this->productFixtures->add(ProductBuilder::aSimpleProduct()->build());
295 |
296 | $this->productFixtures->get(); // returns ProductFixture object for last added product
297 | $this->productFixtures->get('foo'); // returns ProductFixture object for product added with specific key 'foo'
298 | $this->productFixtures->get(0); // returns ProductFixture object for first product added without specific key (numeric array index)
299 | }
300 |
301 | ```
302 |
303 | ### Config Fixtures
304 |
305 | With the config fixture you can set a configuration value globally, i.e. it will ensure that it is not only set in the default scope but also in all store scopes:
306 | ```
307 | ConfigFixture::setGlobal('general/store_information/name', 'Ye Olde Wizard Shop');
308 | ```
309 |
310 | It uses `MutableScopeConfigInterface`, so the configuration is not persisted in the database. Use `@magentoAppIsolation enabled` in your test to make sure that changes are reverted in subsequent tests.
311 |
312 | You can also set configuration values explicitly for stores with `ConfigFixture::setForStore()`
313 |
314 | ## Credits
315 |
316 | - [Fabian Schmengler][link-author]
317 | - [All Contributors][link-contributors]
318 |
319 | ## License
320 |
321 | The MIT License (MIT). Please see [License File](LICENSE.txt) for more information.
322 |
323 | [ico-version]: https://img.shields.io/packagist/v/tddwizard/magento2-fixtures.svg?style=flat-square
324 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
325 | [ico-travis]: https://img.shields.io/travis/tddwizard/magento2-fixtures/master.svg?style=flat-square
326 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/tddwizard/magento2-fixtures?style=flat-square
327 | [ico-code-quality]: https://img.shields.io/scrutinizer/g/tddwizard/magento2-fixtures.svg?style=flat-square
328 |
329 | [link-packagist]: https://packagist.org/packages/tddwizard/magento2-fixtures
330 | [link-travis]: https://travis-ci.org/tddwizard/magento2-fixtures
331 | [link-scrutinizer]: https://scrutinizer-ci.com/g/tddwizard/magento2-fixtures/code-structure
332 | [link-code-quality]: https://scrutinizer-ci.com/g/tddwizard/magento2-fixtures
333 | [link-author]: https://github.com/schmengler
334 | [link-contributors]: ../../contributors
335 |
336 |
--------------------------------------------------------------------------------
/auth.json:
--------------------------------------------------------------------------------
1 | {
2 | "http-basic": {
3 | "repo.magento.com": {
4 | "username": "438956769d577f67f247510ffa624225",
5 | "password": "b5d1664bb6ee2d27ec75dd36d6a2ac4e"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tddwizard/magento2-fixtures",
3 | "description": "Fixture library for Magento 2 integration tests",
4 | "type": "magento2-module",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Fabian Schmengler",
9 | "email": "fs@integer-net.de"
10 | }
11 | ],
12 | "minimum-stability": "stable",
13 | "autoload": {
14 | "files": [
15 | "registration.php"
16 | ],
17 | "psr-4": {
18 | "TddWizard\\Fixtures\\": "src"
19 | }
20 | },
21 | "autoload-dev": {
22 | "psr-4": {
23 | "TddWizard\\Fixtures\\": "tests"
24 | }
25 | },
26 | "repositories": [
27 | {
28 | "type": "composer",
29 | "url": "https://repo.magento.com/"
30 | }
31 | ],
32 | "require": {
33 | "php": "^7.1|^8.0",
34 | "magento/framework": "^102.0|^103.0",
35 | "magento/zendframework1": "^1.14",
36 | "magento/module-catalog": "^103.0|^104.0",
37 | "magento/module-catalog-inventory": "^100.3",
38 | "magento/module-checkout": "^100.3",
39 | "magento/module-customer": "^102.0|^103.0",
40 | "magento/module-directory": "^100.3",
41 | "magento/module-indexer": "^100.3",
42 | "magento/module-payment": "^100.3",
43 | "magento/module-quote": "^101.1",
44 | "magento/module-sales": "^102.0|^103.0",
45 | "fakerphp/faker": "^1.9.1"
46 | },
47 | "require-dev": {
48 | "ext-json": "*",
49 | "magento/module-store": "^101.0",
50 | "phpunit/phpunit": "^6.0|^9.0",
51 | "pds/skeleton": "^1.0",
52 | "phpro/grumphp": "^0.19.0",
53 | "phpstan/phpstan": "^0.12.0",
54 | "squizlabs/php_codesniffer": "^3.3.1"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/grumphp.yml:
--------------------------------------------------------------------------------
1 | grumphp:
2 | tasks:
3 | composer: []
4 | phpcs:
5 | standard: 'PSR2'
6 | encoding: utf-8
7 | phpstan:
8 | configuration: phpstan.neon
9 | phpparser:
10 | visitors:
11 | no_exit_statements: ~
12 | forbidden_function_calls:
13 | blacklist:
14 | - 'var_dump'
15 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 6
3 | paths:
4 | - src
5 | - tests
6 | ignoreErrors:
7 | - '#(class|type) Magento\\TestFramework#i'
8 | - '#(class|type) Magento\\\S*Factory#i'
9 | - '#(method) Magento\\Framework\\Api\\ExtensionAttributesInterface#i'
10 |
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 | categoryRepository = $categoryRepository;
63 | $this->categoryResource = $categoryResource;
64 | $this->categoryLinkRepository = $categoryLinkRepository;
65 | $this->productLinkFactory = $productLinkFactory;
66 | $this->category = $category;
67 | $this->skus = $skus;
68 | }
69 |
70 | public static function topLevelCategory(): CategoryBuilder
71 | {
72 | $objectManager = Bootstrap::getObjectManager();
73 |
74 | // use interface to reflect DI configuration but assume instance of the real model because we need its methods
75 | /** @var Category $category */
76 | $category = $objectManager->create(CategoryInterface::class);
77 |
78 | $category->setName('Top Level Category');
79 | $category->setIsActive(true);
80 | $category->setPath('1/2');
81 |
82 | return new self(
83 | $objectManager->create(CategoryRepositoryInterface::class),
84 | $objectManager->create(CategoryResource::class),
85 | $objectManager->create(CategoryLinkRepositoryInterface::class),
86 | $objectManager->create(CategoryProductLinkInterfaceFactory::class),
87 | $category,
88 | []
89 | );
90 | }
91 |
92 | public static function childCategoryOf(
93 | CategoryFixture $parent
94 | ): CategoryBuilder {
95 | $objectManager = Bootstrap::getObjectManager();
96 | // use interface to reflect DI configuration but assume instance of the real model because we need its methods
97 | /** @var Category $category */
98 | $category = $objectManager->create(CategoryInterface::class);
99 |
100 | $category->setName('Child Category');
101 | $category->setIsActive(true);
102 | $category->setPath((string)$parent->getCategory()->getPath());
103 |
104 | return new self(
105 | $objectManager->create(CategoryRepositoryInterface::class),
106 | $objectManager->create(CategoryResource::class),
107 | $objectManager->create(CategoryLinkRepositoryInterface::class),
108 | $objectManager->create(CategoryProductLinkInterfaceFactory::class),
109 | $category,
110 | []
111 | );
112 | }
113 |
114 | /**
115 | * Assigns products by sku. The keys of the array will be used for the sort position
116 | *
117 | * @param string[] $skus
118 | * @return CategoryBuilder
119 | */
120 | public function withProducts(array $skus): CategoryBuilder
121 | {
122 | $builder = clone $this;
123 | $builder->skus = $skus;
124 | return $builder;
125 | }
126 |
127 | public function withDescription(string $description): CategoryBuilder
128 | {
129 | $builder = clone $this;
130 | $builder->category->setCustomAttribute('description', $description);
131 | return $builder;
132 | }
133 |
134 | public function withName(string $name): CategoryBuilder
135 | {
136 | $builder = clone $this;
137 | $builder->category->setName($name);
138 | return $builder;
139 | }
140 |
141 | public function withUrlKey(string $urlKey): CategoryBuilder
142 | {
143 | $builder = clone $this;
144 | $builder->category->setData('url_key', $urlKey);
145 | return $builder;
146 | }
147 |
148 | public function withIsActive(bool $isActive): CategoryBuilder
149 | {
150 | $builder = clone $this;
151 | $builder->category->setIsActive($isActive);
152 | return $builder;
153 | }
154 |
155 | public function __clone()
156 | {
157 | $this->category = clone $this->category;
158 | }
159 |
160 | /**
161 | * @return Category
162 | * @throws \Exception
163 | */
164 | public function build(): Category
165 | {
166 | $builder = clone $this;
167 |
168 | if (!$builder->category->getData('url_key')) {
169 | $builder->category->setData('url_key', sha1(uniqid('', true)));
170 | }
171 |
172 | // Save with global scope if not specified otherwise
173 | if (!$builder->category->hasData('store_id')) {
174 | $builder->category->setStoreId(0);
175 | }
176 | $builder->categoryResource->save($builder->category);
177 |
178 | foreach ($builder->skus as $position => $sku) {
179 | $productLink = $builder->productLinkFactory->create();
180 | $productLink->setSku($sku);
181 | $productLink->setPosition($position);
182 | $productLink->setCategoryId($builder->category->getId());
183 | $builder->categoryLinkRepository->save($productLink);
184 | }
185 | return $builder->category;
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/src/Catalog/CategoryFixture.php:
--------------------------------------------------------------------------------
1 | category;
22 | }
23 |
24 | public function __construct(CategoryInterface $category)
25 | {
26 | $this->category = $category;
27 | }
28 |
29 | public function getId(): int
30 | {
31 | return (int) $this->category->getId();
32 | }
33 |
34 | public function getUrlKey(): string
35 | {
36 | /** @var Category $category */
37 | $category = $this->category;
38 | return (string)$category->getUrlKey();
39 | }
40 |
41 | public function rollback(): void
42 | {
43 | CategoryFixtureRollback::create()->execute($this);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Catalog/CategoryFixturePool.php:
--------------------------------------------------------------------------------
1 | categoryFixtures[] = new CategoryFixture($category);
21 | } else {
22 | $this->categoryFixtures[$key] = new CategoryFixture($category);
23 | }
24 | }
25 |
26 | /**
27 | * Returns category fixture by key, or last added if key not specified
28 | *
29 | * @param int|string|null $key
30 | * @return CategoryFixture
31 | */
32 | public function get($key = null): CategoryFixture
33 | {
34 | if ($key === null) {
35 | $key = \array_key_last($this->categoryFixtures);
36 | }
37 | if ($key === null || !array_key_exists($key, $this->categoryFixtures)) {
38 | throw new \OutOfBoundsException('No matching category found in fixture pool');
39 | }
40 | return $this->categoryFixtures[$key];
41 | }
42 |
43 | public function rollback(): void
44 | {
45 | CategoryFixtureRollback::create()->execute(...values($this->categoryFixtures));
46 | $this->categoryFixtures = [];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Catalog/CategoryFixtureRollback.php:
--------------------------------------------------------------------------------
1 | registry = $registry;
29 | $this->categoryRepository = $categoryRepository;
30 | }
31 |
32 | public static function create(): CategoryFixtureRollback
33 | {
34 | $objectManager = Bootstrap::getObjectManager();
35 | return new self(
36 | $objectManager->get(Registry::class),
37 | $objectManager->get(CategoryRepositoryInterface::class)
38 | );
39 | }
40 |
41 | /**
42 | * @param CategoryFixture ...$categoryFixtures
43 | * @throws LocalizedException
44 | */
45 | public function execute(CategoryFixture ...$categoryFixtures): void
46 | {
47 | $this->registry->unregister('isSecureArea');
48 | $this->registry->register('isSecureArea', true);
49 |
50 | foreach ($categoryFixtures as $categoryFixture) {
51 | $this->categoryRepository->deleteByIdentifier($categoryFixture->getId());
52 | }
53 |
54 | $this->registry->unregister('isSecureArea');
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Catalog/FulltextIndex.php:
--------------------------------------------------------------------------------
1 | indexerFactory = $indexerFactory;
27 | }
28 |
29 | /**
30 | * @throws \Throwable
31 | */
32 | public static function ensureTablesAreCreated(): void
33 | {
34 | if (!self::$created) {
35 | (new self(Bootstrap::getObjectManager()->create(IndexerFactory::class)))->reindex();
36 | }
37 | }
38 |
39 | /**
40 | * @throws \Throwable
41 | */
42 | public function reindex(): void
43 | {
44 | $indexer = $this->indexerFactory->create();
45 | $indexer->load('catalogsearch_fulltext');
46 | $indexer->reindexAll();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Catalog/IndexFailed.php:
--------------------------------------------------------------------------------
1 | optionManagement = $optionManagement;
56 | $this->option = $option;
57 | $this->optionLabel = $optionLabel;
58 | $this->attributeCode = $attributeCode;
59 | }
60 |
61 | /**
62 | * Clone the builder.
63 | */
64 | public function __clone()
65 | {
66 | $this->option = clone $this->option;
67 | }
68 |
69 | /**
70 | * Create an option.
71 | *
72 | * @param string $attributeCode
73 | * @return OptionBuilder
74 | * @throws \Magento\Framework\Exception\InputException
75 | * @throws \Magento\Framework\Exception\StateException
76 | */
77 | public static function anOptionFor(string $attributeCode): OptionBuilder
78 | {
79 | $objectManager = Bootstrap::getObjectManager();
80 | /** @var AttributeOptionManagementInterface $optionManagement */
81 | $optionManagement = $objectManager->create(AttributeOptionManagementInterface::class);
82 | $items = $optionManagement->getItems(Product::ENTITY, $attributeCode);
83 |
84 | /** @var AttributeOptionLabelInterface $optionLabel */
85 | $optionLabel = $objectManager->create(AttributeOptionLabelInterface::class);
86 | $label = uniqid('Name ', true);
87 | $optionLabel->setStoreId(0);
88 | $optionLabel->setLabel($label);
89 |
90 | /** @var AttributeOption $option */
91 | $option = $objectManager->create(AttributeOption::class);
92 | $option->setLabel($label);
93 | $option->setStoreLabels([$optionLabel]);
94 | $option->setSortOrder(count($items) + 1);
95 | $option->setIsDefault(false);
96 |
97 | return new static(
98 | $optionManagement,
99 | $option,
100 | $optionLabel,
101 | $attributeCode
102 | );
103 | }
104 |
105 | /**
106 | * Set label.
107 | *
108 | * @param string $label
109 | * @return OptionBuilder
110 | */
111 | public function withLabel(string $label): OptionBuilder
112 | {
113 | $builder = clone $this;
114 | $builder->optionLabel->setLabel($label);
115 | $builder->option->setStoreLabels([$builder->optionLabel]);
116 | $builder->option->setLabel($label);
117 |
118 | return $builder;
119 | }
120 |
121 | /**
122 | * Set sort order.
123 | *
124 | * @param int $sortOrder
125 | * @return OptionBuilder
126 | */
127 | public function withSortOrder(int $sortOrder): OptionBuilder
128 | {
129 | $builder = clone $this;
130 | $builder->option->setSortOrder($sortOrder);
131 |
132 | return $builder;
133 | }
134 |
135 | /**
136 | * Set default.
137 | *
138 | * @param bool $isDefault
139 | * @return OptionBuilder
140 | */
141 | public function withIsDefault(bool $isDefault): OptionBuilder
142 | {
143 | $builder = clone $this;
144 | $builder->option->setIsDefault($isDefault);
145 |
146 | return $builder;
147 | }
148 |
149 | /**
150 | * Set store ID.
151 | *
152 | * @param int $storeId
153 | * @return OptionBuilder
154 | */
155 | public function withStoreId(int $storeId): OptionBuilder
156 | {
157 | $builder = clone $this;
158 | $builder->optionLabel->setStoreId($storeId);
159 |
160 | return $builder;
161 | }
162 |
163 | /**
164 | * Build the option and apply it to the attribute.
165 | *
166 | * @return AttributeOption
167 | * @throws \Magento\Framework\Exception\InputException
168 | * @throws \Magento\Framework\Exception\StateException
169 | */
170 | public function build(): AttributeOption
171 | {
172 | $builder = clone $this;
173 |
174 | // add the option
175 | $this->optionManagement->add(
176 | \Magento\Catalog\Model\Product::ENTITY,
177 | $builder->attributeCode,
178 | $builder->option
179 | );
180 |
181 | $optionId = $this->getOptionId();
182 | $builder->option->setId($optionId);
183 |
184 | return $builder->option;
185 | }
186 |
187 | /**
188 | * Get the option ID.
189 | *
190 | * @return int
191 | */
192 | private function getOptionId(): int
193 | {
194 | $objectManager = Bootstrap::getObjectManager();
195 | // the add option above does not return the option, so we need to retrieve it
196 | $attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class);
197 | $attribute = $attributeRepository->get($this->attributeCode);
198 | $attributeValues[$attribute->getAttributeId()] = [];
199 |
200 | // We have to generate a new sourceModel instance each time through to prevent it from
201 | // referencing its _options cache. No other way to get it to pick up newly-added values.
202 | $tableFactory = $objectManager->get(\Magento\Eav\Model\Entity\Attribute\Source\TableFactory::class);
203 | $sourceModel = $tableFactory->create();
204 | $sourceModel->setAttribute($attribute);
205 | foreach ($sourceModel->getAllOptions() as $option) {
206 | $attributeValues[$attribute->getAttributeId()][$option['label']] = $option['value'];
207 | }
208 | if (isset($attributeValues[$attribute->getAttributeId()][$this->optionLabel->getLabel()])) {
209 | return (int)$attributeValues[$attribute->getAttributeId()][$this->optionLabel->getLabel()];
210 | }
211 |
212 | throw new \RuntimeException('Error building option');
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/src/Catalog/OptionFixture.php:
--------------------------------------------------------------------------------
1 | attributeCode = $attributeCode;
34 | $this->option = $option;
35 | }
36 |
37 | /**
38 | * Get the attribute code.
39 | *
40 | * @return string
41 | */
42 | public function getAttributeCode(): string
43 | {
44 | return $this->attributeCode;
45 | }
46 |
47 | /**
48 | * Get the option.
49 | *
50 | * @return AttributeOption
51 | */
52 | public function getOption(): AttributeOption
53 | {
54 | return $this->option;
55 | }
56 |
57 | /**
58 | * Rollback the option(s).
59 | *
60 | * @throws LocalizedException
61 | */
62 | public function rollback(): void
63 | {
64 | OptionFixtureRollback::create()->execute($this);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Catalog/OptionFixturePool.php:
--------------------------------------------------------------------------------
1 | optionFixtures[] = new OptionFixture($option, $attributecode);
24 | } else {
25 | $this->optionFixtures[$key] = new OptionFixture($option, $attributecode);
26 | }
27 | }
28 |
29 | /**
30 | * Returns option fixture by key, or last added if key not specified
31 | *
32 | * @param string|null $key
33 | * @return OptionFixture
34 | */
35 | public function get(string $key = null): OptionFixture
36 | {
37 | if ($key === null) {
38 | $key = \array_key_last($this->optionFixtures);
39 | }
40 | if ($key === null || !\array_key_exists($key, $this->optionFixtures)) {
41 | throw new \OutOfBoundsException('No matching option found in fixture pool');
42 | }
43 | return $this->optionFixtures[$key];
44 | }
45 |
46 | public function rollback(): void
47 | {
48 | OptionFixtureRollback::create()->execute(...values($this->optionFixtures));
49 | $this->optionFixtures = [];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Catalog/OptionFixtureRollback.php:
--------------------------------------------------------------------------------
1 | registry = $registry;
41 | $this->optionManagement = $optionManagement;
42 | }
43 |
44 | /**
45 | * Create the object.
46 | *
47 | * @return OptionFixtureRollback
48 | */
49 | public static function create(): OptionFixtureRollback
50 | {
51 | $objectManager = Bootstrap::getObjectManager();
52 | return new self(
53 | $objectManager->get(Registry::class),
54 | $objectManager->get(AttributeOptionManagementInterface::class)
55 | );
56 | }
57 |
58 | /**
59 | * Remove the given option(s).
60 | *
61 | * @param OptionFixture ...$optionFixtures
62 | * @throws InputException
63 | * @throws NoSuchEntityException
64 | * @throws StateException
65 | */
66 | public function execute(OptionFixture ...$optionFixtures): void
67 | {
68 | $this->registry->unregister('isSecureArea');
69 | $this->registry->register('isSecureArea', true);
70 |
71 | foreach ($optionFixtures as $optionFixture) {
72 | $this->optionManagement->delete(
73 | Product::ENTITY,
74 | $optionFixture->getAttributeCode(),
75 | $optionFixture->getOption()->getId()
76 | );
77 | }
78 |
79 | $this->registry->unregister('isSecureArea');
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Catalog/ProductFixture.php:
--------------------------------------------------------------------------------
1 | product = $product;
18 | }
19 |
20 | public function getProduct(): ProductInterface
21 | {
22 | return $this->product;
23 | }
24 |
25 | public function getId(): int
26 | {
27 | return (int) $this->product->getId();
28 | }
29 |
30 | public function getSku(): string
31 | {
32 | return $this->product->getSku();
33 | }
34 |
35 | public function rollback(): void
36 | {
37 | ProductFixtureRollback::create()->execute($this);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Catalog/ProductFixturePool.php:
--------------------------------------------------------------------------------
1 | productFixtures[] = new ProductFixture($product);
21 | } else {
22 | $this->productFixtures[$key] = new ProductFixture($product);
23 | }
24 | }
25 |
26 | /**
27 | * Returns product fixture by key, or last added if key not specified
28 | *
29 | * @param int|string|null $key
30 | * @return ProductFixture
31 | */
32 | public function get($key = null): ProductFixture
33 | {
34 | if ($key === null) {
35 | $key = \array_key_last($this->productFixtures);
36 | }
37 | if ($key === null || !array_key_exists($key, $this->productFixtures)) {
38 | throw new \OutOfBoundsException('No matching product found in fixture pool');
39 | }
40 | return $this->productFixtures[$key];
41 | }
42 |
43 | public function rollback(): void
44 | {
45 | ProductFixtureRollback::create()->execute(...values($this->productFixtures));
46 | $this->productFixtures = [];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Catalog/ProductFixtureRollback.php:
--------------------------------------------------------------------------------
1 | registry = $registry;
29 | $this->productRepository = $productRepository;
30 | }
31 |
32 | public static function create(): ProductFixtureRollback
33 | {
34 | $objectManager = Bootstrap::getObjectManager();
35 | return new self(
36 | $objectManager->get(Registry::class),
37 | $objectManager->get(ProductRepositoryInterface::class)
38 | );
39 | }
40 |
41 | /**
42 | * @param ProductFixture ...$productFixtures
43 | * @throws LocalizedException
44 | */
45 | public function execute(ProductFixture ...$productFixtures): void
46 | {
47 | $this->registry->unregister('isSecureArea');
48 | $this->registry->register('isSecureArea', true);
49 |
50 | foreach ($productFixtures as $productFixture) {
51 | $this->productRepository->deleteById($productFixture->getSku());
52 | }
53 |
54 | $this->registry->unregister('isSecureArea');
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Checkout/CartBuilder.php:
--------------------------------------------------------------------------------
1 | [buyRequest]] (multiple requests per sku are possible)
27 | */
28 | private $addToCartRequests;
29 |
30 | final public function __construct(ProductRepositoryInterface $productRepository, Cart $cart)
31 | {
32 | $this->productRepository = $productRepository;
33 | $this->cart = $cart;
34 | $this->addToCartRequests = [];
35 | }
36 |
37 | public static function forCurrentSession(): CartBuilder
38 | {
39 | $objectManager = Bootstrap::getObjectManager();
40 | return new static(
41 | $objectManager->create(ProductRepositoryInterface::class),
42 | $objectManager->create(Cart::class)
43 | );
44 | }
45 |
46 | public function withSimpleProduct(string $sku, float $qty = 1): CartBuilder
47 | {
48 | $result = clone $this;
49 | $result->addToCartRequests[$sku][] = new DataObject(['qty' => $qty]);
50 | return $result;
51 | }
52 |
53 | public function withReservedOrderId(string $orderId): CartBuilder
54 | {
55 | $result = clone $this;
56 | $result->cart->getQuote()->setReservedOrderId($orderId);
57 | return $result;
58 | }
59 |
60 | /**
61 | * Lower-level API to support arbitrary products
62 | *
63 | * @param string $sku
64 | * @param int $qty
65 | * @param mixed[] $request
66 | * @return CartBuilder
67 | */
68 | public function withProductRequest($sku, $qty = 1, $request = []): CartBuilder
69 | {
70 | $result = clone $this;
71 | $requestInfo = array_merge(['qty' => $qty], $request);
72 | $result->addToCartRequests[$sku][] = new DataObject($requestInfo);
73 | return $result;
74 | }
75 |
76 | /**
77 | * @return Cart
78 | * @throws LocalizedException
79 | */
80 | public function build(): Cart
81 | {
82 | foreach ($this->addToCartRequests as $sku => $requests) {
83 | /** @var Product $product */
84 | $product = $this->productRepository->get($sku);
85 | foreach ($requests as $requestInfo) {
86 | $this->cart->addProduct($product, $requestInfo);
87 | }
88 | }
89 | $this->cart->save();
90 | return $this->cart;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Checkout/CustomerCheckout.php:
--------------------------------------------------------------------------------
1 | addressRepository = $addressRepository;
75 | $this->quoteRepository = $quoteRepository;
76 | $this->quoteManagement = $quoteManagement;
77 | $this->paymentConfig = $paymentConfig;
78 | $this->cart = $cart;
79 | $this->shippingAddressId = $shippingAddressId;
80 | $this->billingAddressId = $billingAddressId;
81 | $this->shippingMethodCode = $shippingMethodCode;
82 | $this->paymentMethodCode = $paymentMethodCode;
83 | }
84 |
85 | public static function fromCart(Cart $cart): CustomerCheckout
86 | {
87 | $objectManager = Bootstrap::getObjectManager();
88 | return new static(
89 | $objectManager->create(AddressRepositoryInterface::class),
90 | $objectManager->create(CartRepositoryInterface::class),
91 | $objectManager->create(QuoteManagement::class),
92 | $objectManager->create(PaymentConfig::class),
93 | $cart
94 | );
95 | }
96 |
97 | public function withCustomerBillingAddressId(int $addressId): CustomerCheckout
98 | {
99 | $checkout = clone $this;
100 | $checkout->billingAddressId = $addressId;
101 | return $checkout;
102 | }
103 |
104 | public function withCustomerShippingAddressId(int $addressId): CustomerCheckout
105 | {
106 | $checkout = clone $this;
107 | $checkout->shippingAddressId = $addressId;
108 | return $checkout;
109 | }
110 |
111 | public function withShippingMethodCode(string $code): CustomerCheckout
112 | {
113 | $checkout = clone $this;
114 | $checkout->shippingMethodCode = $code;
115 | return $checkout;
116 | }
117 |
118 | public function withPaymentMethodCode(string $code): CustomerCheckout
119 | {
120 | $checkout = clone $this;
121 | $checkout->paymentMethodCode = $code;
122 | return $checkout;
123 | }
124 |
125 | /**
126 | * @return int Customer shipping address as configured or try default shipping address
127 | */
128 | private function getCustomerShippingAddressId(): int
129 | {
130 | return $this->shippingAddressId
131 | ?? (int) $this->cart->getCustomerSession()->getCustomer()->getDefaultShippingAddress()->getId();
132 | }
133 |
134 | /**
135 | * @return int Customer billing address as configured or try default billing address
136 | */
137 | private function getCustomerBillingAddressId(): int
138 | {
139 | return $this->billingAddressId
140 | ?? (int) $this->cart->getCustomerSession()->getCustomer()->getDefaultBillingAddress()->getId();
141 | }
142 |
143 | /**
144 | * @return string Shipping method code as configured, or try first available rate
145 | */
146 | private function getShippingMethodCode(): string
147 | {
148 | return $this->shippingMethodCode
149 | ?? $this->cart->getQuote()->getShippingAddress()->getAllShippingRates()[0]->getCode();
150 | }
151 |
152 | /**
153 | * @return string Payment method code as configured, or try first available method
154 | */
155 | private function getPaymentMethodCode(): string
156 | {
157 | return $this->paymentMethodCode ?? array_values($this->paymentConfig->getActiveMethods())[0]->getCode();
158 | }
159 |
160 | /**
161 | * @return Order
162 | * @throws \Exception
163 | */
164 | public function placeOrder(): Order
165 | {
166 | $this->saveBilling();
167 | $this->saveShipping();
168 | $this->savePayment();
169 | /** @var Quote $reloadedQuote */
170 | $reloadedQuote = $this->quoteRepository->get($this->cart->getQuote()->getId());
171 | // Collect missing totals, like shipping
172 | $reloadedQuote->collectTotals();
173 | $order = $this->quoteManagement->submit($reloadedQuote);
174 | if (! $order instanceof Order) {
175 | $returnType = is_object($order) ? get_class($order) : gettype($order);
176 | throw new \RuntimeException('QuoteManagement::submit() returned ' . $returnType . ' instead of Order');
177 | }
178 | $this->cart->getCheckoutSession()->clearQuote();
179 | return $order;
180 | }
181 |
182 | /**
183 | * @throws \Exception
184 | */
185 | private function saveBilling(): void
186 | {
187 | $billingAddress = $this->cart->getQuote()->getBillingAddress();
188 | $billingAddress->importCustomerAddressData(
189 | $this->addressRepository->getById($this->getCustomerBillingAddressId())
190 | );
191 | $billingAddress->save();
192 | }
193 |
194 | /**
195 | * @throws \Exception
196 | */
197 | private function saveShipping(): void
198 | {
199 | $shippingAddress = $this->cart->getQuote()->getShippingAddress();
200 | $shippingAddress->importCustomerAddressData(
201 | $this->addressRepository->getById($this->getCustomerShippingAddressId())
202 | );
203 | $shippingAddress->setCollectShippingRates(true);
204 | $shippingAddress->collectShippingRates();
205 | $shippingAddress->setShippingMethod($this->getShippingMethodCode());
206 | $shippingAddress->save();
207 | }
208 |
209 | /**
210 | * @throws \Exception
211 | */
212 | private function savePayment(): void
213 | {
214 | $payment = $this->cart->getQuote()->getPayment();
215 | $payment->setMethod($this->getPaymentMethodCode());
216 | $payment->save();
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/Core/ConfigFixture.php:
--------------------------------------------------------------------------------
1 | setValue($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
24 | foreach (self::storeRepository()->getList() as $store) {
25 | self::scopeConfig()->setValue($path, $value, ScopeInterface::SCOPE_STORE, $store->getCode());
26 | }
27 | }
28 |
29 | /**
30 | * Sets configuration in store scope
31 | *
32 | * @param string $path
33 | * @param mixed $value
34 | * @param null $storeCode store code or NULL for current store
35 | */
36 | public static function setForStore(string $path, $value, $storeCode = null) : void
37 | {
38 | self::scopeConfig()->setValue($path, $value, ScopeInterface::SCOPE_STORE, $storeCode);
39 | }
40 |
41 | private static function scopeConfig(): MutableScopeConfigInterface
42 | {
43 | return Bootstrap::getObjectManager()->get(MutableScopeConfigInterface::class);
44 | }
45 |
46 | private static function storeRepository(): StoreRepositoryInterface
47 | {
48 | return Bootstrap::getObjectManager()->get(StoreRepositoryInterface::class);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Customer/AddressBuilder.php:
--------------------------------------------------------------------------------
1 | address = $address;
33 | $this->addressRepository = $addressRepository;
34 | }
35 |
36 | public function __clone()
37 | {
38 | $this->address = clone $this->address;
39 | }
40 |
41 | public static function anAddress(
42 | string $locale = 'de_DE'
43 | ): AddressBuilder {
44 | $objectManager = Bootstrap::getObjectManager();
45 |
46 | $address = self::prepareFakeAddress($objectManager, $locale);
47 | return new self($objectManager->create(AddressRepositoryInterface::class), $address);
48 | }
49 |
50 | public static function aCompanyAddress(
51 | string $locale = 'de_DE',
52 | string $vatId = '1234567890'
53 | ): AddressBuilder {
54 | $objectManager = Bootstrap::getObjectManager();
55 |
56 | $address = self::prepareFakeAddress($objectManager, $locale);
57 | $address->setVatId($vatId);
58 | return new self($objectManager->create(AddressRepositoryInterface::class), $address);
59 | }
60 |
61 | public function asDefaultShipping(): AddressBuilder
62 | {
63 | $builder = clone $this;
64 | $builder->address->setIsDefaultShipping(true);
65 | return $builder;
66 | }
67 |
68 | public function asDefaultBilling(): AddressBuilder
69 | {
70 | $builder = clone $this;
71 | $builder->address->setIsDefaultBilling(true);
72 | return $builder;
73 | }
74 |
75 | public function withPrefix(string $prefix): AddressBuilder
76 | {
77 | $builder = clone $this;
78 | $builder->address->setPrefix($prefix);
79 | return $builder;
80 | }
81 |
82 | public function withFirstname(string $firstname): AddressBuilder
83 | {
84 | $builder = clone $this;
85 | $builder->address->setFirstname($firstname);
86 | return $builder;
87 | }
88 |
89 | public function withMiddlename(string $middlename): AddressBuilder
90 | {
91 | $builder = clone $this;
92 | $builder->address->setMiddlename($middlename);
93 | return $builder;
94 | }
95 |
96 | public function withLastname(string $lastname): AddressBuilder
97 | {
98 | $builder = clone $this;
99 | $builder->address->setLastname($lastname);
100 | return $builder;
101 | }
102 |
103 | public function withSuffix(string $suffix): AddressBuilder
104 | {
105 | $builder = clone $this;
106 | $builder->address->setSuffix($suffix);
107 | return $builder;
108 | }
109 |
110 | public function withStreet(string $street): AddressBuilder
111 | {
112 | $builder = clone $this;
113 | $builder->address->setStreet((array)$street);
114 | return $builder;
115 | }
116 |
117 | public function withCompany(string $company): AddressBuilder
118 | {
119 | $builder = clone $this;
120 | $builder->address->setCompany($company);
121 | return $builder;
122 | }
123 |
124 | public function withTelephone(string $telephone): AddressBuilder
125 | {
126 | $builder = clone $this;
127 | $builder->address->setTelephone($telephone);
128 | return $builder;
129 | }
130 |
131 | public function withPostcode(string $postcode): AddressBuilder
132 | {
133 | $builder = clone $this;
134 | $builder->address->setPostcode($postcode);
135 | return $builder;
136 | }
137 |
138 | public function withCity(string $city): AddressBuilder
139 | {
140 | $builder = clone $this;
141 | $builder->address->setCity($city);
142 | return $builder;
143 | }
144 |
145 | public function withCountryId(string $countryId): AddressBuilder
146 | {
147 | $builder = clone $this;
148 | $builder->address->setCountryId($countryId);
149 | return $builder;
150 | }
151 |
152 | public function withRegionId(int $regionId): AddressBuilder
153 | {
154 | $builder = clone $this;
155 | $builder->address->setRegionId($regionId);
156 | return $builder;
157 | }
158 |
159 | /**
160 | * @param mixed[] $values
161 | * @return AddressBuilder
162 | */
163 | public function withCustomAttributes(array $values): AddressBuilder
164 | {
165 | $builder = clone $this;
166 | foreach ($values as $code => $value) {
167 | $builder->address->setCustomAttribute($code, $value);
168 | }
169 | return $builder;
170 | }
171 |
172 | /**
173 | * @return AddressInterface
174 | * @throws LocalizedException
175 | */
176 | public function build(): AddressInterface
177 | {
178 | return $this->addressRepository->save($this->address);
179 | }
180 |
181 | public function buildWithoutSave(): AddressInterface
182 | {
183 | return clone $this->address;
184 | }
185 |
186 | private static function prepareFakeAddress(
187 | ObjectManagerInterface $objectManager,
188 | string $locale = 'de_DE'
189 | ): AddressInterface {
190 | $faker = FakerFactory::create($locale);
191 | $countryCode = substr($locale, -2);
192 |
193 | try {
194 | $region = $faker->province;
195 | } catch (InvalidArgumentException $exception) {
196 | $region = $faker->state;
197 | }
198 |
199 | $regionId = $objectManager->create(Region::class)->loadByName($region, $countryCode)->getId();
200 |
201 | /** @var AddressInterface $address */
202 | $address = $objectManager->create(AddressInterface::class);
203 | $address
204 | ->setTelephone($faker->phoneNumber)
205 | ->setPostcode($faker->postcode)
206 | ->setCountryId($countryCode)
207 | ->setCity($faker->city)
208 | ->setCompany($faker->company)
209 | ->setStreet([$faker->streetAddress])
210 | ->setLastname($faker->lastName)
211 | ->setFirstname($faker->firstName)
212 | ->setRegionId($regionId);
213 |
214 | return $address;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/src/Customer/CustomerBuilder.php:
--------------------------------------------------------------------------------
1 | customerRepository = $customerRepository;
50 | $this->customer = $customer;
51 | $this->encryptor = $encryptor;
52 | $this->password = $password;
53 | $this->addressBuilders = $addressBuilders;
54 | }
55 |
56 | public function __clone()
57 | {
58 | $this->customer = clone $this->customer;
59 | }
60 |
61 | public static function aCustomer(): CustomerBuilder
62 | {
63 | $objectManager = Bootstrap::getObjectManager();
64 | /** @var CustomerInterface $customer */
65 | $customer = $objectManager->create(CustomerInterface::class);
66 | $customer->setWebsiteId(1)
67 | ->setGroupId(1)
68 | ->setStoreId(1)
69 | ->setPrefix('Mr.')
70 | ->setFirstname('John')
71 | ->setMiddlename('A')
72 | ->setLastname('Smith')
73 | ->setSuffix('Esq.')
74 | ->setTaxvat('12')
75 | ->setGender(0);
76 | $password = 'Test#123';
77 | return new self(
78 | $objectManager->create(CustomerRepositoryInterface::class),
79 | $customer,
80 | $objectManager->create(Encryptor::class),
81 | $password
82 | );
83 | }
84 |
85 | public function withAddresses(AddressBuilder ...$addressBuilders): CustomerBuilder
86 | {
87 | $builder = clone $this;
88 | $builder->addressBuilders = $addressBuilders;
89 | return $builder;
90 | }
91 |
92 | public function withEmail(string $email): CustomerBuilder
93 | {
94 | $builder = clone $this;
95 | $builder->customer->setEmail($email);
96 | return $builder;
97 | }
98 |
99 | public function withGroupId(int $groupId): CustomerBuilder
100 | {
101 | $builder = clone $this;
102 | $builder->customer->setGroupId($groupId);
103 | return $builder;
104 | }
105 |
106 | public function withStoreId(int $storeId): CustomerBuilder
107 | {
108 | $builder = clone $this;
109 | $builder->customer->setStoreId($storeId);
110 | return $builder;
111 | }
112 |
113 | public function withWebsiteId(int $websiteId): CustomerBuilder
114 | {
115 | $builder = clone $this;
116 | $builder->customer->setWebsiteId($websiteId);
117 | return $builder;
118 | }
119 |
120 | public function withPrefix(string $prefix): CustomerBuilder
121 | {
122 | $builder = clone $this;
123 | $builder->customer->setPrefix($prefix);
124 | return $builder;
125 | }
126 |
127 | public function withFirstname(string $firstname): CustomerBuilder
128 | {
129 | $builder = clone $this;
130 | $builder->customer->setFirstname($firstname);
131 | return $builder;
132 | }
133 |
134 | public function withMiddlename(string $middlename): CustomerBuilder
135 | {
136 | $builder = clone $this;
137 | $builder->customer->setMiddlename($middlename);
138 | return $builder;
139 | }
140 |
141 | public function withLastname(string $lastname): CustomerBuilder
142 | {
143 | $builder = clone $this;
144 | $builder->customer->setLastname($lastname);
145 | return $builder;
146 | }
147 |
148 | public function withSuffix(string $suffix): CustomerBuilder
149 | {
150 | $builder = clone $this;
151 | $builder->customer->setSuffix($suffix);
152 | return $builder;
153 | }
154 |
155 | public function withTaxvat(string $taxvat): CustomerBuilder
156 | {
157 | $builder = clone $this;
158 | $builder->customer->setTaxvat($taxvat);
159 | return $builder;
160 | }
161 |
162 | public function withDob(string $dob): CustomerBuilder
163 | {
164 | $builder = clone $this;
165 | $builder->customer->setDob($dob);
166 | return $builder;
167 | }
168 |
169 | /**
170 | * @param mixed[] $values
171 | * @return CustomerBuilder
172 | */
173 | public function withCustomAttributes(array $values): CustomerBuilder
174 | {
175 | $builder = clone $this;
176 | foreach ($values as $code => $value) {
177 | $builder->customer->setCustomAttribute($code, $value);
178 | }
179 | return $builder;
180 | }
181 |
182 | public function withConfirmation(string $confirmation): CustomerBuilder
183 | {
184 | $builder = clone $this;
185 | $builder->customer->setConfirmation($confirmation);
186 | return $builder;
187 | }
188 |
189 | /**
190 | * @return CustomerInterface
191 | * @throws LocalizedException
192 | */
193 | public function build(): CustomerInterface
194 | {
195 | $builder = clone $this;
196 | if (!$builder->customer->getEmail()) {
197 | $builder->customer->setEmail(sha1(uniqid('', true)) . '@example.com');
198 | }
199 | $addresses = array_map(
200 | function (AddressBuilder $addressBuilder) {
201 | return $addressBuilder->buildWithoutSave();
202 | },
203 | $builder->addressBuilders
204 | );
205 | $builder->customer->setAddresses($addresses);
206 | $customer = $builder->saveNewCustomer();
207 | /*
208 | * Magento automatically sets random confirmation key for new account with password.
209 | * We need to save again with our own confirmation (null for confirmed customer)
210 | */
211 | $customer->setConfirmation((string)$builder->customer->getConfirmation());
212 | return $builder->customerRepository->save($customer);
213 | }
214 |
215 | /**
216 | * @SuppressWarnings(PHPMD.UnusedPrivateMethod) False positive: the method is used in build() on the cloned builder
217 | *
218 | * @return CustomerInterface
219 | * @throws LocalizedException
220 | */
221 | private function saveNewCustomer(): CustomerInterface
222 | {
223 | return $this->customerRepository->save($this->customer, $this->encryptor->getHash($this->password, true));
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/src/Customer/CustomerFixture.php:
--------------------------------------------------------------------------------
1 | customer = $customer;
24 | }
25 |
26 | public function getCustomer(): CustomerInterface
27 | {
28 | return $this->customer;
29 | }
30 |
31 | public function getDefaultShippingAddressId(): int
32 | {
33 | return (int) $this->customer->getDefaultShipping();
34 | }
35 |
36 | public function getDefaultBillingAddressId(): int
37 | {
38 | return (int) $this->customer->getDefaultBilling();
39 | }
40 |
41 | public function getOtherAddressId(): int
42 | {
43 | return $this->getNonDefaultAddressIds()[0];
44 | }
45 |
46 | /**
47 | * @return int[]
48 | */
49 | public function getNonDefaultAddressIds(): array
50 | {
51 | return array_values(
52 | array_diff(
53 | $this->getAllAddressIds(),
54 | [$this->getDefaultBillingAddressId(), $this->getDefaultShippingAddressId()]
55 | )
56 | );
57 | }
58 |
59 | /**
60 | * @return int[]
61 | */
62 | public function getAllAddressIds(): array
63 | {
64 | return array_map(
65 | function (AddressInterface $address): int {
66 | return (int)$address->getId();
67 | },
68 | (array)$this->customer->getAddresses()
69 | );
70 | }
71 |
72 | public function getId(): int
73 | {
74 | return (int) $this->customer->getId();
75 | }
76 |
77 | public function getConfirmation(): string
78 | {
79 | return (string)$this->customer->getConfirmation();
80 | }
81 |
82 | public function getEmail(): string
83 | {
84 | return $this->customer->getEmail();
85 | }
86 |
87 | public function login(Session $session = null): void
88 | {
89 | if ($session === null) {
90 | $objectManager = Bootstrap::getObjectManager();
91 | $objectManager->removeSharedInstance(Session::class);
92 | $session = $objectManager->get(Session::class);
93 | }
94 | $session->setCustomerId($this->getId());
95 | }
96 |
97 | public function logout(Session $session = null): void
98 | {
99 | if ($session === null) {
100 | $objectManager = Bootstrap::getObjectManager();
101 | $session = $objectManager->get(Session::class);
102 | }
103 |
104 | $session->logout();
105 | }
106 |
107 | public function rollback(): void
108 | {
109 | CustomerFixtureRollback::create()->execute($this);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Customer/CustomerFixturePool.php:
--------------------------------------------------------------------------------
1 | customerFixtures[] = new CustomerFixture($customer);
21 | } else {
22 | $this->customerFixtures[$key] = new CustomerFixture($customer);
23 | }
24 | }
25 |
26 | /**
27 | * Returns customer fixture by key, or last added if key not specified
28 | *
29 | * @param string|null $key
30 | * @return CustomerFixture
31 | */
32 | public function get(string $key = null): CustomerFixture
33 | {
34 | if ($key === null) {
35 | $key = \array_key_last($this->customerFixtures);
36 | }
37 | if ($key === null || !array_key_exists($key, $this->customerFixtures)) {
38 | throw new \OutOfBoundsException('No matching customer found in fixture pool');
39 | }
40 | return $this->customerFixtures[$key];
41 | }
42 |
43 | public function rollback(): void
44 | {
45 | CustomerFixtureRollback::create()->execute(...values($this->customerFixtures));
46 | $this->customerFixtures = [];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Customer/CustomerFixtureRollback.php:
--------------------------------------------------------------------------------
1 | registry = $registry;
28 | $this->customerRepository = $customerRepository;
29 | }
30 |
31 | public static function create(): CustomerFixtureRollback
32 | {
33 | $objectManager = Bootstrap::getObjectManager();
34 | return new self(
35 | $objectManager->get(Registry::class),
36 | $objectManager->get(CustomerRepositoryInterface::class)
37 | );
38 | }
39 |
40 | /**
41 | * @param CustomerFixture ...$customerFixtures
42 | * @throws LocalizedException
43 | */
44 | public function execute(CustomerFixture ...$customerFixtures): void
45 | {
46 | $this->registry->unregister('isSecureArea');
47 | $this->registry->register('isSecureArea', true);
48 |
49 | foreach ($customerFixtures as $customerFixture) {
50 | $this->customerRepository->deleteById($customerFixture->getId());
51 | }
52 |
53 | $this->registry->unregister('isSecureArea');
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Sales/CreditmemoBuilder.php:
--------------------------------------------------------------------------------
1 | itemFactory = $itemFactory;
50 | $this->refundOrder = $refundOrder;
51 | $this->creditmemoRepository = $creditmemoRepository;
52 | $this->order = $order;
53 |
54 | $this->orderItems = [];
55 | }
56 |
57 | public static function forOrder(
58 | Order $order
59 | ): CreditmemoBuilder {
60 | $objectManager = Bootstrap::getObjectManager();
61 |
62 | return new static(
63 | $objectManager->create(CreditmemoItemCreationInterfaceFactory::class),
64 | $objectManager->create(RefundOrderInterface::class),
65 | $objectManager->create(CreditmemoRepositoryInterface::class),
66 | $order
67 | );
68 | }
69 |
70 | public function withItem(int $orderItemId, int $qty): CreditmemoBuilder
71 | {
72 | $builder = clone $this;
73 |
74 | $builder->orderItems[$orderItemId] = $qty;
75 |
76 | return $builder;
77 | }
78 |
79 | public function build(): CreditmemoInterface
80 | {
81 | // order must be invoiced before a refund can be created.
82 | if ($this->order->canInvoice()) {
83 | InvoiceBuilder::forOrder($this->order)->build();
84 | }
85 |
86 | // refund items must be explicitly set
87 | if (empty($this->orderItems)) {
88 | foreach ($this->order->getItems() as $item) {
89 | $this->orderItems[$item->getItemId()] = (float)$item->getQtyOrdered();
90 | }
91 | }
92 |
93 | $creditmemoItems = $this->buildCreditmemoItems();
94 |
95 | $creditmemoId = $this->refundOrder->execute($this->order->getEntityId(), $creditmemoItems);
96 |
97 | return $this->creditmemoRepository->get($creditmemoId);
98 | }
99 |
100 | /**
101 | * @return \Magento\Sales\Api\Data\CreditmemoItemCreationInterface[]
102 | */
103 | private function buildCreditmemoItems(): array
104 | {
105 | $creditmemoItems = [];
106 | foreach ($this->orderItems as $orderItemId => $qty) {
107 | $creditmemoItem = $this->itemFactory->create();
108 | $creditmemoItem->setOrderItemId($orderItemId);
109 | $creditmemoItem->setQty($qty);
110 | $creditmemoItems[] = $creditmemoItem;
111 | }
112 | return $creditmemoItems;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/Sales/CreditmemoFixture.php:
--------------------------------------------------------------------------------
1 | creditmemo = $creditmemo;
18 | }
19 |
20 | public function getCreditmemo(): CreditmemoInterface
21 | {
22 | return $this->creditmemo;
23 | }
24 |
25 | public function getId(): int
26 | {
27 | return (int) $this->creditmemo->getEntityId();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Sales/CreditmemoFixturePool.php:
--------------------------------------------------------------------------------
1 | creditmemoFixtures[] = new CreditmemoFixture($creditmemo);
20 | } else {
21 | $this->creditmemoFixtures[$key] = new CreditmemoFixture($creditmemo);
22 | }
23 | }
24 |
25 | /**
26 | * Returns creditmemo fixture by key, or last added if key not specified
27 | *
28 | * @param string|null $key
29 | * @return CreditmemoFixture
30 | */
31 | public function get(string $key = null): CreditmemoFixture
32 | {
33 | if ($key === null) {
34 | $key = \array_key_last($this->creditmemoFixtures);
35 | }
36 | if ($key === null || !array_key_exists($key, $this->creditmemoFixtures)) {
37 | throw new \OutOfBoundsException('No matching creditmemo found in fixture pool');
38 | }
39 | return $this->creditmemoFixtures[$key];
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Sales/InvoiceBuilder.php:
--------------------------------------------------------------------------------
1 | itemFactory = $itemFactory;
50 | $this->invoiceOrder = $invoiceOrder;
51 | $this->invoiceRepository = $invoiceRepository;
52 | $this->order = $order;
53 |
54 | $this->orderItems = [];
55 | }
56 |
57 | public static function forOrder(
58 | Order $order
59 | ): InvoiceBuilder {
60 | $objectManager = Bootstrap::getObjectManager();
61 |
62 | return new static(
63 | $objectManager->create(InvoiceItemCreationInterfaceFactory::class),
64 | $objectManager->create(InvoiceOrderInterface::class),
65 | $objectManager->create(InvoiceRepositoryInterface::class),
66 | $order
67 | );
68 | }
69 |
70 | public function withItem(int $orderItemId, int $qty): InvoiceBuilder
71 | {
72 | $builder = clone $this;
73 |
74 | $builder->orderItems[$orderItemId] = $qty;
75 |
76 | return $builder;
77 | }
78 |
79 | public function build(): InvoiceInterface
80 | {
81 | $invoiceItems = $this->buildInvoiceItems();
82 |
83 | $invoiceId = $this->invoiceOrder->execute($this->order->getEntityId(), false, $invoiceItems);
84 |
85 | return $this->invoiceRepository->get($invoiceId);
86 | }
87 |
88 | /**
89 | * @return \Magento\Sales\Api\Data\InvoiceItemCreationInterface[]
90 | */
91 | private function buildInvoiceItems(): array
92 | {
93 | $invoiceItems = [];
94 |
95 | foreach ($this->orderItems as $orderItemId => $qty) {
96 | $invoiceItem = $this->itemFactory->create();
97 | $invoiceItem->setOrderItemId($orderItemId);
98 | $invoiceItem->setQty($qty);
99 | $invoiceItems[] = $invoiceItem;
100 | }
101 | return $invoiceItems;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Sales/InvoiceFixture.php:
--------------------------------------------------------------------------------
1 | invoice = $shipment;
18 | }
19 |
20 | public function getInvoice(): InvoiceInterface
21 | {
22 | return $this->invoice;
23 | }
24 |
25 | public function getId(): int
26 | {
27 | return (int) $this->invoice->getEntityId();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Sales/InvoiceFixturePool.php:
--------------------------------------------------------------------------------
1 | invoiceFixtures[] = new InvoiceFixture($invoice);
20 | } else {
21 | $this->invoiceFixtures[$key] = new InvoiceFixture($invoice);
22 | }
23 | }
24 |
25 | /**
26 | * Returns invoice fixture by key, or last added if key not specified
27 | *
28 | * @param string|null $key
29 | * @return InvoiceFixture
30 | */
31 | public function get(string $key = null): InvoiceFixture
32 | {
33 | if ($key === null) {
34 | $key = \array_key_last($this->invoiceFixtures);
35 | }
36 | if ($key === null || !array_key_exists($key, $this->invoiceFixtures)) {
37 | throw new \OutOfBoundsException('No matching invoice found in fixture pool');
38 | }
39 | return $this->invoiceFixtures[$key];
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Sales/OrderBuilder.php:
--------------------------------------------------------------------------------
1 | productBuilders = $productBuilders;
57 |
58 | return $builder;
59 | }
60 |
61 | public function withCustomer(CustomerBuilder $customerBuilder): OrderBuilder
62 | {
63 | $builder = clone $this;
64 | $builder->customerBuilder = $customerBuilder;
65 |
66 | return $builder;
67 | }
68 |
69 | public function withCart(CartBuilder $cartBuilder): OrderBuilder
70 | {
71 | $builder = clone $this;
72 | $builder->cartBuilder = $cartBuilder;
73 |
74 | return $builder;
75 | }
76 |
77 | public function withShippingMethod(string $shippingMethod): OrderBuilder
78 | {
79 | $builder = clone $this;
80 | $builder->shippingMethod = $shippingMethod;
81 |
82 | return $builder;
83 | }
84 |
85 | public function withPaymentMethod(string $paymentMethod): OrderBuilder
86 | {
87 | $builder = clone $this;
88 | $builder->paymentMethod = $paymentMethod;
89 |
90 | return $builder;
91 | }
92 |
93 | /**
94 | * @return Order
95 | * @throws \Exception
96 | */
97 | public function build(): Order
98 | {
99 | $builder = clone $this;
100 |
101 | if (empty($builder->productBuilders)) {
102 | // init simple products
103 | for ($i = 0; $i < 3; $i++) {
104 | $builder->productBuilders[] = ProductBuilder::aSimpleProduct();
105 | }
106 | }
107 |
108 | // create products
109 | $products = array_map(
110 | static function (ProductBuilder $productBuilder) {
111 | return $productBuilder->build();
112 | },
113 | $builder->productBuilders
114 | );
115 |
116 | if (empty($builder->customerBuilder)) {
117 | // init customer
118 | $builder->customerBuilder = CustomerBuilder::aCustomer()
119 | ->withAddresses(AddressBuilder::anAddress()->asDefaultBilling()->asDefaultShipping());
120 | }
121 |
122 | // log customer in
123 | $customer = $builder->customerBuilder->build();
124 | $customerFixture = new CustomerFixture($customer);
125 | $customerFixture->login();
126 |
127 | if (empty($builder->cartBuilder)) {
128 | // init cart, add products
129 | $builder->cartBuilder = CartBuilder::forCurrentSession();
130 | foreach ($products as $product) {
131 | $qty = 1;
132 | $builder->cartBuilder = $builder->cartBuilder->withSimpleProduct($product->getSku(), $qty);
133 | }
134 | }
135 |
136 | // check out, place order
137 | $checkout = CustomerCheckout::fromCart($builder->cartBuilder->build());
138 | if ($builder->shippingMethod) {
139 | $checkout = $checkout->withShippingMethodCode($builder->shippingMethod);
140 | }
141 |
142 | if ($builder->paymentMethod) {
143 | $checkout = $checkout->withPaymentMethodCode($builder->paymentMethod);
144 | }
145 |
146 | $order = $checkout->placeOrder();
147 |
148 | $customerFixture->logout();
149 |
150 | return $order;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/Sales/OrderFixture.php:
--------------------------------------------------------------------------------
1 | order = $order;
18 | }
19 |
20 | public function getOrder(): Order
21 | {
22 | return $this->order;
23 | }
24 |
25 | public function getId(): int
26 | {
27 | return (int) $this->order->getEntityId();
28 | }
29 |
30 | public function getCustomerId(): int
31 | {
32 | return (int) $this->order->getCustomerId();
33 | }
34 |
35 | public function getCustomerEmail(): string
36 | {
37 | return (string) $this->order->getCustomerEmail();
38 | }
39 |
40 | /**
41 | * Obtain `qty_ordered` per order item, indexed with `item_id`.
42 | *
43 | * @return float[]
44 | */
45 | public function getOrderItemQtys(): array
46 | {
47 | $qtys = [];
48 | foreach ($this->order->getItems() as $item) {
49 | $qtys[$item->getItemId()] = (float)$item->getQtyOrdered();
50 | }
51 |
52 | return $qtys;
53 | }
54 |
55 | public function getPaymentMethod(): string
56 | {
57 | $payment = $this->order->getPayment();
58 | if ($payment === null) {
59 | throw new \RuntimeException('Order does not have any payment information');
60 | }
61 | return (string)$payment->getMethod();
62 | }
63 |
64 | public function getShippingMethod(): string
65 | {
66 | return (string)$this->order->getShippingMethod();
67 | }
68 |
69 | public function rollback(): void
70 | {
71 | OrderFixtureRollback::create()->execute($this);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Sales/OrderFixturePool.php:
--------------------------------------------------------------------------------
1 | orderFixtures[] = new OrderFixture($order);
21 | } else {
22 | $this->orderFixtures[$key] = new OrderFixture($order);
23 | }
24 | }
25 |
26 | /**
27 | * Returns order fixture by key, or last added if key not specified
28 | *
29 | * @param string|null $key
30 | * @return OrderFixture
31 | */
32 | public function get(string $key = null): OrderFixture
33 | {
34 | if ($key === null) {
35 | $key = \array_key_last($this->orderFixtures);
36 | }
37 | if ($key === null || !array_key_exists($key, $this->orderFixtures)) {
38 | throw new \OutOfBoundsException('No matching order found in fixture pool');
39 | }
40 | return $this->orderFixtures[$key];
41 | }
42 |
43 | public function rollback(): void
44 | {
45 | OrderFixtureRollback::create()->execute(...values($this->orderFixtures));
46 | $this->orderFixtures = [];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Sales/OrderFixtureRollback.php:
--------------------------------------------------------------------------------
1 | registry = $registry;
48 | $this->orderRepository = $orderRepository;
49 | $this->customerRepository = $customerRepository;
50 | $this->productRepository = $productRepository;
51 | }
52 |
53 | public static function create(): OrderFixtureRollback
54 | {
55 | $objectManager = Bootstrap::getObjectManager();
56 |
57 | return new self(
58 | $objectManager->get(Registry::class),
59 | $objectManager->get(OrderRepositoryInterface::class),
60 | $objectManager->get(CustomerRepositoryInterface::class),
61 | $objectManager->get(ProductRepositoryInterface::class)
62 | );
63 | }
64 |
65 | /**
66 | * Roll back orders with associated customers and products.
67 | *
68 | * @param OrderFixture ...$orderFixtures
69 | * @throws LocalizedException
70 | * @throws NoSuchEntityException
71 | */
72 | public function execute(OrderFixture ...$orderFixtures): void
73 | {
74 | $this->registry->unregister('isSecureArea');
75 | $this->registry->register('isSecureArea', true);
76 |
77 | foreach ($orderFixtures as $orderFixture) {
78 | $orderItems = $this->orderRepository->get($orderFixture->getId())->getItems();
79 |
80 | $this->orderRepository->deleteById($orderFixture->getId());
81 | $this->customerRepository->deleteById($orderFixture->getCustomerId());
82 | array_walk(
83 | $orderItems,
84 | function (OrderItemInterface $orderItem) {
85 | try {
86 | $this->productRepository->deleteById($orderItem->getSku());
87 | } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
88 | // ignore if already deleted
89 | }
90 | }
91 | );
92 | }
93 |
94 | $this->registry->unregister('isSecureArea');
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/Sales/ShipmentBuilder.php:
--------------------------------------------------------------------------------
1 | itemFactory = $itemFactory;
63 | $this->trackFactory = $trackFactory;
64 | $this->shipOrder = $shipOrder;
65 | $this->shipmentRepository = $shipmentRepository;
66 | $this->order = $order;
67 |
68 | $this->orderItems = [];
69 | $this->trackingNumbers = [];
70 | }
71 |
72 | public static function forOrder(
73 | Order $order
74 | ): ShipmentBuilder {
75 | $objectManager = Bootstrap::getObjectManager();
76 |
77 | return new static(
78 | $objectManager->create(ShipmentItemCreationInterfaceFactory::class),
79 | $objectManager->create(ShipmentTrackCreationInterfaceFactory::class),
80 | $objectManager->create(ShipOrderInterface::class),
81 | $objectManager->create(ShipmentRepositoryInterface::class),
82 | $order
83 | );
84 | }
85 |
86 | public function withItem(int $orderItemId, int $qty): ShipmentBuilder
87 | {
88 | $builder = clone $this;
89 |
90 | $builder->orderItems[$orderItemId] = $qty;
91 |
92 | return $builder;
93 | }
94 |
95 | public function withTrackingNumbers(string ...$trackingNumbers): ShipmentBuilder
96 | {
97 | $builder = clone $this;
98 |
99 | $builder->trackingNumbers = $trackingNumbers;
100 |
101 | return $builder;
102 | }
103 |
104 | public function build(): ShipmentInterface
105 | {
106 | $shipmentItems = $this->buildShipmentItems();
107 | $tracks = $this->buildTracks();
108 |
109 | $shipmentId = $this->shipOrder->execute(
110 | $this->order->getEntityId(),
111 | $shipmentItems,
112 | false,
113 | false,
114 | null,
115 | $tracks
116 | );
117 |
118 | $shipment = $this->shipmentRepository->get($shipmentId);
119 | if (!empty($this->trackingNumbers)) {
120 | $shipment->setShippingLabel('%PDF-1.4');
121 | $this->shipmentRepository->save($shipment);
122 | }
123 |
124 | return $shipment;
125 | }
126 |
127 | /**
128 | * @return \Magento\Sales\Api\Data\ShipmentTrackCreationInterface[]
129 | */
130 | private function buildTracks(): array
131 | {
132 | return array_map(
133 | function (string $trackingNumber): ShipmentTrackCreationInterface {
134 | $carrierCode = strtok((string)$this->order->getShippingMethod(), '_');
135 | $track = $this->trackFactory->create();
136 | $track->setCarrierCode($carrierCode);
137 | $track->setTitle($carrierCode);
138 | $track->setTrackNumber($trackingNumber);
139 |
140 | return $track;
141 | },
142 | $this->trackingNumbers
143 | );
144 | }
145 |
146 | /**
147 | * @return \Magento\Sales\Api\Data\ShipmentItemCreationInterface[]
148 | */
149 | private function buildShipmentItems(): array
150 | {
151 | $shipmentItems = [];
152 |
153 | foreach ($this->orderItems as $orderItemId => $qty) {
154 | $shipmentItem = $this->itemFactory->create();
155 | $shipmentItem->setOrderItemId($orderItemId);
156 | $shipmentItem->setQty($qty);
157 | $shipmentItems[] = $shipmentItem;
158 | }
159 | return $shipmentItems;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/Sales/ShipmentFixture.php:
--------------------------------------------------------------------------------
1 | shipment = $shipment;
18 | }
19 |
20 | public function getShipment(): ShipmentInterface
21 | {
22 | return $this->shipment;
23 | }
24 |
25 | public function getId(): int
26 | {
27 | return (int) $this->shipment->getEntityId();
28 | }
29 |
30 | /**
31 | * @return \Magento\Sales\Api\Data\ShipmentTrackInterface[]
32 | */
33 | public function getTracks(): array
34 | {
35 | return $this->shipment->getTracks();
36 | }
37 |
38 | public function getShippingLabel(): string
39 | {
40 | return (string) $this->shipment->getShippingLabel();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Sales/ShipmentFixturePool.php:
--------------------------------------------------------------------------------
1 | shipmentFixtures[] = new ShipmentFixture($shipment);
20 | } else {
21 | $this->shipmentFixtures[$key] = new ShipmentFixture($shipment);
22 | }
23 | }
24 |
25 | /**
26 | * Returns shipment fixture by key, or last added if key not specified
27 | *
28 | * @param string|null $key
29 | * @return ShipmentFixture
30 | */
31 | public function get(string $key = null): ShipmentFixture
32 | {
33 | if ($key === null) {
34 | $key = \array_key_last($this->shipmentFixtures);
35 | }
36 | if ($key === null || !array_key_exists($key, $this->shipmentFixtures)) {
37 | throw new \OutOfBoundsException('No matching shipment found in fixture pool');
38 | }
39 | return $this->shipmentFixtures[$key];
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Theme/ThemeFixture.php:
--------------------------------------------------------------------------------
1 | get(Registration::class);
23 | $registration->register();
24 | }
25 |
26 | /**
27 | * Set the current theme
28 | *
29 | * @param string $themePath a theme identifier without the area, e.g. Magento/luma
30 | */
31 | public static function setCurrentTheme(string $themePath): void
32 | {
33 | /** @var DesignInterface $design */
34 | $design = Bootstrap::getObjectManager()->get(DesignInterface::class);
35 | $design->setDesignTheme($themePath);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/tests/Catalog/CategoryBuilderTest.php:
--------------------------------------------------------------------------------
1 | categoryRepository = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class);
35 | $this->categories = [];
36 | $this->products = [];
37 | }
38 |
39 | protected function tearDown(): void
40 | {
41 | if (!empty($this->categories)) {
42 | foreach ($this->categories as $product) {
43 | CategoryFixtureRollback::create()->execute($product);
44 | }
45 | }
46 | if (! empty($this->products)) {
47 | foreach ($this->products as $product) {
48 | ProductFixtureRollback::create()->execute($product);
49 | }
50 | }
51 | }
52 |
53 | public function testDefaultTopLevelCategory()
54 | {
55 | $categoryFixture = new CategoryFixture(
56 | CategoryBuilder::topLevelCategory()->build()
57 | );
58 | $this->categories[] = $categoryFixture;
59 |
60 | /** @var Category $category */
61 | $category = $this->categoryRepository->get($categoryFixture->getId());
62 |
63 | // store ids are mixed type, normalize first for strict type checking
64 | $storeIds = array_map('strval', $category->getStoreIds());
65 |
66 | $this->assertEquals('Top Level Category', $category->getName(), 'Category name does not match expected value.');
67 | $this->assertContains('0', $storeIds, 'Admin store ID is not assigned.');
68 | $this->assertContains('1', $storeIds, 'Default store ID is not assigned.');
69 | $this->assertEquals(
70 | '1/2/' . $categoryFixture->getId(),
71 | $category->getPath(),
72 | 'Category path does not match expected value.'
73 | );
74 | }
75 |
76 | public function testDefaultChildCategory()
77 | {
78 | $parentCategoryFixture = new CategoryFixture(
79 | CategoryBuilder::topLevelCategory()->build()
80 | );
81 | $this->categories[] = $parentCategoryFixture;
82 | $childCategoryFixture = new CategoryFixture(
83 | CategoryBuilder::childCategoryOf($parentCategoryFixture)->build()
84 | );
85 |
86 | /** @var Category $category */
87 | $category = $this->categoryRepository->get($childCategoryFixture->getId());
88 |
89 | // store ids are mixed type, normalize first for strict type checking
90 | $storeIds = array_map('strval', $category->getStoreIds());
91 |
92 | $this->assertEquals('Child Category', $category->getName(), 'Category name does not match expected value.');
93 | $this->assertContains('0', $storeIds, 'Admin store ID is not assigned.');
94 | $this->assertContains('1', $storeIds, 'Default store ID is not assigned.');
95 | $this->assertEquals(
96 | '1/2/' . $parentCategoryFixture->getId() . '/' . $childCategoryFixture->getId(),
97 | $category->getPath(),
98 | 'Category path does not match expected value.'
99 | );
100 | }
101 |
102 | public function testCategoryWithSpecificAttributes()
103 | {
104 | $categoryFixture = new CategoryFixture(
105 | CategoryBuilder::topLevelCategory()
106 | ->withName('Custom Name')
107 | ->withDescription('Custom Description')
108 | ->withIsActive(false)
109 | ->withUrlKey('my-url-key')
110 | ->build()
111 | );
112 | $this->categories[] = $categoryFixture;
113 |
114 | /** @var Category $category */
115 | $category = $this->categoryRepository->get($categoryFixture->getId());
116 | $this->assertEquals('0', $category->getIsActive(), 'Category should be inactive');
117 | $this->assertEquals('Custom Name', $category->getName(), 'Category name');
118 | $this->assertEquals('my-url-key', $category->getUrlKey(), 'Category URL key');
119 | $this->assertEquals(
120 | 'Custom Description',
121 | $category->getCustomAttribute('description')->getValue(),
122 | 'Category description'
123 | );
124 | }
125 |
126 | public function testCategoryWithProducts()
127 | {
128 | $product1 = new ProductFixture(ProductBuilder::aSimpleProduct()->build());
129 | $product2 = new ProductFixture(ProductBuilder::aSimpleProduct()->build());
130 | $categoryFixture = new CategoryFixture(
131 | CategoryBuilder::topLevelCategory()->withProducts([$product1->getSku(), $product2->getSku()])->build()
132 | );
133 | $this->products[] = $product1;
134 | $this->products[] = $product2;
135 | $this->categories[] = $categoryFixture;
136 |
137 | /** @var Category $category */
138 | $category = $this->categoryRepository->get($categoryFixture->getId());
139 |
140 | $this->assertEquals(
141 | [$product1->getId() => 0, $product2->getId() => 1],
142 | $category->getProductsPosition(),
143 | 'Product positions'
144 | );
145 | }
146 |
147 | public function testMultipleCategories()
148 | {
149 | $this->categories[0] = new CategoryFixture(
150 | CategoryBuilder::topLevelCategory()->build()
151 | );
152 | $this->categories[1] = new CategoryFixture(
153 | CategoryBuilder::topLevelCategory()->build()
154 | );
155 |
156 | /** @var Category $category1 */
157 | $category1 = $this->categoryRepository->get($this->categories[0]->getId());
158 | /** @var Category $category2 */
159 | $category2 = $this->categoryRepository->get($this->categories[1]->getId());
160 | $this->assertNotEquals(
161 | $category1->getUrlKey(),
162 | $category2->getUrlKey(),
163 | 'Categories should be created with different URL keys'
164 | );
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/tests/Catalog/CategoryFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | categoryFixtures = new CategoryFixturePool();
31 | $this->categoryRepository = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class);
32 | }
33 |
34 | public function testLastCategoryFixtureReturnedByDefault()
35 | {
36 | $firstCategory = $this->createCategory();
37 | $lastCategory = $this->createCategory();
38 | $this->categoryFixtures->add($firstCategory);
39 | $this->categoryFixtures->add($lastCategory);
40 | $categoryFixture = $this->categoryFixtures->get();
41 | $this->assertEquals($lastCategory->getId(), $categoryFixture->getId());
42 | }
43 |
44 | public function testExceptionThrownWhenAccessingEmptyCategoryPool()
45 | {
46 | $this->expectException(\OutOfBoundsException::class);
47 | $this->categoryFixtures->get();
48 | }
49 |
50 | public function testCategoryFixtureReturnedByKey()
51 | {
52 | $firstCategory = $this->createCategory();
53 | $lastCategory = $this->createCategory();
54 | $this->categoryFixtures->add($firstCategory, 'first');
55 | $this->categoryFixtures->add($lastCategory, 'last');
56 | $categoryFixture = $this->categoryFixtures->get('first');
57 | $this->assertEquals($firstCategory->getId(), $categoryFixture->getId());
58 | }
59 |
60 | public function testCategoryFixtureReturnedByNumericKey()
61 | {
62 | $firstCategory = $this->createCategory();
63 | $lastCategory = $this->createCategory();
64 | $this->categoryFixtures->add($firstCategory);
65 | $this->categoryFixtures->add($lastCategory);
66 | $categoryFixture = $this->categoryFixtures->get(0);
67 | $this->assertEquals($firstCategory->getId(), $categoryFixture->getId());
68 | }
69 |
70 | public function testExceptionThrownWhenAccessingNonexistingKey()
71 | {
72 | $category = $this->createCategory();
73 | $this->categoryFixtures->add($category, 'foo');
74 | $this->expectException(\OutOfBoundsException::class);
75 | $this->categoryFixtures->get('bar');
76 | }
77 |
78 | public function testRollbackRemovesCategorysFromPool()
79 | {
80 | $category = $this->createCategoryInDb();
81 | $this->categoryFixtures->add($category);
82 | $this->categoryFixtures->rollback();
83 | $this->expectException(\OutOfBoundsException::class);
84 | $this->categoryFixtures->get();
85 | }
86 |
87 | public function testRollbackWorksWithKeys()
88 | {
89 | $category = $this->createCategoryInDb();
90 | $this->categoryFixtures->add($category, 'key');
91 | $this->categoryFixtures->rollback();
92 | $this->expectException(\OutOfBoundsException::class);
93 | $this->categoryFixtures->get();
94 | }
95 |
96 | public function testRollbackDeletesCategorysFromDb()
97 | {
98 | $category = $this->createCategoryInDb();
99 | $this->categoryFixtures->add($category);
100 | $this->categoryFixtures->rollback();
101 | $this->expectException(NoSuchEntityException::class);
102 | $this->categoryRepository->get($category->getId());
103 | }
104 |
105 | /**
106 | * Creates dummy category object
107 | *
108 | * @return \Magento\Catalog\Model\Category
109 | * @throws \Exception
110 | */
111 | private function createCategory(): \Magento\Catalog\Model\Category
112 | {
113 | static $nextId = 1;
114 | /** @var Category $category */
115 | $category = Bootstrap::getObjectManager()->create(Category::class);
116 | $category->setId($nextId++);
117 | return $category;
118 | }
119 |
120 | /**
121 | * Creates category using builder
122 | *
123 | * @return \Magento\Catalog\Model\Category
124 | * @throws \Exception
125 | */
126 | private function createCategoryInDb(): \Magento\Catalog\Model\Category
127 | {
128 | return CategoryBuilder::topLevelCategory()->build();
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/tests/Catalog/IndexerErrorsTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('Since Magento 2.3.6 / 2.4.1 these transaction exceptions do not occur anymore');
29 | $this->expectException(\Exception::class);
30 |
31 | try {
32 | /** @var StoreManagerInterface $storeManager */
33 | $storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
34 | $secondWebsiteId = $storeManager->getWebsite('test')->getId();
35 | ProductBuilder::aSimpleProduct()->withWebsiteIds([$secondWebsiteId])->build();
36 | } catch (\Exception $exception) {
37 | // manual check, there is no common assertion in PHPUnit 6 / PHPUnit 9
38 | $this->assertNotFalse(
39 | preg_match(
40 | '{@magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php}',
41 | $exception->getMessage()
42 | )
43 | );
44 |
45 | throw $exception;
46 | }
47 | }
48 |
49 | public static function disableReindexSchedule()
50 | {
51 | /* @var IndexerInterface $model */
52 | $model = Bootstrap::getObjectManager()->get(IndexerRegistry::class)->get('catalogsearch_fulltext');
53 | self::$indexIsScheduledOrig = $model->isScheduled();
54 | $model->setScheduled(false);
55 | }
56 |
57 | public static function disableReindexScheduleRollback()
58 | {
59 | /* @var IndexerInterface $model */
60 | $model = Bootstrap::getObjectManager()->get(IndexerRegistry::class)->get('catalogsearch_fulltext');
61 | $model->setScheduled(self::$indexIsScheduledOrig);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/Catalog/OptionBuilderTest.php:
--------------------------------------------------------------------------------
1 | optionManagement = $objectManager->get(AttributeOptionManagementInterface::class);
37 | $this->optionFactory = $objectManager->get(OptionFactory::class);
38 | $this->optionResourceModel = $objectManager->get(OptionResource::class);
39 | }
40 |
41 | protected function tearDown(): void
42 | {
43 | if (!empty($this->options)) {
44 | foreach ($this->options as $optionFixture) {
45 | $optionFixture->rollBack();
46 | }
47 | }
48 | }
49 |
50 | /**
51 | * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
52 | */
53 | public function testAddOption(): void
54 | {
55 | $userDefinedAttributeCode = 'dropdown_attribute';
56 | $optionFixture = new OptionFixture(
57 | OptionBuilder::anOptionFor($userDefinedAttributeCode)->build(),
58 | $userDefinedAttributeCode
59 | );
60 | $this->options[] = $optionFixture;
61 |
62 | /** @var \Magento\Eav\Model\Entity\Attribute\Option $option */
63 | $option = $this->optionFactory->create();
64 | $this->optionResourceModel->load($option, $optionFixture->getOption()->getId());
65 |
66 | self::assertEquals($optionFixture->getOption()->getId(), $option->getId());
67 | }
68 |
69 | /**
70 | * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
71 | */
72 | public function testAddOptionWithLabel(): void
73 | {
74 | $userDefinedAttributeCode = 'dropdown_attribute';
75 | $label = uniqid('Label ', true);
76 | $optionFixture = new OptionFixture(
77 | OptionBuilder::anOptionFor($userDefinedAttributeCode)->withLabel($label)->build(),
78 | $userDefinedAttributeCode
79 | );
80 | $this->options[] = $optionFixture;
81 |
82 | /** @var \Magento\Eav\Model\Entity\Attribute\Option $option */
83 | $option = $this->optionFactory->create();
84 | $this->optionResourceModel->load($option, $optionFixture->getOption()->getId());
85 |
86 | $items = $this->optionManagement->getItems(Product::ENTITY, $userDefinedAttributeCode);
87 |
88 | self::assertEquals($optionFixture->getOption()->getId(), $option->getId());
89 | $foundLabel = false;
90 | foreach ($items as $item) {
91 | if ((int)$item->getValue() === $optionFixture->getOption()->getId()) {
92 | self::assertEquals($label, $item->getLabel());
93 | $foundLabel = true;
94 | }
95 | }
96 | if (!$foundLabel) {
97 | self::fail('No label found');
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/tests/Catalog/OptionFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | optionFixtures = new OptionFixturePool();
44 | $this->optionFactory = Bootstrap::getObjectManager()->get(AttributeOptionFactory::class);
45 | $this->optionResourceModel = Bootstrap::getObjectManager()->get(OptionResource::class);
46 | }
47 |
48 | public function testLastOptionFixtureReturnedByDefault(): void
49 | {
50 | $firstOption = $this->createOption();
51 | $lastOption = $this->createOption();
52 | $this->optionFixtures->add($firstOption, 'option_1');
53 | $this->optionFixtures->add($lastOption, 'option_2');
54 | $optionFixture = $this->optionFixtures->get();
55 | self::assertEquals($lastOption->getId(), $optionFixture->getOption()->getId());
56 | }
57 |
58 | public function testExceptionThrownWhenAccessingEmptyOptionPool(): void
59 | {
60 | $this->expectException(\OutOfBoundsException::class);
61 | $this->optionFixtures->get();
62 | }
63 |
64 | public function testOptionFixtureReturnedByKey(): void
65 | {
66 | $firstOption = $this->createOption();
67 | $lastOption = $this->createOption();
68 | $this->optionFixtures->add($firstOption, 'option_1', 'first');
69 | $this->optionFixtures->add($lastOption, 'option_2', 'last');
70 | $optionFixture = $this->optionFixtures->get('first');
71 | self::assertEquals($firstOption->getId(), $optionFixture->getOption()->getId());
72 | }
73 |
74 | public function testExceptionThrownWhenAccessingNonexistingKey(): void
75 | {
76 | $option = $this->createOption();
77 | $this->optionFixtures->add($option, 'option_1', 'foo');
78 | $this->expectException(\OutOfBoundsException::class);
79 | $this->optionFixtures->get('bar');
80 | }
81 |
82 | /**
83 | * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
84 | */
85 | public function testRollbackRemovesOptionsFromPool(): void
86 | {
87 | $option = $this->createOptionInDb($this->dbAttributeCode);
88 | $this->optionFixtures->add($option, $this->dbAttributeCode);
89 | $this->optionFixtures->rollback();
90 | $this->expectException(\OutOfBoundsException::class);
91 | $this->optionFixtures->get();
92 | }
93 |
94 | /**
95 | * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
96 | */
97 | public function testRollbackDeletesOptionsFromDb(): void
98 | {
99 | $option = $this->createOptionInDb($this->dbAttributeCode);
100 | $this->optionFixtures->add($option, $this->dbAttributeCode);
101 | $this->optionFixtures->rollback();
102 | $option = $this->optionFactory->create();
103 | $this->optionResourceModel->load($option, $option->getId());
104 | self::assertEmpty($option->getId());
105 | }
106 |
107 | /**
108 | * Creates a dummy option object
109 | *
110 | * @return AttributeOption
111 | */
112 | private function createOption(): AttributeOption
113 | {
114 | static $nextId = 1;
115 | /** @var AttributeOption $option */
116 | $option = Bootstrap::getObjectManager()->create(AttributeOption::class);
117 | $option->setId($nextId++);
118 |
119 | return $option;
120 | }
121 |
122 | /**
123 | * Uses builder to create a customer
124 | *
125 | * @param string $attributeCode
126 | * @return AttributeOption
127 | * @throws \Magento\Framework\Exception\InputException
128 | * @throws \Magento\Framework\Exception\StateException
129 | */
130 | private function createOptionInDb(string $attributeCode): AttributeOption
131 | {
132 | return OptionBuilder::anOptionFor($attributeCode)->withLabel('Testing')->build();
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/tests/Catalog/OptionFixtureRollbackTest.php:
--------------------------------------------------------------------------------
1 | optionFactory = $objectManager->get(OptionFactory::class);
31 | $this->optionResourceModel = $objectManager->get(OptionResource::class);
32 | }
33 |
34 | /**
35 | * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
36 | */
37 | public function testRollbackSingleOptionFixture(): void
38 | {
39 | $userDefinedAttributeCode = 'dropdown_attribute';
40 | $optionFixture = new OptionFixture(
41 | OptionBuilder::anOptionFor($userDefinedAttributeCode)->build(),
42 | $userDefinedAttributeCode
43 | );
44 | OptionFixtureRollback::create()->execute($optionFixture);
45 |
46 | /** @var \Magento\Eav\Model\Entity\Attribute\Option $option */
47 | $option = $this->optionFactory->create();
48 | $this->optionResourceModel->load($option, $optionFixture->getOption()->getId());
49 | self::assertNull($option->getId());
50 | }
51 |
52 | /**
53 | * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
54 | */
55 | public function testRollbackMultipleOptionFixtures(): void
56 | {
57 | $userDefinedAttributeCode = 'dropdown_attribute';
58 | $optionFixture = new OptionFixture(
59 | OptionBuilder::anOptionFor($userDefinedAttributeCode)->build(),
60 | $userDefinedAttributeCode
61 | );
62 | $otherOptionFixture = new OptionFixture(
63 | OptionBuilder::anOptionFor($userDefinedAttributeCode)->build(),
64 | $userDefinedAttributeCode
65 | );
66 | OptionFixtureRollback::create()->execute($optionFixture, $otherOptionFixture);
67 |
68 | /** @var \Magento\Eav\Model\Entity\Attribute\Option $option */
69 | $option = $this->optionFactory->create();
70 | $this->optionResourceModel->load($option, $optionFixture->getOption()->getId());
71 | self::assertNull($option->getId());
72 |
73 | $this->optionResourceModel->load($option, $otherOptionFixture->getOption()->getId());
74 | self::assertNull($option->getId());
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/Catalog/ProductFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | productFixtures = new ProductFixturePool();
31 | $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
32 | }
33 |
34 | public function testLastProductFixtureReturnedByDefault()
35 | {
36 | $firstProduct = $this->createProduct();
37 | $lastProduct = $this->createProduct();
38 | $this->productFixtures->add($firstProduct);
39 | $this->productFixtures->add($lastProduct);
40 | $productFixture = $this->productFixtures->get();
41 | $this->assertEquals($lastProduct->getSku(), $productFixture->getSku());
42 | }
43 |
44 | public function testExceptionThrownWhenAccessingEmptyProductPool()
45 | {
46 | $this->expectException(\OutOfBoundsException::class);
47 | $this->productFixtures->get();
48 | }
49 |
50 | public function testProductFixtureReturnedByKey()
51 | {
52 | $firstProduct = $this->createProduct();
53 | $lastProduct = $this->createProduct();
54 | $this->productFixtures->add($firstProduct, 'first');
55 | $this->productFixtures->add($lastProduct, 'last');
56 | $productFixture = $this->productFixtures->get('first');
57 | $this->assertEquals($firstProduct->getSku(), $productFixture->getSku());
58 | }
59 |
60 | public function testProductFixtureReturnedByNumericKey()
61 | {
62 | $firstProduct = $this->createProduct();
63 | $lastProduct = $this->createProduct();
64 | $this->productFixtures->add($firstProduct);
65 | $this->productFixtures->add($lastProduct);
66 | $productFixture = $this->productFixtures->get(0);
67 | $this->assertEquals($firstProduct->getSku(), $productFixture->getSku());
68 | }
69 |
70 | public function testExceptionThrownWhenAccessingNonexistingKey()
71 | {
72 | $product = $this->createProduct();
73 | $this->productFixtures->add($product, 'foo');
74 | $this->expectException(\OutOfBoundsException::class);
75 | $this->productFixtures->get('bar');
76 | }
77 |
78 | public function testRollbackRemovesProductsFromPool()
79 | {
80 | $product = $this->createProductInDb();
81 | $this->productFixtures->add($product);
82 | $this->productFixtures->rollback();
83 | $this->expectException(\OutOfBoundsException::class);
84 | $this->productFixtures->get();
85 | }
86 |
87 | public function testRollbackWorksWithKeys()
88 | {
89 | $product = $this->createProductInDb();
90 | $this->productFixtures->add($product, 'key');
91 | $this->productFixtures->rollback();
92 | $this->expectException(\OutOfBoundsException::class);
93 | $this->productFixtures->get();
94 | }
95 |
96 | public function testRollbackDeletesProductsFromDb()
97 | {
98 | $product = $this->createProductInDb();
99 | $this->productFixtures->add($product);
100 | $this->productFixtures->rollback();
101 | $this->expectException(NoSuchEntityException::class);
102 | $this->productRepository->get($product->getSku());
103 | }
104 |
105 | /**
106 | * Creates a dummy product object
107 | *
108 | * @return ProductInterface
109 | * @throws \Exception
110 | */
111 | private function createProduct(): ProductInterface
112 | {
113 | static $nextId = 1;
114 | /** @var ProductInterface $product */
115 | $product = Bootstrap::getObjectManager()->create(ProductInterface::class);
116 | $product->setSku('product-' . $nextId);
117 | $product->setId($nextId++);
118 | return $product;
119 | }
120 |
121 | /**
122 | * Uses builder to create a product
123 | *
124 | * @return ProductInterface
125 | * @throws \Exception
126 | */
127 | private function createProductInDb(): ProductInterface
128 | {
129 | return ProductBuilder::aSimpleProduct()->build();
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/tests/Catalog/ProductFixtureRollbackTest.php:
--------------------------------------------------------------------------------
1 | productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
25 | }
26 |
27 | public function testRollbackSingleProductFixture()
28 | {
29 | $productFixture = new ProductFixture(
30 | ProductBuilder::aSimpleProduct()->build()
31 | );
32 | ProductFixtureRollback::create()->execute($productFixture);
33 | $this->expectException(NoSuchEntityException::class);
34 | $this->productRepository->getById($productFixture->getId());
35 | }
36 |
37 | public function testRollbackMultipleProductFixtures()
38 | {
39 | $productFixture = new ProductFixture(
40 | ProductBuilder::aSimpleProduct()->build()
41 | );
42 | $otherProductFixture = new ProductFixture(
43 | ProductBuilder::aSimpleProduct()->build()
44 | );
45 | ProductFixtureRollback::create()->execute($productFixture, $otherProductFixture);
46 | $productDeleted = false;
47 | try {
48 | $this->productRepository->getById($productFixture->getId());
49 | } catch (NoSuchEntityException $e) {
50 | $productDeleted = true;
51 | }
52 | $this->assertTrue($productDeleted, 'First product should be deleted');
53 | $this->expectException(NoSuchEntityException::class);
54 | $this->productRepository->getById($otherProductFixture->getId());
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Checkout/CartBuilderTest.php:
--------------------------------------------------------------------------------
1 | productFixture = new ProductFixture(
20 | ProductBuilder::aSimpleProduct()->build()
21 | );
22 | }
23 |
24 | /**
25 | * @magentoAppIsolation enabled
26 | * @magentoAppArea frontend
27 | */
28 | public function testProductCanBeAddedWithCustomBuyRequest()
29 | {
30 | $qty = 2;
31 | $customOptionId = 42;
32 | $customOptionValue = 'foobar';
33 | $cart = CartBuilder::forCurrentSession()->withProductRequest(
34 | $this->productFixture->getSku(),
35 | $qty,
36 | ['options' => [$customOptionId => $customOptionValue]]
37 | )->build();
38 | $quoteItems = $cart->getQuote()->getAllItems();
39 | $this->assertCount(1, $quoteItems, "1 quote item should be added");
40 | /** @var Item $quoteItem */
41 | $quoteItem = reset($quoteItems);
42 | $serializedBuyRequest = $quoteItem->getOptionByCode('info_buyRequest')->getValue();
43 | if (!json_decode($serializedBuyRequest)) {
44 | // Magento 2.1 PHP serialization
45 | $serializedBuyRequest = json_encode(unserialize($serializedBuyRequest, []));
46 | }
47 | $this->assertJsonStringEqualsJsonString(
48 | json_encode(['qty' => $qty, 'options' => ['42' => 'foobar']]),
49 | $serializedBuyRequest,
50 | "Value of info_buyRequest option should be as configured"
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/Checkout/CustomerCheckoutTest.php:
--------------------------------------------------------------------------------
1 | productFixtures = new ProductFixturePool();
29 | $this->customerFixtures = new CustomerFixturePool();
30 | $this->customerFixtures->add(
31 | CustomerBuilder::aCustomer()->withAddresses(
32 | AddressBuilder::anAddress()->asDefaultBilling()->asDefaultShipping()
33 | )->build()
34 | );
35 | $this->productFixtures->add(
36 | ProductBuilder::aSimpleProduct()->withPrice(10)->build(),
37 | 'simple'
38 | );
39 | $this->productFixtures->add(
40 | ProductBuilder::aVirtualProduct()->withPrice(10)->build(),
41 | 'virtual'
42 | );
43 | }
44 |
45 | protected function tearDown(): void
46 | {
47 | $this->customerFixtures->rollback();
48 | $this->productFixtures->rollback();
49 | }
50 |
51 | /**
52 | * @magentoAppIsolation enabled
53 | * @magentoAppArea frontend
54 | */
55 | public function testCreateOrderFromCart()
56 | {
57 | $this->customerFixtures->get()->login();
58 | $checkout = CustomerCheckout::fromCart(
59 | CartBuilder::forCurrentSession()->withSimpleProduct(
60 | $this->productFixtures->get('simple')->getSku()
61 | )->build()
62 | );
63 | $order = $checkout->placeOrder();
64 | $this->assertNotEmpty($order->getEntityId(), 'Order should be saved successfully');
65 | $this->assertNotEmpty($order->getShippingDescription(), 'Order should have a shipping description');
66 | }
67 |
68 | /**
69 | * @magentoAppIsolation enabled
70 | * @magentoAppArea frontend
71 | */
72 | public function testCreateOrderFromCartWithVirtualProduct()
73 | {
74 | $this->customerFixtures->get()->login();
75 | $checkout = CustomerCheckout::fromCart(
76 | CartBuilder::forCurrentSession()->withSimpleProduct(
77 | $this->productFixtures->get('virtual')->getSku()
78 | )->build()
79 | );
80 | $order = $checkout->placeOrder();
81 | $this->assertNotEmpty($order->getEntityId(), 'Order should be saved successfully');
82 | $this->assertEmpty(
83 | $order->getExtensionAttributes()->getShippingAssignments(),
84 | 'Order with virtual product should not have any shipping assignments'
85 | );
86 | $this->assertEmpty($order->getShippingDescription(), 'Order should not have a shipping description');
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/tests/Core/ConfigFixtureTest.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $objectManager->get(MutableScopeConfigInterface::class);
35 | $this->storeManager = $objectManager->get(StoreManagerInterface::class);
36 | }
37 |
38 | public function testSetGlobalChangesDefaultScope()
39 | {
40 | ConfigFixture::setGlobal(self::STORE_NAME_PATH, 'Ye Olde Wizard Shop');
41 | $this->assertConfigValue(
42 | 'Ye Olde Wizard Shop',
43 | self::STORE_NAME_PATH,
44 | ScopeConfigInterface::SCOPE_TYPE_DEFAULT
45 | );
46 | }
47 |
48 | /**
49 | * @magentoAppArea frontend
50 | */
51 | public function testSetGlobalOverridesAllScopes()
52 | {
53 | $this->givenStoreValue(self::STORE_NAME_PATH, 'Store Override');
54 | $this->givenWebsiteValue(self::STORE_NAME_PATH, 'Website Override');
55 | ConfigFixture::setGlobal(self::STORE_NAME_PATH, 'Global Value');
56 | $this->assertConfigValue('Global Value', self::STORE_NAME_PATH, ScopeInterface::SCOPE_STORE);
57 | }
58 |
59 | /**
60 | * @magentoAppArea frontend
61 | * @magentoDataFixture Magento/Store/_files/second_store.php
62 | */
63 | public function testSetForStoreWithCurrentStore()
64 | {
65 | $this->storeManager->setCurrentStore(self::SECOND_STORE_ID_FROM_FIXTURE);
66 | ConfigFixture::setForStore(self::STORE_NAME_PATH, 'Store store');
67 | $this->assertConfigValue(
68 | 'Store store',
69 | self::STORE_NAME_PATH,
70 | ScopeInterface::SCOPE_STORE,
71 | self::SECOND_STORE_ID_FROM_FIXTURE
72 | );
73 | }
74 |
75 | /**
76 | * @magentoAppArea frontend
77 | * @magentoDataFixture Magento/Store/_files/second_store.php
78 | */
79 | public function testSetForStoreWithExplicitStore()
80 | {
81 | ConfigFixture::setForStore(self::STORE_NAME_PATH, 'Store 1', self::FIRST_STORE_ID);
82 | ConfigFixture::setForStore(self::STORE_NAME_PATH, 'Store 2', self::SECOND_STORE_ID_FROM_FIXTURE);
83 | $this->assertConfigValue(
84 | 'Store 1',
85 | self::STORE_NAME_PATH,
86 | ScopeInterface::SCOPE_STORE,
87 | self::FIRST_STORE_ID
88 | );
89 | $this->assertConfigValue(
90 | 'Store 2',
91 | self::STORE_NAME_PATH,
92 | ScopeInterface::SCOPE_STORE,
93 | self::SECOND_STORE_ID_FROM_FIXTURE
94 | );
95 | }
96 |
97 | private function givenStoreValue(string $path, string $storeValue): void
98 | {
99 | $this->scopeConfig->setValue($path, $storeValue, ScopeInterface::SCOPE_STORE);
100 | }
101 |
102 | private function givenWebsiteValue(string $path, string $websiteValue): void
103 | {
104 | $this->scopeConfig->setValue($path, $websiteValue, ScopeInterface::SCOPE_WEBSITE);
105 | }
106 |
107 | private function assertConfigValue($expectedValue, string $path, string $scope, string $scopeCode = null): void
108 | {
109 | $this->assertEquals(
110 | $expectedValue,
111 | $this->scopeConfig->getValue($path, $scope, $scopeCode)
112 | );
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/tests/Customer/CustomerBuilderTest.php:
--------------------------------------------------------------------------------
1 | objectManager = Bootstrap::getObjectManager();
37 | $this->customerRepository = $this->objectManager->create(CustomerRepositoryInterface::class);
38 | $this->customers = [];
39 | }
40 |
41 | protected function tearDown(): void
42 | {
43 | if (! empty($this->customers)) {
44 | foreach ($this->customers as $customer) {
45 | CustomerFixtureRollback::create()->execute($customer);
46 | }
47 | }
48 | }
49 |
50 | public function testDefaultCustomer(): void
51 | {
52 | $customerFixture = new CustomerFixture(
53 | CustomerBuilder::aCustomer()->build()
54 | );
55 | $this->customers[] = $customerFixture;
56 | $customer = $this->customerRepository->getById($customerFixture->getId());
57 | $this->assertNull($customer->getConfirmation(), 'Customer should be active');
58 | $this->assertEquals(1, $customer->getWebsiteId(), 'Default website');
59 | $this->assertEquals(1, $customer->getStoreId(), 'Default store');
60 | $this->assertEquals(1, $customer->getGroupId(), 'Default customer group');
61 | }
62 |
63 | public function testDefaultCustomerWithDefaultAddresses(): void
64 | {
65 | $customerFixture = new CustomerFixture(
66 | CustomerBuilder::aCustomer()
67 | ->withAddresses(
68 | AddressBuilder::anAddress()->asDefaultShipping(),
69 | AddressBuilder::anAddress()->asDefaultBilling()
70 | )->build()
71 | );
72 | $this->customers[] = $customerFixture;
73 | $customer = $this->customerRepository->getById($customerFixture->getId());
74 |
75 | $this->assertCount(
76 | 2,
77 | $customer->getAddresses(),
78 | 'Customer should have two addresses'
79 | );
80 | $this->assertNotEquals(
81 | $customer->getDefaultBilling(),
82 | $customer->getDefaultShipping(),
83 | 'Default shipping address should be different from default billing address'
84 | );
85 | }
86 |
87 | /**
88 | * @magentoDataFixture Magento/Store/_files/second_store.php
89 | */
90 | public function testCustomerWithSpecificAttributes(): void
91 | {
92 | /** @var StoreManagerInterface $storeManager */
93 | $storeManager = $this->objectManager->get(StoreManagerInterface::class);
94 | $secondStoreId = $storeManager->getStore('fixture_second_store')->getId();
95 | $customerFixture = new CustomerFixture(
96 | CustomerBuilder::aCustomer()
97 | ->withEmail('example@example.com')
98 | ->withGroupId(2)
99 | ->withStoreId($secondStoreId)
100 | ->withPrefix('Agent')
101 | ->withFirstname('James')
102 | ->withMiddlename('H')
103 | ->withLastname('Bond')
104 | ->withSuffix('007')
105 | ->withTaxvat('7')
106 | ->build()
107 | );
108 | $this->customers[] = $customerFixture;
109 | $customer = $this->customerRepository->getById($customerFixture->getId());
110 | $this->assertEquals('example@example.com', $customer->getEmail());
111 | $this->assertEquals(2, $customer->getGroupId());
112 | $this->assertEquals($secondStoreId, $customer->getStoreId());
113 | $this->assertEquals('Agent', $customer->getPrefix());
114 | $this->assertEquals('James', $customer->getFirstname());
115 | $this->assertEquals('H', $customer->getMiddlename());
116 | $this->assertEquals('Bond', $customer->getLastname());
117 | $this->assertEquals('007', $customer->getSuffix());
118 | $this->assertEquals('7', $customer->getTaxvat());
119 | }
120 |
121 | public function testAddressWithSpecificAttributes(): void
122 | {
123 | $customerFixture = new CustomerFixture(
124 | CustomerBuilder::aCustomer()
125 | ->withAddresses(
126 | AddressBuilder::anAddress()
127 | ->withPrefix('Sir')
128 | ->withFirstname('Wasch')
129 | ->withMiddleName('H')
130 | ->withLastname('Bär')
131 | ->withSuffix('Esquire')
132 | ->withStreet('Trierer Str. 791')
133 | ->withTelephone('555-666-777')
134 | ->withCompany('integer_net')
135 | ->withCountryId('DE')
136 | ->withRegionId(88)
137 | ->withPostcode('52078')
138 | ->withCity('Aachen')
139 | ->asDefaultShipping()
140 | ->asDefaultBilling()
141 | )->build()
142 | );
143 | $this->customers[] = $customerFixture;
144 | $customer = $this->customerRepository->getById($customerFixture->getId());
145 | $address = $customer->getAddresses()[0];
146 | $this->assertEquals('Sir', $address->getPrefix());
147 | $this->assertEquals('Wasch', $address->getFirstname());
148 | $this->assertEquals('H', $address->getMiddlename());
149 | $this->assertEquals('Bär', $address->getLastname());
150 | $this->assertEquals('Esquire', $address->getSuffix());
151 | $this->assertEquals(['Trierer Str. 791'], $address->getStreet());
152 | $this->assertEquals('555-666-777', $address->getTelephone());
153 | $this->assertEquals('integer_net', $address->getCompany());
154 | $this->assertEquals('DE', $address->getCountryId());
155 | $this->assertEquals('52078', $address->getPostcode());
156 | $this->assertEquals('Aachen', $address->getCity());
157 | $this->assertEquals(88, $address->getRegionId());
158 | }
159 |
160 | /**
161 | * @throws LocalizedException
162 | */
163 | public function testLocalizedAddresses(): void
164 | {
165 | $customerFixture = new CustomerFixture(
166 | CustomerBuilder::aCustomer()->withAddresses(
167 | AddressBuilder::anAddress('de_DE')->asDefaultBilling(),
168 | AddressBuilder::anAddress('en_US')->asDefaultShipping()
169 | )->build()
170 | );
171 |
172 | foreach ($this->customerRepository->getById($customerFixture->getId())->getAddresses() as $address) {
173 | self::assertSame($address->isDefaultBilling() ? 'DE' : 'US', $address->getCountryId());
174 | }
175 | }
176 |
177 | /**
178 | * @throws LocalizedException
179 | */
180 | public function testCompanyAddress(): void
181 | {
182 | $vatId = '1112223334';
183 | $customerFixture = new CustomerFixture(
184 | CustomerBuilder::aCustomer()->withAddresses(
185 | AddressBuilder::aCompanyAddress('de_DE', $vatId)->asDefaultBilling()
186 | )->build()
187 | );
188 |
189 | $addresses = $this->customerRepository->getById($customerFixture->getId())->getAddresses();
190 | /** @var AddressInterface $firstAddress */
191 | $onlyAddress = reset($addresses);
192 | self::assertSame($onlyAddress->getVatId(), $vatId);
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/tests/Customer/CustomerFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | customerFixtures = new CustomerFixturePool();
30 | $this->customerRepository = Bootstrap::getObjectManager()->create(CustomerRepositoryInterface::class);
31 | }
32 |
33 | public function testLastCustomerFixtureReturnedByDefault()
34 | {
35 | $firstCustomer = $this->createCustomer();
36 | $lastCustomer = $this->createCustomer();
37 | $this->customerFixtures->add($firstCustomer);
38 | $this->customerFixtures->add($lastCustomer);
39 | $customerFixture = $this->customerFixtures->get();
40 | $this->assertEquals($lastCustomer->getId(), $customerFixture->getId());
41 | }
42 |
43 | public function testExceptionThrownWhenAccessingEmptyCustomerPool()
44 | {
45 | $this->expectException(\OutOfBoundsException::class);
46 | $this->customerFixtures->get();
47 | }
48 |
49 | public function testCustomerFixtureReturnedByKey()
50 | {
51 | $firstCustomer = $this->createCustomer();
52 | $lastCustomer = $this->createCustomer();
53 | $this->customerFixtures->add($firstCustomer, 'first');
54 | $this->customerFixtures->add($lastCustomer, 'last');
55 | $customerFixture = $this->customerFixtures->get('first');
56 | $this->assertEquals($firstCustomer->getId(), $customerFixture->getId());
57 | }
58 |
59 | public function testExceptionThrownWhenAccessingNonexistingKey()
60 | {
61 | $customer = $this->createCustomer();
62 | $this->customerFixtures->add($customer, 'foo');
63 | $this->expectException(\OutOfBoundsException::class);
64 | $this->customerFixtures->get('bar');
65 | }
66 |
67 | public function testRollbackRemovesCustomersFromPool()
68 | {
69 | $customer = $this->createCustomerInDb();
70 | $this->customerFixtures->add($customer);
71 | $this->customerFixtures->rollback();
72 | $this->expectException(\OutOfBoundsException::class);
73 | $this->customerFixtures->get();
74 | }
75 | public function testRollbackDeletesCustomersFromDb()
76 | {
77 | $customer = $this->createCustomerInDb();
78 | $this->customerFixtures->add($customer);
79 | $this->customerFixtures->rollback();
80 | $this->expectException(NoSuchEntityException::class);
81 | $this->customerRepository->get($customer->getId());
82 | }
83 |
84 | /**
85 | * Creates a dummy customer object
86 | *
87 | * @return CustomerInterface
88 | * @throws \Magento\Framework\Exception\LocalizedException
89 | */
90 | private function createCustomer(): CustomerInterface
91 | {
92 | static $nextId = 1;
93 | /** @var CustomerInterface $customer */
94 | $customer = Bootstrap::getObjectManager()->create(CustomerInterface::class);
95 | $customer->setId($nextId++);
96 | return $customer;
97 | }
98 |
99 | /**
100 | * Uses builder to create a customer
101 | *
102 | * @return CustomerInterface
103 | * @throws \Magento\Framework\Exception\LocalizedException
104 | */
105 | private function createCustomerInDb(): CustomerInterface
106 | {
107 | return CustomerBuilder::aCustomer()->build();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/tests/Customer/CustomerFixtureRollbackTest.php:
--------------------------------------------------------------------------------
1 | customerRepository = Bootstrap::getObjectManager()->create(CustomerRepositoryInterface::class);
24 | }
25 |
26 | public function testRollbackSingleCustomerFixture()
27 | {
28 | $customerFixture = new CustomerFixture(
29 | CustomerBuilder::aCustomer()->build()
30 | );
31 | CustomerFixtureRollback::create()->execute($customerFixture);
32 | $this->expectException(NoSuchEntityException::class);
33 | $this->customerRepository->getById($customerFixture->getId());
34 | }
35 |
36 | public function testRollbackMultipleCustomerFixtures()
37 | {
38 | $customerFixture = new CustomerFixture(
39 | CustomerBuilder::aCustomer()->build()
40 | );
41 | $otherCustomerFixture = new CustomerFixture(
42 | CustomerBuilder::aCustomer()->build()
43 | );
44 | CustomerFixtureRollback::create()->execute($customerFixture, $otherCustomerFixture);
45 | $customerDeleted = false;
46 | try {
47 | $this->customerRepository->getById($customerFixture->getId());
48 | } catch (NoSuchEntityException $e) {
49 | $customerDeleted = true;
50 | }
51 | $this->assertTrue($customerDeleted, 'First customer should be deleted');
52 | $this->expectException(NoSuchEntityException::class);
53 | $this->customerRepository->getById($otherCustomerFixture->getId());
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/Sales/CreditmemoBuilderTest.php:
--------------------------------------------------------------------------------
1 | creditmemoRepository = Bootstrap::getObjectManager()->create(CreditmemoRepositoryInterface::class);
35 | }
36 |
37 | /**
38 | * @throws LocalizedException
39 | */
40 | protected function tearDown(): void
41 | {
42 | OrderFixtureRollback::create()->execute($this->orderFixture);
43 |
44 | parent::tearDown();
45 | }
46 |
47 | /**
48 | * Create a credit memo for all the order's items.
49 | *
50 | * @test
51 | *
52 | * @throws \Exception
53 | */
54 | public function createCreditmemo()
55 | {
56 | $order = OrderBuilder::anOrder()->withPaymentMethod('checkmo')->build();
57 | $this->orderFixture = new OrderFixture($order);
58 |
59 | $refundFixture = new CreditmemoFixture(CreditmemoBuilder::forOrder($order)->build());
60 |
61 | self::assertInstanceOf(CreditmemoInterface::class, $this->creditmemoRepository->get($refundFixture->getId()));
62 | self::assertFalse($order->canCreditmemo());
63 | }
64 |
65 | /**
66 | * Create a credit memo for some of the order's items.
67 | *
68 | * @test
69 | * @throws \Exception
70 | */
71 | public function createPartialCreditmemos()
72 | {
73 | $order = OrderBuilder::anOrder()->withPaymentMethod('checkmo')->withProducts(
74 | ProductBuilder::aSimpleProduct()->withSku('foo'),
75 | ProductBuilder::aSimpleProduct()->withSku('bar')
76 | )->withCart(
77 | CartBuilder::forCurrentSession()
78 | ->withSimpleProduct('foo', 2)
79 | ->withSimpleProduct('bar', 3)
80 | )->build();
81 | $this->orderFixture = new OrderFixture($order);
82 |
83 | $orderItemIds = [];
84 | /** @var OrderItemInterface $orderItem */
85 | foreach ($order->getAllVisibleItems() as $orderItem) {
86 | $orderItemIds[$orderItem->getSku()] = $orderItem->getItemId();
87 | }
88 |
89 | $refundFixture = new CreditmemoFixture(
90 | CreditmemoBuilder::forOrder($order)
91 | ->withItem($orderItemIds['foo'], 2)
92 | ->withItem($orderItemIds['bar'], 2)
93 | ->build()
94 | );
95 |
96 | self::assertInstanceOf(CreditmemoInterface::class, $this->creditmemoRepository->get($refundFixture->getId()));
97 | self::assertTrue($order->canCreditmemo());
98 |
99 | $refundFixture = new CreditmemoFixture(
100 | CreditmemoBuilder::forOrder($order)
101 | ->withItem($orderItemIds['bar'], 1)
102 | ->build()
103 | );
104 |
105 | self::assertInstanceOf(CreditmemoInterface::class, $this->creditmemoRepository->get($refundFixture->getId()));
106 | self::assertFalse($order->canCreditmemo());
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/tests/Sales/CreditmemoFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | creditmemoFixtures = new CreditmemoFixturePool();
29 | $this->creditmemoRepository = Bootstrap::getObjectManager()->create(CreditmemoRepositoryInterface::class);
30 | }
31 |
32 | public function testLastCreditmemoFixtureReturnedByDefault()
33 | {
34 | $firstCreditmemo = $this->createCreditmemo();
35 | $lastCreditmemo = $this->createCreditmemo();
36 | $this->creditmemoFixtures->add($firstCreditmemo);
37 | $this->creditmemoFixtures->add($lastCreditmemo);
38 | $creditmemoFixture = $this->creditmemoFixtures->get();
39 | $this->assertEquals($lastCreditmemo->getEntityId(), $creditmemoFixture->getId());
40 | }
41 |
42 | public function testExceptionThrownWhenAccessingEmptyCreditmemoPool()
43 | {
44 | $this->expectException(\OutOfBoundsException::class);
45 | $this->creditmemoFixtures->get();
46 | }
47 |
48 | public function testCreditmemoFixtureReturnedByKey()
49 | {
50 | $firstCreditmemo = $this->createCreditmemo();
51 | $lastCreditmemo = $this->createCreditmemo();
52 | $this->creditmemoFixtures->add($firstCreditmemo, 'first');
53 | $this->creditmemoFixtures->add($lastCreditmemo, 'last');
54 | $creditmemoFixture = $this->creditmemoFixtures->get('first');
55 | $this->assertEquals($firstCreditmemo->getEntityId(), $creditmemoFixture->getId());
56 | }
57 |
58 | public function testExceptionThrownWhenAccessingNonexistingKey()
59 | {
60 | $creditmemo = $this->createCreditmemo();
61 | $this->creditmemoFixtures->add($creditmemo, 'foo');
62 | $this->expectException(\OutOfBoundsException::class);
63 | $this->creditmemoFixtures->get('bar');
64 | }
65 |
66 | /**
67 | * @return CreditmemoInterface
68 | * @throws \Exception
69 | */
70 | private function createCreditmemo(): CreditmemoInterface
71 | {
72 | static $nextId = 1;
73 | /** @var CreditmemoInterface $creditmemo */
74 | $creditmemo = Bootstrap::getObjectManager()->create(CreditmemoInterface::class);
75 | $creditmemo->setEntityId($nextId++);
76 | return $creditmemo;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tests/Sales/InvoiceBuilderTest.php:
--------------------------------------------------------------------------------
1 | invoiceRepository = Bootstrap::getObjectManager()->create(InvoiceRepositoryInterface::class);
35 | }
36 |
37 | /**
38 | * @throws LocalizedException
39 | */
40 | protected function tearDown(): void
41 | {
42 | OrderFixtureRollback::create()->execute($this->orderFixture);
43 |
44 | parent::tearDown();
45 | }
46 |
47 | /**
48 | * Create a invoice for all the order's items.
49 | *
50 | * @test
51 | *
52 | * @throws \Exception
53 | */
54 | public function createInvoice()
55 | {
56 | $order = OrderBuilder::anOrder()->build();
57 | $this->orderFixture = new OrderFixture($order);
58 |
59 | $invoiceFixture = new InvoiceFixture(InvoiceBuilder::forOrder($order)->build());
60 |
61 | self::assertInstanceOf(InvoiceInterface::class, $this->invoiceRepository->get($invoiceFixture->getId()));
62 | self::assertFalse($order->canInvoice());
63 | }
64 |
65 | /**
66 | * Create an invoice for some of the order's items.
67 | *
68 | * @test
69 | * @throws \Exception
70 | */
71 | public function createPartialInvoices()
72 | {
73 | $order = OrderBuilder::anOrder()->withProducts(
74 | ProductBuilder::aSimpleProduct()->withSku('foo'),
75 | ProductBuilder::aSimpleProduct()->withSku('bar')
76 | )->withCart(
77 | CartBuilder::forCurrentSession()
78 | ->withSimpleProduct('foo', 2)
79 | ->withSimpleProduct('bar', 3)
80 | )->build();
81 | $this->orderFixture = new OrderFixture($order);
82 |
83 | $orderItemIds = [];
84 | /** @var OrderItemInterface $orderItem */
85 | foreach ($order->getAllVisibleItems() as $orderItem) {
86 | $orderItemIds[$orderItem->getSku()] = $orderItem->getItemId();
87 | }
88 |
89 | $invoiceFixture = new InvoiceFixture(
90 | InvoiceBuilder::forOrder($order)
91 | ->withItem($orderItemIds['foo'], 2)
92 | ->withItem($orderItemIds['bar'], 2)
93 | ->build()
94 | );
95 |
96 | self::assertInstanceOf(InvoiceInterface::class, $this->invoiceRepository->get($invoiceFixture->getId()));
97 | self::assertTrue($order->canInvoice());
98 |
99 | $invoiceFixture = new InvoiceFixture(
100 | InvoiceBuilder::forOrder($order)
101 | ->withItem($orderItemIds['bar'], 1)
102 | ->build()
103 | );
104 |
105 | self::assertInstanceOf(InvoiceInterface::class, $this->invoiceRepository->get($invoiceFixture->getId()));
106 | self::assertFalse($order->canInvoice());
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/tests/Sales/InvoiceFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | invoiceFixtures = new InvoiceFixturePool();
29 | $this->invoiceRepository = Bootstrap::getObjectManager()->create(InvoiceRepositoryInterface::class);
30 | }
31 |
32 | public function testLastInvoiceFixtureReturnedByDefault()
33 | {
34 | $firstInvoice = $this->createInvoice();
35 | $lastInvoice = $this->createInvoice();
36 | $this->invoiceFixtures->add($firstInvoice);
37 | $this->invoiceFixtures->add($lastInvoice);
38 | $invoiceFixture = $this->invoiceFixtures->get();
39 | $this->assertEquals($lastInvoice->getEntityId(), $invoiceFixture->getId());
40 | }
41 |
42 | public function testExceptionThrownWhenAccessingEmptyInvoicePool()
43 | {
44 | $this->expectException(\OutOfBoundsException::class);
45 | $this->invoiceFixtures->get();
46 | }
47 |
48 | public function testInvoiceFixtureReturnedByKey()
49 | {
50 | $firstInvoice = $this->createInvoice();
51 | $lastInvoice = $this->createInvoice();
52 | $this->invoiceFixtures->add($firstInvoice, 'first');
53 | $this->invoiceFixtures->add($lastInvoice, 'last');
54 | $invoiceFixture = $this->invoiceFixtures->get('first');
55 | $this->assertEquals($firstInvoice->getEntityId(), $invoiceFixture->getId());
56 | }
57 |
58 | public function testExceptionThrownWhenAccessingNonexistingKey()
59 | {
60 | $invoice = $this->createInvoice();
61 | $this->invoiceFixtures->add($invoice, 'foo');
62 | $this->expectException(\OutOfBoundsException::class);
63 | $this->invoiceFixtures->get('bar');
64 | }
65 |
66 | /**
67 | * @return InvoiceInterface
68 | * @throws \Exception
69 | */
70 | private function createInvoice(): InvoiceInterface
71 | {
72 | static $nextId = 1;
73 | /** @var InvoiceInterface $invoice */
74 | $invoice = Bootstrap::getObjectManager()->create(InvoiceInterface::class);
75 | $invoice->setEntityId($nextId++);
76 | return $invoice;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tests/Sales/OrderBuilderTest.php:
--------------------------------------------------------------------------------
1 | orderRepository = Bootstrap::getObjectManager()->create(OrderRepositoryInterface::class);
36 | }
37 |
38 | /**
39 | * @throws LocalizedException
40 | */
41 | protected function tearDown(): void
42 | {
43 | OrderFixtureRollback::create()->execute(...$this->orderFixtures);
44 |
45 | parent::tearDown();
46 | }
47 |
48 | /**
49 | * Create an order for an internally generated customer and internally generated product(s).
50 | *
51 | * Easy to set up, least flexible.
52 | *
53 | * @test
54 | * @throws \Exception
55 | */
56 | public function createOrder()
57 | {
58 | $orderFixture = new OrderFixture(
59 | OrderBuilder::anOrder()->build()
60 | );
61 | $this->orderFixtures[] = $orderFixture;
62 |
63 | self::assertInstanceOf(OrderInterface::class, $this->orderRepository->get($orderFixture->getId()));
64 | self::assertNotEmpty($orderFixture->getOrderItemQtys());
65 | }
66 |
67 | /**
68 | * Create an order for an internally generated customer.
69 | *
70 | * Control the product included with the order, use random item quantities.
71 | *
72 | * @test
73 | * @throws \Exception
74 | */
75 | public function createOrderWithProduct()
76 | {
77 | $orderFixture = new OrderFixture(
78 | OrderBuilder::anOrder()->withProducts(ProductBuilder::aSimpleProduct())->build()
79 | );
80 | $this->orderFixtures[] = $orderFixture;
81 |
82 | self::assertInstanceOf(OrderInterface::class, $this->orderRepository->get($orderFixture->getId()));
83 | self::assertCount(1, $orderFixture->getOrderItemQtys());
84 | }
85 |
86 | /**
87 | * Create an order for an internally generated customer with multiple products.
88 | *
89 | * Control the products included with the order, use random item quantities.
90 | *
91 | * @test
92 | * @throws \Exception
93 | */
94 | public function createOrderWithProducts()
95 | {
96 | $orderFixture = new OrderFixture(
97 | OrderBuilder::anOrder()->withProducts(
98 | ProductBuilder::aSimpleProduct()->withSku('foo'),
99 | ProductBuilder::aSimpleProduct()->withSku('bar')
100 | )->build()
101 | );
102 | $this->orderFixtures[] = $orderFixture;
103 |
104 | self::assertInstanceOf(OrderInterface::class, $this->orderRepository->get($orderFixture->getId()));
105 | self::assertCount(2, $orderFixture->getOrderItemQtys());
106 | }
107 |
108 | /**
109 | * Create an order for a given customer with internally generated product(s).
110 | *
111 | * Control the customer placing the order.
112 | *
113 | * @test
114 | * @throws \Exception
115 | */
116 | public function createOrderWithCustomer()
117 | {
118 | $customerEmail = 'test@example.com';
119 | $customerBuilder = CustomerBuilder::aCustomer()
120 | ->withEmail($customerEmail)
121 | ->withAddresses(AddressBuilder::anAddress()->asDefaultBilling()->asDefaultShipping());
122 |
123 | $orderFixture = new OrderFixture(
124 | OrderBuilder::anOrder()->withCustomer($customerBuilder)->build()
125 | );
126 | $this->orderFixtures[] = $orderFixture;
127 |
128 | self::assertInstanceOf(OrderInterface::class, $this->orderRepository->get($orderFixture->getId()));
129 | self::assertSame($customerEmail, $orderFixture->getCustomerEmail());
130 | self::assertNotEmpty($orderFixture->getOrderItemQtys());
131 | }
132 |
133 | /**
134 | * Create an order for a given cart.
135 | *
136 | * Complex to set up, most flexible:
137 | * - define products
138 | * - define customer
139 | * - set item quantities
140 | * - set payment and shipping method
141 | *
142 | * @test
143 | * @throws \Exception
144 | */
145 | public function createOrderWithCart()
146 | {
147 | $cartItems = ['foo' => 2, 'bar' => 3];
148 | $customerEmail = 'test@example.com';
149 | $paymentMethod = 'checkmo';
150 | $shippingMethod = 'flatrate_flatrate';
151 |
152 | $productBuilders = [];
153 | foreach ($cartItems as $sku => $qty) {
154 | $productBuilders[] = ProductBuilder::aSimpleProduct()->withSku($sku);
155 | }
156 |
157 | $customerBuilder = CustomerBuilder::aCustomer();
158 | $customerBuilder = $customerBuilder
159 | ->withEmail($customerEmail)
160 | ->withAddresses(AddressBuilder::anAddress()->asDefaultBilling()->asDefaultShipping());
161 |
162 | $cartBuilder = CartBuilder::forCurrentSession();
163 | foreach ($cartItems as $sku => $qty) {
164 | $cartBuilder = $cartBuilder->withSimpleProduct($sku, $qty);
165 | }
166 |
167 | $orderFixture = new OrderFixture(
168 | OrderBuilder::anOrder()
169 | ->withProducts(...$productBuilders)
170 | ->withCustomer($customerBuilder)
171 | ->withCart($cartBuilder)
172 | ->withPaymentMethod($paymentMethod)->withShippingMethod($shippingMethod)
173 | ->build()
174 | );
175 | $this->orderFixtures[] = $orderFixture;
176 |
177 | self::assertInstanceOf(OrderInterface::class, $this->orderRepository->get($orderFixture->getId()));
178 | self::assertSame($customerEmail, $orderFixture->getCustomerEmail());
179 | self::assertEmpty(array_diff($cartItems, $orderFixture->getOrderItemQtys()));
180 | self::assertSame($paymentMethod, $orderFixture->getPaymentMethod());
181 | self::assertSame($shippingMethod, $orderFixture->getShippingMethod());
182 | }
183 |
184 | /**
185 | * Create multiple orders. Assert all of them were successfully built.
186 | *
187 | * @test
188 | * @throws \Exception
189 | */
190 | public function createMultipleOrders()
191 | {
192 | $shippingMethod = 'flatrate_flatrate';
193 |
194 | // first order, simple
195 | $orderFixture = new OrderFixture(
196 | OrderBuilder::anOrder()
197 | ->withShippingMethod($shippingMethod)
198 | ->build()
199 | );
200 | $this->orderFixtures[] = $orderFixture;
201 |
202 | // second order, with specified cart
203 | $cartBuilder = CartBuilder::forCurrentSession();
204 | $orderWithCartFixture = new OrderFixture(
205 | OrderBuilder::anOrder()
206 | ->withShippingMethod($shippingMethod)
207 | ->withProducts(ProductBuilder::aSimpleProduct()->withSku('bar'))
208 | ->withCart($cartBuilder->withSimpleProduct('bar', 3))
209 | ->build()
210 | );
211 | $this->orderFixtures[] = $orderWithCartFixture;
212 |
213 | // third order, with specified customer
214 | $orderWithCustomerFixture = new OrderFixture(
215 | OrderBuilder::anOrder()
216 | ->withShippingMethod($shippingMethod)
217 | ->withCustomer(
218 | CustomerBuilder::aCustomer()
219 | ->withAddresses(
220 | AddressBuilder::anAddress('de_AT')
221 | ->asDefaultBilling()
222 | ->asDefaultShipping()
223 | )
224 | )
225 | ->build()
226 | );
227 | $this->orderFixtures[] = $orderWithCustomerFixture;
228 |
229 | // assert all fixtures were created with separate customers.
230 | self::assertCount(3, $this->orderFixtures);
231 | self::assertContainsOnlyInstancesOf(OrderFixture::class, $this->orderFixtures);
232 |
233 | $customerIds[$orderFixture->getCustomerId()] = 1;
234 | $customerIds[$orderWithCartFixture->getCustomerId()] = 1;
235 | $customerIds[$orderWithCustomerFixture->getCustomerId()] = 1;
236 | self::assertCount(3, $customerIds);
237 | }
238 |
239 | /**
240 | * Create orders for faker addresses with either state or province. Assert both types have a `region_id` assigned.
241 | *
242 | * @test
243 | * @throws \Exception
244 | */
245 | public function createIntlOrders()
246 | {
247 | $atLocale = 'de_AT';
248 | $atOrder = OrderBuilder::anOrder()
249 | ->withCustomer(
250 | CustomerBuilder::aCustomer()->withAddresses(
251 | AddressBuilder::anAddress($atLocale)->asDefaultBilling()->asDefaultShipping()
252 | )
253 | )
254 | ->build();
255 | $this->orderFixtures[] = new OrderFixture($atOrder);
256 |
257 | $usLocale = 'en_US';
258 | $usOrder = OrderBuilder::anOrder()
259 | ->withCustomer(
260 | CustomerBuilder::aCustomer()->withAddresses(
261 | AddressBuilder::anAddress($usLocale)->asDefaultBilling()->asDefaultShipping()
262 | )
263 | )
264 | ->build();
265 | $this->orderFixtures[] = new OrderFixture($usOrder);
266 |
267 | $caLocale = 'en_CA';
268 | $caOrder = OrderBuilder::anOrder()
269 | ->withCustomer(
270 | CustomerBuilder::aCustomer()->withAddresses(
271 | AddressBuilder::anAddress($caLocale)->asDefaultBilling()->asDefaultShipping()
272 | )
273 | )
274 | ->build();
275 | $this->orderFixtures[] = new OrderFixture($caOrder);
276 |
277 | self::assertSame(substr($atLocale, 3, 4), $atOrder->getBillingAddress()->getCountryId());
278 | self::assertNotEmpty($atOrder->getBillingAddress()->getRegionId());
279 | self::assertSame(substr($usLocale, 3, 4), $usOrder->getBillingAddress()->getCountryId());
280 | self::assertNotEmpty($usOrder->getBillingAddress()->getRegionId());
281 | self::assertSame(substr($caLocale, 3, 4), $caOrder->getBillingAddress()->getCountryId());
282 | self::assertNotEmpty($caOrder->getBillingAddress()->getRegionId());
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/tests/Sales/OrderFixturePoolRollbackTest.php:
--------------------------------------------------------------------------------
1 | build();
33 | }
34 |
35 | protected function setUp(): void
36 | {
37 | $this->orderFixtures = new OrderFixturePool();
38 | $this->orderRepository = Bootstrap::getObjectManager()->create(OrderRepositoryInterface::class);
39 | }
40 |
41 | public function testRollbackRemovesOrdersFromPool()
42 | {
43 | $this->orderFixtures->add(self::$order);
44 | $this->orderFixtures->rollback();
45 | $this->expectException(\OutOfBoundsException::class);
46 | $this->orderFixtures->get();
47 | }
48 |
49 | public function testRollbackWorksWithKeys()
50 | {
51 | $this->orderFixtures->add(self::$order, 'key');
52 | $this->orderFixtures->rollback();
53 | $this->expectException(\OutOfBoundsException::class);
54 | $this->orderFixtures->get();
55 | }
56 |
57 | public function testRollbackDeletesOrdersFromDb()
58 | {
59 | $this->orderFixtures->add(self::$order);
60 | $this->orderFixtures->rollback();
61 | $this->expectException(NoSuchEntityException::class);
62 | $this->orderRepository->get(self::$order->getId());
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/Sales/OrderFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | orderFixtures = new OrderFixturePool();
24 | }
25 |
26 | public function testLastOrderFixtureReturnedByDefault()
27 | {
28 | $firstOrder = $this->createOrder();
29 | $lastOrder = $this->createOrder();
30 | $this->orderFixtures->add($firstOrder);
31 | $this->orderFixtures->add($lastOrder);
32 | $orderFixture = $this->orderFixtures->get();
33 | $this->assertEquals($lastOrder->getId(), $orderFixture->getId());
34 | }
35 |
36 | public function testExceptionThrownWhenAccessingEmptyOrderPool()
37 | {
38 | $this->expectException(\OutOfBoundsException::class);
39 | $this->orderFixtures->get();
40 | }
41 |
42 | public function testOrderFixtureReturnedByKey()
43 | {
44 | $firstOrder = $this->createOrder();
45 | $lastOrder = $this->createOrder();
46 | $this->orderFixtures->add($firstOrder, 'first');
47 | $this->orderFixtures->add($lastOrder, 'last');
48 | $orderFixture = $this->orderFixtures->get('first');
49 | $this->assertEquals($firstOrder->getId(), $orderFixture->getId());
50 | }
51 |
52 | public function testExceptionThrownWhenAccessingNonexistingKey()
53 | {
54 | $order = $this->createOrder();
55 | $this->orderFixtures->add($order, 'foo');
56 | $this->expectException(\OutOfBoundsException::class);
57 | $this->orderFixtures->get('bar');
58 | }
59 |
60 | /**
61 | * @return Order
62 | * @throws \Exception
63 | */
64 | private function createOrder(): Order
65 | {
66 | static $nextId = 1;
67 | /** @var Order $order */
68 | $order = Bootstrap::getObjectManager()->create(Order::class);
69 | $order->setId($nextId++);
70 | return $order;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tests/Sales/ShipmentBuilderTest.php:
--------------------------------------------------------------------------------
1 | shipmentRepository = Bootstrap::getObjectManager()->create(ShipmentRepositoryInterface::class);
36 | }
37 |
38 | /**
39 | * @throws LocalizedException
40 | */
41 | protected function tearDown(): void
42 | {
43 | OrderFixtureRollback::create()->execute($this->orderFixture);
44 |
45 | parent::tearDown();
46 | }
47 |
48 | /**
49 | * Create a shipment for all the order's items.
50 | *
51 | * @test
52 | *
53 | * @throws \Exception
54 | */
55 | public function createShipment()
56 | {
57 | $order = OrderBuilder::anOrder()->build();
58 | $this->orderFixture = new OrderFixture($order);
59 |
60 | $shipmentFixture = new ShipmentFixture(ShipmentBuilder::forOrder($order)->build());
61 |
62 | self::assertInstanceOf(ShipmentInterface::class, $this->shipmentRepository->get($shipmentFixture->getId()));
63 | self::assertEmpty($shipmentFixture->getShippingLabel());
64 | self::assertEmpty($shipmentFixture->getTracks());
65 | self::assertFalse($order->canShip());
66 | }
67 |
68 | /**
69 | * Create a shipment for all the order's items with tracks and shipping label.
70 | *
71 | * @test
72 | *
73 | * @throws \Exception
74 | */
75 | public function createShipmentWithTracks()
76 | {
77 | $order = OrderBuilder::anOrder()->build();
78 | $this->orderFixture = new OrderFixture($order);
79 |
80 | $shipmentFixture = new ShipmentFixture(
81 | ShipmentBuilder::forOrder($order)->withTrackingNumbers('123456', '987654', 'abcdef')->build()
82 | );
83 |
84 | self::assertInstanceOf(ShipmentInterface::class, $this->shipmentRepository->get($shipmentFixture->getId()));
85 |
86 | self::assertNotEmpty($shipmentFixture->getShippingLabel());
87 | self::assertNotEmpty($shipmentFixture->getTracks());
88 | self::assertContainsOnlyInstancesOf(ShipmentTrackInterface::class, $shipmentFixture->getTracks());
89 | self::assertCount(3, $shipmentFixture->getTracks());
90 | self::assertFalse($order->canShip());
91 | }
92 |
93 | /**
94 | * Create a shipment for some of the order's items.
95 | *
96 | * @test
97 | * @throws \Exception
98 | */
99 | public function createPartialShipments()
100 | {
101 | $order = OrderBuilder::anOrder()->withProducts(
102 | ProductBuilder::aSimpleProduct()->withSku('foo'),
103 | ProductBuilder::aSimpleProduct()->withSku('bar')
104 | )->withCart(
105 | CartBuilder::forCurrentSession()
106 | ->withSimpleProduct('foo', 2)
107 | ->withSimpleProduct('bar', 3)
108 | )->build();
109 | $this->orderFixture = new OrderFixture($order);
110 |
111 | $orderItemIds = [];
112 | /** @var OrderItemInterface $orderItem */
113 | foreach ($order->getAllVisibleItems() as $orderItem) {
114 | $orderItemIds[$orderItem->getSku()] = $orderItem->getItemId();
115 | }
116 |
117 | $shipmentFixture = new ShipmentFixture(
118 | ShipmentBuilder::forOrder($order)
119 | ->withItem($orderItemIds['foo'], 2)
120 | ->withItem($orderItemIds['bar'], 2)
121 | ->build()
122 | );
123 |
124 | self::assertInstanceOf(ShipmentInterface::class, $this->shipmentRepository->get($shipmentFixture->getId()));
125 | self::assertTrue($order->canShip());
126 |
127 | $shipmentFixture = new ShipmentFixture(
128 | ShipmentBuilder::forOrder($order)
129 | ->withItem($orderItemIds['bar'], 1)
130 | ->build()
131 | );
132 |
133 | self::assertInstanceOf(ShipmentInterface::class, $this->shipmentRepository->get($shipmentFixture->getId()));
134 | self::assertFalse($order->canShip());
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/tests/Sales/ShipmentFixturePoolTest.php:
--------------------------------------------------------------------------------
1 | shipmentFixtures = new ShipmentFixturePool();
29 | $this->shipmentRepository = Bootstrap::getObjectManager()->create(ShipmentRepositoryInterface::class);
30 | }
31 |
32 | public function testLastShipmentFixtureReturnedByDefault()
33 | {
34 | $firstShipment = $this->createShipment();
35 | $lastShipment = $this->createShipment();
36 | $this->shipmentFixtures->add($firstShipment);
37 | $this->shipmentFixtures->add($lastShipment);
38 | $shipmentFixture = $this->shipmentFixtures->get();
39 | $this->assertEquals($lastShipment->getEntityId(), $shipmentFixture->getId());
40 | }
41 |
42 | public function testExceptionThrownWhenAccessingEmptyShipmentPool()
43 | {
44 | $this->expectException(\OutOfBoundsException::class);
45 | $this->shipmentFixtures->get();
46 | }
47 |
48 | public function testShipmentFixtureReturnedByKey()
49 | {
50 | $firstShipment = $this->createShipment();
51 | $lastShipment = $this->createShipment();
52 | $this->shipmentFixtures->add($firstShipment, 'first');
53 | $this->shipmentFixtures->add($lastShipment, 'last');
54 | $shipmentFixture = $this->shipmentFixtures->get('first');
55 | $this->assertEquals($firstShipment->getEntityId(), $shipmentFixture->getId());
56 | }
57 |
58 | public function testExceptionThrownWhenAccessingNonexistingKey()
59 | {
60 | $shipment = $this->createShipment();
61 | $this->shipmentFixtures->add($shipment, 'foo');
62 | $this->expectException(\OutOfBoundsException::class);
63 | $this->shipmentFixtures->get('bar');
64 | }
65 |
66 | /**
67 | * @return ShipmentInterface
68 | * @throws \Exception
69 | */
70 | private function createShipment(): ShipmentInterface
71 | {
72 | static $nextId = 1;
73 | /** @var ShipmentInterface $shipment */
74 | $shipment = Bootstrap::getObjectManager()->create(ShipmentInterface::class);
75 | $shipment->setEntityId($nextId++);
76 | return $shipment;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tests/Theme/ThemeFixtureTest.php:
--------------------------------------------------------------------------------
1 | get(DesignInterface::class);
22 | $this->assertEquals('Magento/blank', $design->getDesignTheme()->getCode());
23 | }
24 |
25 | public function testCanUseTestThemeAfterRegistering()
26 | {
27 | ThemeFixture::registerTestThemes();
28 | ThemeFixture::setCurrentTheme('Custom/default');
29 | /** @var DesignInterface $design */
30 | $design = Bootstrap::getObjectManager()->get(DesignInterface::class);
31 | $this->assertEquals('Custom/default', $design->getDesignTheme()->getCode());
32 | $this->assertGreaterThan(0, $design->getDesignTheme()->getId());
33 | $this->assertEquals('Magento/blank', $design->getDesignTheme()->getParentTheme()->getCode());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Theme/_files/design/frontend/Custom/default/registration.php:
--------------------------------------------------------------------------------
1 |
9 |
10 | Test theme
11 | Magento/blank
12 |
13 |
--------------------------------------------------------------------------------
/tests/install-config-mysql.php:
--------------------------------------------------------------------------------
1 | 'DB_HOST',
5 | 'db-user' => 'DB_USER',
6 | 'db-password' => 'DB_PASSWORD',
7 | 'db-name' => 'DB_NAME',
8 | 'db-prefix' => '',
9 | 'backend-frontname' => 'backend',
10 | 'admin-user' => \Magento\TestFramework\Bootstrap::ADMIN_NAME,
11 | 'admin-password' => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD,
12 | 'admin-email' => \Magento\TestFramework\Bootstrap::ADMIN_EMAIL,
13 | 'admin-firstname' => \Magento\TestFramework\Bootstrap::ADMIN_FIRSTNAME,
14 | 'admin-lastname' => \Magento\TestFramework\Bootstrap::ADMIN_LASTNAME,
15 | 'amqp-host' => '',
16 | 'amqp-port' => '',
17 | 'amqp-user' => '',
18 | 'amqp-password' => '',
19 | ];
20 |
--------------------------------------------------------------------------------
/tests/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
7 |
15 |
16 |
17 | ../../../vendor/tddwizard/magento2-fixtures/tests
18 |
19 |
20 |
21 |
22 |
23 | ../../../vendor/tddwizard/magento2-fixtures/src
24 |
25 |
26 |
27 |
28 | .
29 | testsuite
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------