mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
Merge branch '2.0' into api-platform-3
* 2.0: (69 commits) Add a conflict for "doctrine/doctrine-bundle": "2.11.0" Disable check for security vulnerabilites step [CS][DX] Refactor [API] Adjust contract tests after changes with translations [API] Disallow removing the translation in default locale [API] Improve contract test of creating a product with invalid translation locale [Maintenance] Fix other possibly invalid route attribute getting [Maintenance] Fix AdminFilterSubscriber failing when no _route attribute present [ApiBundle] Fix ProductAssociationTypesTest [ApiBundle] Remove unnecessary method in TranslatableLocaleKeyDenormalizer [ApiBundle] Translation key as locale [ApiBundle] Fix type-hint [PromotionBundle] Remove unnecessary constraint [AttributeBundle] Standardize locale validation translations Standardize locale validation translations [ApiBundle] Improve readability [ApiBundle] Add contract test to check validation of locale [TaxonomyBundle] Add validation on locale of translation [ShippingBundle] Add validation on locale of translation [PromotionBundle] Add validation on locale of translation ...
This commit is contained in:
commit
398a14b12f
226 changed files with 4553 additions and 699 deletions
1
.github/workflows/ci_static-checks.yaml
vendored
1
.github/workflows/ci_static-checks.yaml
vendored
|
|
@ -103,6 +103,7 @@ jobs:
|
|||
|
||||
- name: "Check for security vulnerabilities"
|
||||
run: symfony security:check
|
||||
continue-on-error: true
|
||||
|
||||
- name: "Validate Twig templates"
|
||||
run: bin/console lint:twig src
|
||||
|
|
|
|||
|
|
@ -27,3 +27,7 @@ references related issues.
|
|||
|
||||
- https://github.com/stof/StofDoctrineExtensionsBundle/issues/455
|
||||
- https://github.com/doctrine-extensions/DoctrineExtensions/issues/2600
|
||||
|
||||
- `doctrine/doctrine-bundle:2.11.0`:
|
||||
|
||||
This version uses the [readonly property](https://github.com/doctrine/DoctrineBundle/blob/2.11.0/Repository/ServiceEntityRepositoryProxy.php#L34), which is available from PHP 8.1 while the package also supports PHP 7.4 and 8.0, leading to compatibility issues.
|
||||
|
|
|
|||
111
UPGRADE-2.0.md
111
UPGRADE-2.0.md
|
|
@ -3,7 +3,7 @@
|
|||
## Codebase
|
||||
|
||||
* Doctrine MongoDB and PHPCR is not longer supported in ResourceBundle and GridBundle:
|
||||
|
||||
|
||||
* The following classes were removed:
|
||||
|
||||
* `Sylius\Bundle\GridBundle\Doctrine\PHPCRODM\DataSource`
|
||||
|
|
@ -26,13 +26,13 @@
|
|||
* `Sylius\Bundle\ResourceBundle\EventListener\ODMTranslatableListener`
|
||||
|
||||
* The following services were removed:
|
||||
|
||||
|
||||
* `sylius.event_subscriber.odm_mapped_super_class`
|
||||
* `sylius.event_subscriber.odm_repository_class`
|
||||
* `sylius.grid_driver.doctrine.phpcrodm`
|
||||
|
||||
|
||||
* The following parameters were removed:
|
||||
|
||||
|
||||
* `sylius.mongodb_odm.repository.class`
|
||||
* `sylius.phpcr_odm.repository.class`
|
||||
|
||||
|
|
@ -40,28 +40,33 @@
|
|||
|
||||
* `sylius.mailer.templates`
|
||||
|
||||
* Added the `Sylius\Component\Order\Context\ResettableCartContextInterface` that extends `Sylius\Component\Order\Context\CartContextInterface` and `Symfony\Contracts\Service\ResetInterface`.
|
||||
* Added the `Sylius\Component\Order\Context\ResettableCartContextInterface` that
|
||||
extends `Sylius\Component\Order\Context\CartContextInterface` and `Symfony\Contracts\Service\ResetInterface`.
|
||||
|
||||
* The name of the default `LiipImagineBundle`'s resolver and loader were changed from **default** to **sylius_image** ([reference](https://github.com/Sylius/Sylius/pull/12543)).
|
||||
To change the default resolver and/or loader for `LiipImagineBundle`, configure `cache` and/or `data_loader` parameters under the `liip_imagine` key.
|
||||
* The name of the default `LiipImagineBundle`'s resolver and loader were changed from **default** to **sylius_image
|
||||
** ([reference](https://github.com/Sylius/Sylius/pull/12543)).
|
||||
To change the default resolver and/or loader for `LiipImagineBundle`, configure `cache` and/or `data_loader`
|
||||
parameters under the `liip_imagine` key.
|
||||
|
||||
* The `sylius/calendar` package has been replaced with `symfony/clock` package. All usages of the `Sylius\Calendar\Provider\DateTimeProviderInterface` class
|
||||
have been replaced with `Symfony\Component\Clock\ClockInterface` class.
|
||||
* The `sylius/calendar` package has been replaced with `symfony/clock` package. All usages of
|
||||
the `Sylius\Calendar\Provider\DateTimeProviderInterface` class
|
||||
have been replaced with `Symfony\Component\Clock\ClockInterface` class.
|
||||
|
||||
* The following classes were changed:
|
||||
* The following classes were changed:
|
||||
|
||||
* `Sylius\Bundle\CoreBundle\CatalogPromotion\Announcer\CatalogPromotionAnnouncer`
|
||||
* `Sylius\Bundle\CoreBundle\MessageHandler\Admin\Account\RequestResetPasswordEmailHandler`
|
||||
* `Sylius\Bundle\CoreBundle\PriceHistory\Logger\PriceChangeLogger`
|
||||
* `Sylius\Bundle\CoreBundle\PriceHistory\Remover\ChannelPricingLogEntriesRemover`
|
||||
* `Sylius\Bundle\ShippingBundle\Assigner\ShippingDateAssigner`
|
||||
* `Sylius\Bundle\PromotionBundle\Criteria\DateRange`
|
||||
* `Sylius\Bundle\ApiBundle\Applicator\ArchivingShippingMethodApplicator`
|
||||
* `Sylius\Bundle\ApiBundle\CommandHandler\Account\RequestResetPasswordTokenHandler`
|
||||
* `Sylius\Bundle\ApiBundle\CommandHandler\Account\VerifyCustomerAccountHandler`
|
||||
* `Sylius\Component\Taxation\Checker\TaxRateDateEligibilityChecker`
|
||||
* `Sylius\Bundle\CoreBundle\CatalogPromotion\Announcer\CatalogPromotionAnnouncer`
|
||||
* `Sylius\Bundle\CoreBundle\MessageHandler\Admin\Account\RequestResetPasswordEmailHandler`
|
||||
* `Sylius\Bundle\CoreBundle\PriceHistory\Logger\PriceChangeLogger`
|
||||
* `Sylius\Bundle\CoreBundle\PriceHistory\Remover\ChannelPricingLogEntriesRemover`
|
||||
* `Sylius\Bundle\ShippingBundle\Assigner\ShippingDateAssigner`
|
||||
* `Sylius\Bundle\PromotionBundle\Criteria\DateRange`
|
||||
* `Sylius\Bundle\ApiBundle\Applicator\ArchivingShippingMethodApplicator`
|
||||
* `Sylius\Bundle\ApiBundle\CommandHandler\Account\RequestResetPasswordTokenHandler`
|
||||
* `Sylius\Bundle\ApiBundle\CommandHandler\Account\VerifyCustomerAccountHandler`
|
||||
* `Sylius\Component\Taxation\Checker\TaxRateDateEligibilityChecker`
|
||||
|
||||
* The parameter order of `Sylius\Bundle\CoreBundle\Form\Type\Checkout\AddressType::__construct` has been changed:
|
||||
|
||||
```php
|
||||
public function __construct(
|
||||
+ private readonly AddressComparatorInterface $addressComparator,
|
||||
|
|
@ -73,7 +78,9 @@
|
|||
|
||||
* The `\Serializable` interface has been removed from the `Sylius\Component\User\Model\UserInterface`.
|
||||
|
||||
* The parameter order of the `Sylius\Component\Core\OrderProcessing\OrderPaymentProcessor::__construct` has been changed:
|
||||
* The parameter order of the `Sylius\Component\Core\OrderProcessing\OrderPaymentProcessor::__construct` has been
|
||||
changed:
|
||||
|
||||
```php
|
||||
public function __construct(
|
||||
private OrderPaymentProviderInterface $orderPaymentProvider,
|
||||
|
|
@ -112,7 +119,67 @@
|
|||
): void;
|
||||
```
|
||||
|
||||
* The `swiftmailer/swiftmailer` dependency has been removed. Use `symfony/mailer` instead.
|
||||
|
||||
* The following repository classes and interfaces were added, if you have custom repositories,
|
||||
you need to update them to extend the new ones:
|
||||
|
||||
Addressing:
|
||||
|
||||
* `Sylius\Bundle\AddressingBundle\Doctrine\ORM\AddressRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Addressing\Repository\AddressRepositoryInterface`
|
||||
* `Sylius\Bundle\AddressingBundle\Doctrine\ORM\CountryRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Addressing\Repository\CountryRepositoryInterface`
|
||||
* `Sylius\Bundle\AddressingBundle\Doctrine\ORM\ProvinceRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Addressing\Repository\ProvinceRepositoryInterface`
|
||||
* `Sylius\Bundle\AddressingBundle\Doctrine\ORM\ZoneMemberRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Addressing\Repository\ZoneMemberRepositoryInterface`
|
||||
|
||||
Attribute:
|
||||
|
||||
* `Sylius\Bundle\AttributeBundle\Doctrine\ORM\AttributeRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Attribute\Repository\AttributeRepositoryInterface`
|
||||
* `Sylius\Bundle\AttributeBundle\Doctrine\ORM\AttributeTranslationRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Attribute\Repository\AttributeTranslationRepositoryInterface`
|
||||
* `Sylius\Bundle\AttributeBundle\Doctrine\ORM\AttributeValueRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Attribute\Repository\AttributeValueRepositoryInterface`
|
||||
|
||||
Currency:
|
||||
|
||||
* `Sylius\Bundle\CurrencyBundle\Doctrine\ORM\CurrencyRepository`
|
||||
extends `Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository`
|
||||
implements `Sylius\Component\Currency\Repository\CurrencyRepositoryInterface`
|
||||
|
||||
* The following repository classes and interfaces namespaces were changed, if you have custom repositories,
|
||||
you need to update them to extend the new ones:
|
||||
|
||||
Addressing:
|
||||
|
||||
* `Sylius\Bundle\CoreBundle\Doctrine\ORM\AddressRepository` extended class changed from
|
||||
`Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository` to
|
||||
`Sylius\Bundle\AddressingBundle\Doctrine\ORM\AddressRepository`
|
||||
* `Sylius\Component\Core\Repository\AddressRepositoryInterface` implemented interface changed from
|
||||
`Sylius\Component\Resource\Repository\RepositoryInterface` to
|
||||
`Sylius\Component\Addressing\Repository\AddressRepositoryInterface`
|
||||
|
||||
Attribute:
|
||||
|
||||
* `Sylius\Bundle\CoreBundle\Doctrine\ORM\AttributeRepository` extended class changed from
|
||||
`Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository` to
|
||||
`Sylius\Bundle\AttributeBundle\Doctrine\ORM\AttributeRepository`
|
||||
* `Sylius\Component\Product\Repository\ProductAttributeValueRepositoryInterface` extended interface changed from
|
||||
`Sylius\Component\Resource\Repository\RepositoryInterface` to
|
||||
`Sylius\Component\Attribute\Repository\AttributeValueRepositoryInterface`
|
||||
|
||||
## Frontend
|
||||
|
||||
* `use_webpack` option was removed from the `sylius_ui` configuration, and the Webpack has become the only module bundler provided by Sylius.
|
||||
* `use_webpack` option was removed from the `sylius_ui` configuration, and the Webpack has become the only module
|
||||
bundler provided by Sylius.
|
||||
* `use_webpack` twig global variable was removed. Webpack is always used now, and there is no need to check for it.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
"null", "true", "false",
|
||||
"static", "self", "parent",
|
||||
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "mixed", "never",
|
||||
"Swift_Message",
|
||||
"ApiPlatform\\Core\\Api\\FilterInterface",
|
||||
"ApiPlatform\\Core\\Api\\IriConverterInterface",
|
||||
"ApiPlatform\\Core\\Api\\OperationType",
|
||||
|
|
@ -82,11 +81,12 @@
|
|||
"Payum\\Core\\Security\\TokenInterface",
|
||||
"Payum\\Core\\Security\\Util\\Random",
|
||||
"Payum\\Core\\Storage\\AbstractStorage",
|
||||
"Payum\\Paypal\\ExpressCheckout\\Nvp\\PaypalExpressCheckoutGatewayFactory",
|
||||
"Payum\\Stripe\\StripeCheckoutGatewayFactory",
|
||||
"PHPUnit\\Framework\\ExpectationFailedException",
|
||||
"Psr\\Container\\ContainerInterface",
|
||||
"Psr\\Http\\Message\\RequestFactoryInterface",
|
||||
"Psr\\Http\\Message\\StreamFactoryInterface",
|
||||
"Stripe\\Stripe",
|
||||
"Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactoryInterface"
|
||||
],
|
||||
"php-core-extensions" : [
|
||||
|
|
|
|||
|
|
@ -62,9 +62,7 @@
|
|||
"liip/imagine-bundle": "^2.10",
|
||||
"nyholm/psr7": "^1.6",
|
||||
"pagerfanta/pagerfanta": "^3.0",
|
||||
"payum/core": "^1.7.3",
|
||||
"payum/offline": "^1.7.3",
|
||||
"payum/paypal-express-checkout-nvp": "^1.7.3",
|
||||
"payum/payum-bundle": "^2.5",
|
||||
"php-http/httplug": "^2.4",
|
||||
"php-http/message-factory": "^1.0",
|
||||
|
|
@ -186,6 +184,7 @@
|
|||
"sylius/user-bundle": "self.version"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/doctrine-bundle": "2.11.0",
|
||||
"doctrine/orm": ">= 2.16.0",
|
||||
"stof/doctrine-extensions-bundle": "1.8.0"
|
||||
},
|
||||
|
|
@ -211,6 +210,8 @@
|
|||
"matthiasnoback/symfony-dependency-injection-test": "^4.2",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"mockery/mockery": "^1.4",
|
||||
"payum/paypal-express-checkout-nvp": "^1.7.3",
|
||||
"payum/stripe": "^1.7.3",
|
||||
"phparkitect/phparkitect": "^0.2.9",
|
||||
"phpspec/phpspec": "^7.2",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
|
|
@ -223,7 +224,6 @@
|
|||
"psalm/plugin-mockery": "1.1.0",
|
||||
"psr/event-dispatcher": "^1.0",
|
||||
"rector/rector": "^0.18.0",
|
||||
"stripe/stripe-php": "^12.0",
|
||||
"sylius-labs/coding-standard": "^4.2",
|
||||
"symfony/browser-kit": "^6.3.2",
|
||||
"symfony/debug-bundle": "^6.3.2",
|
||||
|
|
|
|||
|
|
@ -44,6 +44,61 @@ final class ChannelsTest extends JsonApiTestCase
|
|||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_deletes_a_channel(): void
|
||||
{
|
||||
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: '/api/v2/admin/channels/MOBILE',
|
||||
server: $header,
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_OK);
|
||||
|
||||
$this->client->request(
|
||||
method: 'DELETE',
|
||||
uri: '/api/v2/admin/channels/MOBILE',
|
||||
server: $header,
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: '/api/v2/admin/channels/MOBILE',
|
||||
server: $header,
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_prevents_deleting_the_only_channel(): void
|
||||
{
|
||||
$this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
$this->client->request(
|
||||
method: 'DELETE',
|
||||
uri: '/api/v2/admin/channels/MOBILE',
|
||||
server: $header,
|
||||
);
|
||||
$this->client->request(
|
||||
method: 'DELETE',
|
||||
uri: '/api/v2/admin/channels/WEB',
|
||||
server: $header,
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/channel/delete_channel_that_cannot_be_deleted',
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_gets_channels(): void
|
||||
{
|
||||
|
|
@ -119,7 +174,15 @@ final class ChannelsTest extends JsonApiTestCase
|
|||
uri: '/api/v2/admin/channels/' . $channel->getCode(),
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'defaultLocale' => '/api/v2/admin/locales/en_US',
|
||||
'locales' => ['/api/v2/admin/locales/en_US'],
|
||||
'shippingAddressInCheckoutRequired' => true,
|
||||
'taxCalculationStrategy' => 'order_items_based',
|
||||
'accountVerificationRequired' => false,
|
||||
'name' => 'Web Store',
|
||||
'description' => 'different description',
|
||||
'hostname' => 'updated-hostname.com',
|
||||
'color' => 'blue',
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
|
|
@ -129,4 +192,48 @@ final class ChannelsTest extends JsonApiTestCase
|
|||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_updates_a_shop_billing_data(): void
|
||||
{
|
||||
$fixtures = $this->loadFixturesFromFiles(['authentication/api_administrator.yaml', 'channel.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
/** @var ChannelInterface $channel */
|
||||
$channel = $fixtures['channel_web'];
|
||||
|
||||
$this->client->request(
|
||||
method: 'PUT',
|
||||
uri: sprintf('/api/v2/admin/channels/%s', $channel->getCode()),
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'shopBillingData' => [
|
||||
'@id' => sprintf('/api/v2/admin/shop-billing-datas/%s', $channel->getShopBillingData()->getId()),
|
||||
'company' => 'DifferentCompany',
|
||||
'taxId' => '123',
|
||||
'countryCode' => 'DE',
|
||||
'street' => 'Different Street',
|
||||
'city' => 'different City',
|
||||
'postcode' => '12-124'
|
||||
]
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponseCode(
|
||||
$this->client->getResponse(),
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: sprintf('/api/v2/admin/channels/%s/shop-billing-data', $channel->getCode()),
|
||||
server: $header,
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/put_shop_billing_data_response',
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ final class ProductAssociationTypesTest extends JsonApiTestCase
|
|||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/get_product_association_type_response',
|
||||
'admin/product_association_type/get_product_association_type_response',
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ final class ProductAssociationTypesTest extends JsonApiTestCase
|
|||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/get_product_association_type_collection_response',
|
||||
'admin/product_association_type/get_product_association_type_collection_response',
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
|
|
@ -97,11 +97,31 @@ final class ProductAssociationTypesTest extends JsonApiTestCase
|
|||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/post_product_association_type_response',
|
||||
'admin/product_association_type/post_product_association_type_response',
|
||||
Response::HTTP_CREATED,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_create_product_association_type_without_required_data(): void
|
||||
{
|
||||
$this->loadFixturesFromFiles(['product/product_with_many_locales.yaml', 'authentication/api_administrator.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
$this->client->request(
|
||||
method: 'POST',
|
||||
uri: '/api/v2/admin/product-association-types',
|
||||
server: $header,
|
||||
content: '{}',
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/product_association_type/post_product_association_type_without_required_data_response',
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_updates_product_association_type(): void
|
||||
{
|
||||
|
|
@ -116,17 +136,22 @@ final class ProductAssociationTypesTest extends JsonApiTestCase
|
|||
server: $header,
|
||||
content: json_encode([
|
||||
'code' => 'TEST',
|
||||
'translations' => ['en_US' => [
|
||||
'name' => 'test',
|
||||
'description' => 'test description',
|
||||
'locale' => 'de_DE'
|
||||
]]
|
||||
'translations' => [
|
||||
'en_US' => [
|
||||
'@id' => sprintf('/api/v2/admin/product-association-type-translations/%s', $associationType->getTranslation('en_US')->getId()),
|
||||
'name' => 'Similar products',
|
||||
],
|
||||
'de_DE' => [
|
||||
'name' => 'test',
|
||||
'description' => 'test description'
|
||||
],
|
||||
]
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/put_product_association_type_response',
|
||||
'admin/product_association_type/put_product_association_type_response',
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,13 +394,7 @@ final class ProductAttributesTest extends JsonApiTestCase
|
|||
method: 'POST',
|
||||
uri: '/api/v2/admin/product-attributes',
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'translations' => [
|
||||
'en_US' => [
|
||||
'locale' => 'en_US',
|
||||
],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
content: '{}',
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
|
|
@ -423,6 +417,12 @@ final class ProductAttributesTest extends JsonApiTestCase
|
|||
content: json_encode([
|
||||
'code' => 'test',
|
||||
'type' => 'foobar',
|
||||
'translations' => [
|
||||
'en_US' => [
|
||||
'locale' => 'en_US',
|
||||
'name' => 'Test',
|
||||
],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
}
|
||||
|
||||
/** @test */
|
||||
public function it_creates_a_product_variant_enabled_by_default(): void
|
||||
public function it_creates_a_product_variant_enabled_by_default_with_translation_in_default_locale(): void
|
||||
{
|
||||
$this->loadFixturesFromFiles([
|
||||
'authentication/api_administrator.yaml',
|
||||
|
|
@ -168,7 +168,7 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/product_variant/post_product_variant_enabled_by_default_response',
|
||||
'admin/product_variant/post_product_variant_enabled_by_default_with_translation_in_default_locale_response',
|
||||
Response::HTTP_CREATED,
|
||||
);
|
||||
}
|
||||
|
|
@ -325,13 +325,11 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
'minimumPrice' => 500,
|
||||
]],
|
||||
'translations' => [
|
||||
'pl_PL' => [
|
||||
'@id' => sprintf('/api/v2/admin/product-variant-translations/%s', $productVariant->getTranslation('pl_PL')->getId()),
|
||||
'locale' => 'pl_PL',
|
||||
'name' => 'Czerwony kubek',
|
||||
'en_US' => [
|
||||
'@id' => sprintf('/api/v2/admin/product-variant-translations/%s', $productVariant->getTranslation('en_US')->getId()),
|
||||
'name' => 'Red mug',
|
||||
],
|
||||
'de_DE' => [
|
||||
'locale' => 'de_DE',
|
||||
'name' => 'Rote Tasse',
|
||||
],
|
||||
],
|
||||
|
|
@ -396,6 +394,41 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_allow_to_update_product_variant_without_translation_in_default_locale(): void
|
||||
{
|
||||
$fixtures = $this->loadFixturesFromFiles([
|
||||
'authentication/api_administrator.yaml',
|
||||
'channel.yaml',
|
||||
'tax_category.yaml',
|
||||
'shipping_category.yaml',
|
||||
'product/product_variant.yaml',
|
||||
]);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
/** @var ProductVariantInterface $productVariant */
|
||||
$productVariant = $fixtures['product_variant'];
|
||||
|
||||
$this->client->request(
|
||||
method: 'PUT',
|
||||
uri: sprintf('/api/v2/admin/product-variants/%s', $productVariant->getCode()),
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'translations' => [
|
||||
'de_DE' => [
|
||||
'name' => 'Tasse',
|
||||
],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/product_variant/put_product_variant_without_translation_in_default_locale_response',
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_deletes_the_product_variant(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ final class ProductsTest extends JsonApiTestCase
|
|||
]],
|
||||
'translations' => [
|
||||
'en_US' => [
|
||||
'locale' => 'en_US',
|
||||
'slug' => 'mug',
|
||||
'name' => 'Mug',
|
||||
'description' => 'This is a mug',
|
||||
|
|
@ -108,7 +107,6 @@ final class ProductsTest extends JsonApiTestCase
|
|||
'metaDescription' => 'Mug description',
|
||||
],
|
||||
'pl_PL' => [
|
||||
'locale' => 'pl_PL',
|
||||
'slug' => 'kubek',
|
||||
'name' => 'Kubek',
|
||||
'description' => 'To jest kubek',
|
||||
|
|
@ -127,6 +125,58 @@ final class ProductsTest extends JsonApiTestCase
|
|||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_create_a_product_without_required_data(): void
|
||||
{
|
||||
$this->loadFixturesFromFile('authentication/api_administrator.yaml');
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
$this->client->request(
|
||||
method: 'POST',
|
||||
uri: '/api/v2/admin/products',
|
||||
server: $header,
|
||||
content: '{}',
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/product/post_product_without_required_data_response',
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_create_a_product_with_invalid_translation_locale(): void
|
||||
{
|
||||
$this->loadFixturesFromFile('authentication/api_administrator.yaml');
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
$this->client->request(
|
||||
method: 'POST',
|
||||
uri: '/api/v2/admin/products',
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'code' => 'MUG',
|
||||
'translations' => [
|
||||
'en_US' => [
|
||||
'slug' => 'mug',
|
||||
'name' => 'Mug',
|
||||
],
|
||||
'a' => [
|
||||
'slug' => 'kubek',
|
||||
'name' => 'Kubek',
|
||||
],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'admin/product/post_product_with_invalid_translation_locale',
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_updates_the_existing_product(): void
|
||||
{
|
||||
|
|
@ -178,7 +228,6 @@ final class ProductsTest extends JsonApiTestCase
|
|||
'translations' => [
|
||||
'en_US' => [
|
||||
'@id' => sprintf('/api/v2/admin/product-translations/%s', $product->getTranslation('en_US')->getId()),
|
||||
'locale' => 'en_US',
|
||||
'slug' => 'caps/cap',
|
||||
'name' => 'Cap',
|
||||
'description' => 'This is a cap',
|
||||
|
|
@ -188,7 +237,6 @@ final class ProductsTest extends JsonApiTestCase
|
|||
],
|
||||
'pl_PL' => [
|
||||
'@id' => sprintf('/api/v2/admin/product-translations/%s', $product->getTranslation('pl_PL')->getId()),
|
||||
'locale' => 'pl_PL',
|
||||
'slug' => 'czapki/czapka',
|
||||
'name' => 'Czapka',
|
||||
'description' => 'To jest czapka',
|
||||
|
|
|
|||
|
|
@ -99,13 +99,7 @@ final class TaxonsTest extends JsonApiTestCase
|
|||
method: 'POST',
|
||||
uri: '/api/v2/admin/taxons',
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'translations' => [
|
||||
'en_US' => [
|
||||
'locale' => 'en_US',
|
||||
]
|
||||
]
|
||||
], JSON_THROW_ON_ERROR),
|
||||
content: '{}',
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Feature: Adding a new channel
|
|||
And the store operates in "United States" and "Poland"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @api
|
||||
@api @ui
|
||||
Scenario: Adding a new channel
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
|
|
@ -23,7 +23,7 @@ Feature: Adding a new channel
|
|||
Then I should be notified that it has been successfully created
|
||||
And the channel "Mobile channel" should appear in the registry
|
||||
|
||||
@ui @api
|
||||
@api @ui
|
||||
Scenario: Adding a new channel with additional fields
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Feature: Adding a new channel with shop billing data
|
|||
And the store operates in "United States"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @api
|
||||
@api @ui
|
||||
Scenario: Adding a new channel with shop billing data
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Feature: Browsing channels
|
|||
And the store operates on another channel named "Mobile Channel"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @api
|
||||
@api @ui
|
||||
Scenario: Browsing defined channels
|
||||
When I want to browse channels
|
||||
Then I should see 2 channels in the list
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Feature: Channel unique code validation
|
|||
Given the store operates on a channel identified by "WEB" code
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Trying to add channel with taken code
|
||||
When I want to create a new channel
|
||||
And I specify its code as "WEB"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Feature: Channel validation
|
|||
Background:
|
||||
Given I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Trying to add a new channel without specifying its code
|
||||
When I want to create a new channel
|
||||
And I name it "Mobile channel"
|
||||
|
|
@ -16,7 +16,7 @@ Feature: Channel validation
|
|||
Then I should be notified that code is required
|
||||
And channel with name "Mobile channel" should not be added
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Trying to add a new channel without specifying its name
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
|
|
@ -25,7 +25,7 @@ Feature: Channel validation
|
|||
Then I should be notified that name is required
|
||||
And channel with code "MOBILE" should not be added
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Trying to add a new channel without base currency
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
|
|
@ -34,7 +34,7 @@ Feature: Channel validation
|
|||
Then I should be notified that base currency is required
|
||||
And channel with code "MOBILE" should not be added
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Trying to add a new channel without default locale
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
|
|
@ -43,7 +43,7 @@ Feature: Channel validation
|
|||
Then I should be notified that default locale is required
|
||||
And channel with code "MOBILE" should not be added
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Trying to remove name from existing channel
|
||||
Given the store operates on a channel named "Web Channel"
|
||||
When I want to modify this channel
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Feature: Deleting a channel
|
|||
And the store operates on another channel named "Mobile Store"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Deleted channel should disappear from the registry
|
||||
When I delete channel "Web Store"
|
||||
Then I should be notified that it has been successfully deleted
|
||||
|
|
|
|||
|
|
@ -8,20 +8,12 @@ Feature: Editing channel
|
|||
Given the store operates on a channel named "Web Channel"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@todo
|
||||
Scenario: Trying to change channel code
|
||||
@api @ui
|
||||
Scenario: Being unable to change the code of an existing channel
|
||||
When I want to modify a channel "Web Channel"
|
||||
And I change its code to "MOBILE"
|
||||
And I save my changes
|
||||
Then I should be notified that code cannot be changed
|
||||
And channel "Web Channel" should still have code "MOBILE"
|
||||
Then I should not be able to edit its code
|
||||
|
||||
@ui
|
||||
Scenario: Seeing disabled code field when editing channel
|
||||
When I want to modify a channel "Web Channel"
|
||||
Then the code field should be disabled
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Renaming the channel
|
||||
When I want to modify a channel "Web Channel"
|
||||
And I rename it to "Website store"
|
||||
|
|
@ -29,7 +21,7 @@ Feature: Editing channel
|
|||
Then I should be notified that it has been successfully edited
|
||||
And this channel name should be "Website store"
|
||||
|
||||
@ui
|
||||
Scenario: Seeing disabled base currency field during channel edition
|
||||
@api @ui
|
||||
Scenario: Being unable to change base currency of an existing channel
|
||||
When I want to modify a channel "Web Channel"
|
||||
Then the base currency field should be disabled
|
||||
Then I should not be able to edit its base currency
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Feature: Editing menu taxon on channel
|
|||
And channel "Web Store" has menu taxon "Clothes"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @javascript
|
||||
@api @ui @javascript
|
||||
Scenario: Editing menu taxon on the channel
|
||||
When I want to modify a channel "Web Store"
|
||||
And I change its menu taxon to "Guns"
|
||||
|
|
|
|||
|
|
@ -10,14 +10,20 @@ Feature: Editing shop billing data on channel
|
|||
And channel "Web Store" billing data is "Ragnarok", "Pacific Coast Hwy", "90806" "Los Angeles", "United States" with "1100110011" tax ID
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Editing shop billing data on channel
|
||||
When I want to modify a channel "Web Store"
|
||||
And I specify company as "Götterdämmerung"
|
||||
And I specify tax ID as "666777"
|
||||
And I specify shop billing address as "Valhalla", "123" "Asgard", "United States"
|
||||
And I specify shop billing data for this channel as "Götterdämmerung", "Valhalla", "123", "Asgard", "666777" tax ID and "United States" country
|
||||
And I save my changes
|
||||
Then I should be notified that it has been successfully edited
|
||||
And this channel company should be "Götterdämmerung"
|
||||
And this channel tax ID should be "666777"
|
||||
And this channel shop billing address should be "Valhalla", "123" "Asgard", "United States"
|
||||
And this channel shop billing address should be "Valhalla", "123" "Asgard" and "United States" country
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Editing shop billing data with wrong country code
|
||||
When I want to modify a channel "Web Store"
|
||||
And I specify new country code for this channel as "ZZ"
|
||||
And I save my changes
|
||||
Then I should be notified that it is not a valid country
|
||||
And this channel shop billing address should still be "Pacific Coast Hwy", "90806" "Los Angeles" and "United States" country
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ Feature: Not being able to add a disabled channel when no other exist
|
|||
And the store has locale "English (United States)"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
Scenario: Adding a new disabled channel should result
|
||||
@api @ui
|
||||
Scenario: Trying to add a new disabled channel when no other exist
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
And I name it "Mobile channel"
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ Feature: Not being able to delete a last available channel
|
|||
Given the store operates on a channel named "Web Store"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
Scenario: Prevented from deleting only channel
|
||||
@api @ui
|
||||
Scenario: Preventing from deleting only channel
|
||||
When I delete channel "Web Store"
|
||||
Then I should be notified that it cannot be deleted
|
||||
And this channel should still be in the registry
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Feature: Toggling a channel
|
|||
Given the store operates on a channel named "Web Channel"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Disabling the last available channel
|
||||
Given the channel "Web Channel" is enabled
|
||||
When I want to modify this channel
|
||||
|
|
|
|||
|
|
@ -7,20 +7,24 @@ Feature: Selecting available currencies for a channel
|
|||
Background:
|
||||
Given the store has currency "Euro"
|
||||
And the store has locale "English (United States)"
|
||||
And the store operates in "United States"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding a new channel with currencies
|
||||
When I want to create a new channel
|
||||
And I specify its code as MOBILE
|
||||
And I choose "Euro" as the base currency
|
||||
And I name it "Mobile store"
|
||||
And I allow for paying in "Euro"
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
And I add it
|
||||
Then I should be notified that it has been successfully created
|
||||
And paying in Euro should be possible for the "Mobile store" channel
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding currencies to an existing channel
|
||||
Given the store operates on a channel named "Web store"
|
||||
When I want to modify this channel
|
||||
|
|
|
|||
|
|
@ -8,19 +8,21 @@ Feature: Selecting default tax zone for a channel
|
|||
Given the store operates on a single channel in "United States"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding a new channel with default tax zone
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
And I name it "Mobile store"
|
||||
And I select the "United States" as default tax zone
|
||||
And I choose "USD" as the base currency
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
And I add it
|
||||
Then I should be notified that it has been successfully created
|
||||
And the default tax zone for the "Mobile store" channel should be "United States"
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Selecting default tax zone for existing channel
|
||||
Given the store operates on a channel named "Web store"
|
||||
When I want to modify this channel
|
||||
|
|
@ -29,7 +31,7 @@ Feature: Selecting default tax zone for a channel
|
|||
Then I should be notified that it has been successfully edited
|
||||
And the default tax zone for the "Web store" channel should be "United States"
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Removing existing channel default tax zone
|
||||
Given the store operates on a channel named "Web store"
|
||||
And its default tax zone is zone "US"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Feature: Selecting available locales for a channel
|
|||
And the store has locale "English (United States)"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding a new channel with locales
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
|
|
@ -17,11 +17,12 @@ Feature: Selecting available locales for a channel
|
|||
And I make it available in "English (United States)"
|
||||
And I choose "Euro" as the base currency
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I select the "Order items based" as tax calculation strategy
|
||||
And I add it
|
||||
Then I should be notified that it has been successfully created
|
||||
And the channel "Mobile Channel" should be available in "English (United States)"
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding locales to an existing channel
|
||||
Given the store operates on a channel named "Web Channel"
|
||||
When I want to modify this channel
|
||||
|
|
@ -30,23 +31,23 @@ Feature: Selecting available locales for a channel
|
|||
Then I should be notified that it has been successfully edited
|
||||
And the channel "Web Channel" should be available in "English (United States)"
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Being unable to disable locale used as the default one for a channel
|
||||
Given the store operates on a channel named "Web"
|
||||
And this channel allows to shop using "English (United States)" and "Polish (Poland)" locales
|
||||
And this channel uses the "English (United States)" locale as default
|
||||
And I am modifying a channel "Web"
|
||||
When I make it available only in "Polish (Poland)"
|
||||
When I want to modify this channel
|
||||
And I make it available only in "Polish (Poland)"
|
||||
And I try to save my changes
|
||||
Then I should be notified that the default locale has to be enabled
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Being unable to set disabled locale as a default one for a channel
|
||||
Given the store has locale "Polish (Poland)"
|
||||
And the store operates on a channel named "Web"
|
||||
And this channel allows to shop using the "English (United States)" locale
|
||||
And this channel uses the "English (United States)" locale as default
|
||||
And I am modifying a channel "Web"
|
||||
When I choose "Polish (Poland)" as a default locale
|
||||
When I want to modify this channel
|
||||
And I choose "Polish (Poland)" as a default locale
|
||||
And I try to save my changes
|
||||
Then I should be notified that the default locale has to be enabled
|
||||
|
|
|
|||
|
|
@ -20,19 +20,20 @@ Feature: Selecting tax calculation strategy for a channel
|
|||
Then I should be notified that it has been successfully created
|
||||
And the tax calculation strategy for the "Mobile store" channel should be "Order items based"
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding a new channel with tax calculation strategy
|
||||
When I want to create a new channel
|
||||
And I specify its code as "MOBILE"
|
||||
And I select the "Order item units based" as tax calculation strategy
|
||||
And I name it "Mobile store"
|
||||
And I choose "Euro" as the base currency
|
||||
And I make it available in "English (United States)"
|
||||
And I choose "English (United States)" as a default locale
|
||||
And I add it
|
||||
Then I should be notified that it has been successfully created
|
||||
And the tax calculation strategy for the "Mobile store" channel should be "Order item units based"
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Changing tax calculation strategy of existing channel
|
||||
Given the store operates on a channel named "Web store"
|
||||
When I want to modify this channel
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Feature: Toggling a channel
|
|||
And the store operates on another channel named "Mobile Channel"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Disabling the channel
|
||||
Given the channel "Web Channel" is enabled
|
||||
When I want to modify this channel
|
||||
|
|
@ -18,7 +18,7 @@ Feature: Toggling a channel
|
|||
Then I should be notified that it has been successfully edited
|
||||
And this channel should be disabled
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Enabling the channel
|
||||
Given the channel "Web Channel" is disabled
|
||||
When I want to modify this channel
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Feature: Seeing correct select attribute values in different locale than default
|
|||
Given the store is available in "French (France)"
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui @javascript @api
|
||||
@ui @javascript @no-api
|
||||
Scenario: Seeing correct attribute values in different locale than default one
|
||||
When I want to create a new select product attribute
|
||||
And I specify its code as "mug_material"
|
||||
|
|
|
|||
|
|
@ -1820,6 +1820,11 @@ parameters:
|
|||
count: 1
|
||||
path: src/Sylius/Bundle/ApiBundle/Serializer/ShippingMethodNormalizer.php
|
||||
|
||||
-
|
||||
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\Serializer\\\\TranslatableDenormalizer\\:\\:hasDefaultTranslation\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: src/Sylius/Bundle/ApiBundle/Serializer/TranslatableDenormalizer.php
|
||||
|
||||
-
|
||||
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\Serializer\\\\ZoneDenormalizer\\:\\:getZoneMemberByCode\\(\\) has parameter \\$zoneMembers with generic interface Doctrine\\\\Common\\\\Collections\\\\Selectable but does not specify its types\\: TKey, T$#"
|
||||
count: 1
|
||||
|
|
|
|||
|
|
@ -24,5 +24,7 @@
|
|||
|
||||
<server name="IS_DOCTRINE_ORM_SUPPORTED" value="true" />
|
||||
<server name="ESCAPE_JSON" value="true" />
|
||||
|
||||
<server name="KERNEL_CLASS" value="App\Kernel" />
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,9 @@ interface ApiClientInterface
|
|||
|
||||
public function addFile(string $key, UploadedFile $file): void;
|
||||
|
||||
public function addRequestData(string $key, string|int|bool|array $value): void;
|
||||
public function addRequestData(string $key, null|string|int|bool|array $value): void;
|
||||
|
||||
public function replaceRequestData(string $key, null|string|int|bool|array $value): void;
|
||||
|
||||
public function setSubResourceData(string $key, array $data): void;
|
||||
|
||||
|
|
|
|||
|
|
@ -200,12 +200,18 @@ final class ApiPlatformClient implements ApiClientInterface
|
|||
$this->request->updateFiles([$key => $file]);
|
||||
}
|
||||
|
||||
/** @param string|int|bool|array $value */
|
||||
public function addRequestData(string $key, $value): void
|
||||
public function addRequestData(string $key, null|string|int|bool|array $value): void
|
||||
{
|
||||
$this->request->updateContent([$key => $value]);
|
||||
}
|
||||
|
||||
public function replaceRequestData(string $key, null|string|int|bool|array $value): void
|
||||
{
|
||||
$requestContent = $this->request->getContent();
|
||||
|
||||
$this->request->setContent(array_replace($requestContent, [$key => $value]));
|
||||
}
|
||||
|
||||
public function updateRequestData(array $data): void
|
||||
{
|
||||
$this->request->updateContent($data);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Behat\Context\Api\Admin;
|
||||
|
||||
use ApiPlatform\Api\IriConverterInterface;
|
||||
use Behat\Behat\Context\Context;
|
||||
use Sylius\Behat\Client\ApiClientInterface;
|
||||
use Sylius\Behat\Client\ResponseCheckerInterface;
|
||||
use Sylius\Behat\Context\Api\Resources;
|
||||
use Sylius\Component\Addressing\Model\CountryInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ShopBillingDataInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class ManagingChannelsBillingDataContext implements Context
|
||||
{
|
||||
public function __construct(
|
||||
private ApiClientInterface $client,
|
||||
private ResponseCheckerInterface $responseChecker,
|
||||
private IriConverterInterface $iriConverter,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this channel) company should be "([^"]+)"$/
|
||||
*/
|
||||
public function thisChannelCompanyShouldBe(ChannelInterface $channel, string $company): void
|
||||
{
|
||||
$shopBillingData = $this->getShopBillingDataFromChannel($channel);
|
||||
|
||||
Assert::same($shopBillingData->getCompany(), $company);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this channel) tax ID should be "([^"]+)"$/
|
||||
*/
|
||||
public function thisChanneTaxIdShouldBe(ChannelInterface $channel, string $taxId): void
|
||||
{
|
||||
$shopBillingData = $this->getShopBillingDataFromChannel($channel);
|
||||
|
||||
Assert::same($shopBillingData->getTaxId(), $taxId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this channel) shop billing address should be "([^"]+)", "([^"]+)" "([^"]+)" and ("([^"]+)" country)$/
|
||||
* @Then /^(this channel) shop billing address should still be "([^"]+)", "([^"]+)" "([^"]+)" and ("([^"]+)" country)$/
|
||||
*/
|
||||
public function thisChannelShopBillingAddressShouldBe(
|
||||
ChannelInterface $channel,
|
||||
string $street,
|
||||
string $postcode,
|
||||
string $city,
|
||||
CountryInterface $country,
|
||||
): void {
|
||||
$shopBillingData = $this->getShopBillingDataFromChannel($channel);
|
||||
|
||||
Assert::same($shopBillingData->getStreet(), $street);
|
||||
Assert::same($shopBillingData->getPostcode(), $postcode);
|
||||
Assert::same($shopBillingData->getCity(), $city);
|
||||
Assert::same($shopBillingData->getCountryCode(), $country->getCode());
|
||||
}
|
||||
|
||||
private function getShopBillingDataFromChannel(ChannelInterface $channel): ShopBillingDataInterface
|
||||
{
|
||||
$this->client->show(Resources::CHANNELS, $channel->getCode());
|
||||
|
||||
/** @var ShopBillingDataInterface $shopBillingData */
|
||||
$shopBillingData = $this->iriConverter->getResourceFromIri($this->responseChecker->getValue($this->client->getLastResponse(), 'shopBillingData'));
|
||||
|
||||
return $shopBillingData;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ use Sylius\Behat\Client\ResponseCheckerInterface;
|
|||
use Sylius\Behat\Context\Api\Resources;
|
||||
use Sylius\Behat\Service\Converter\SectionAwareIriConverterInterface;
|
||||
use Sylius\Component\Addressing\Model\CountryInterface;
|
||||
use Sylius\Component\Addressing\Model\ZoneInterface;
|
||||
use Sylius\Component\Core\Formatter\StringInflector;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
|
|
@ -47,6 +48,14 @@ final class ManagingChannelsContext implements Context
|
|||
$this->client->buildCreateRequest(Resources::CHANNELS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I delete channel :channel
|
||||
*/
|
||||
public function iDeleteChannel(ChannelInterface $channel): void
|
||||
{
|
||||
$this->client->delete(Resources::CHANNELS, $channel->getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I want to modify a channel :channel
|
||||
*/
|
||||
|
|
@ -55,23 +64,78 @@ final class ManagingChannelsContext implements Context
|
|||
$this->client->buildUpdateRequest(Resources::CHANNELS, $channel->getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I rename it to :name
|
||||
* @When I do not name it
|
||||
* @When I remove its name
|
||||
*/
|
||||
public function iRenameIt(string $name = ''): void
|
||||
{
|
||||
$this->client->addRequestData('name', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I (enable|disable) it$/
|
||||
*/
|
||||
public function iDisableIt(string $toggleAction): void
|
||||
{
|
||||
$this->client->addRequestData('enabled', $toggleAction === 'enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I change its menu taxon to :taxon
|
||||
*/
|
||||
public function iChangeItsMenuTaxonTo(TaxonInterface $taxon): void
|
||||
{
|
||||
$this->client->addRequestData('menuTaxon', $this->sectionAwareIriConverter->getIriFromResourceInSection($taxon, 'admin'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I specify its :field as :value
|
||||
* @When I :field it :value
|
||||
* @When I set its :field as :value
|
||||
* @When I define its :field as :value
|
||||
* @When I do not specify its :field
|
||||
*/
|
||||
public function iSpecifyItsAs(string $field, string $value): void
|
||||
public function iSpecifyItsAs(string $field, string $value = ''): void
|
||||
{
|
||||
$this->client->addRequestData($field, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I choose :currency as the base currency
|
||||
* @When I do not choose base currency
|
||||
*/
|
||||
public function iChooseAsTheBaseCurrency(CurrencyInterface $currency): void
|
||||
public function iChooseAsTheBaseCurrency(?CurrencyInterface $currency = null): void
|
||||
{
|
||||
$this->client->addRequestData('baseCurrency', $this->sectionAwareIriConverter->getIriFromResourceInSection($currency, 'admin'));
|
||||
$this->client->addRequestData(
|
||||
'baseCurrency',
|
||||
null === $currency ? $currency : $this->sectionAwareIriConverter->getIriFromResourceInSection($currency, 'admin'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I allow for paying in :currency
|
||||
*/
|
||||
public function iAllowToPayingForThisChannel(CurrencyInterface $currency): void
|
||||
{
|
||||
$this->client->addRequestData('currencies', [$this->iriConverter->getIriFromResource($currency)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I select the :zone as default tax zone
|
||||
*/
|
||||
public function iSelectDefaultTaxZone(ZoneInterface $zone): void
|
||||
{
|
||||
$this->client->addRequestData('defaultTaxZone', $this->iriConverter->getIriFromResource($zone));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I remove its default tax zone
|
||||
*/
|
||||
public function iRemoveItsDefaultTaxZone(): void
|
||||
{
|
||||
$this->client->addRequestData('defaultTaxZone', null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -79,15 +143,27 @@ final class ManagingChannelsContext implements Context
|
|||
*/
|
||||
public function iMakeItAvailableInLocale(LocaleInterface $locale): void
|
||||
{
|
||||
$this->client->addRequestData('locales', [$this->iriConverter->getIriFromResource($locale)]);
|
||||
$this->client->addRequestData('locales', [$this->sectionAwareIriConverter->getIriFromResourceInSection($locale, 'admin')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I make it available only in :locale
|
||||
*/
|
||||
public function iMakeItAvailableOnlyInLocale(LocaleInterface $locale): void
|
||||
{
|
||||
$this->client->replaceRequestData('locales', [$this->sectionAwareIriConverter->getIriFromResourceInSection($locale, 'admin')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I choose :locale as a default locale
|
||||
* @When I do not choose default locale
|
||||
*/
|
||||
public function iChooseAsADefaultLocale(LocaleInterface $locale): void
|
||||
public function iChooseAsADefaultLocale(?LocaleInterface $locale = null): void
|
||||
{
|
||||
$this->client->addRequestData('defaultLocale', $this->sectionAwareIriConverter->getIriFromResourceInSection($locale, 'admin'));
|
||||
$this->client->addRequestData(
|
||||
'defaultLocale',
|
||||
null === $locale ? $locale : $this->sectionAwareIriConverter->getIriFromResourceInSection($locale, 'admin'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,6 +241,44 @@ final class ManagingChannelsContext implements Context
|
|||
$this->shopBillingData['taxId'] = $taxId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I specify shop billing data for (this channel) as "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)" tax ID and ("([^"]+)" country)$/
|
||||
*/
|
||||
public function iSpecifyShopBillingDataAs(
|
||||
ChannelInterface $channel,
|
||||
string $company,
|
||||
string $street,
|
||||
string $postcode,
|
||||
string $city,
|
||||
string $taxId,
|
||||
CountryInterface $country,
|
||||
): void {
|
||||
$shopBillingDataId = $this->iriConverter->getIriFromResource($channel->getShopBillingData());
|
||||
|
||||
$this->client->addRequestData(
|
||||
'shopBillingData',
|
||||
[
|
||||
'@id' => $shopBillingDataId,
|
||||
'company' => $company,
|
||||
'street' => $street,
|
||||
'postcode' => $postcode,
|
||||
'city' => $city,
|
||||
'countryCode' => $country->getCode(),
|
||||
'taxId' => $taxId,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I specify new country code for (this channel) as "([^"]+)"$/
|
||||
*/
|
||||
public function iSpecifyNewCountryCodeForThisChannelAs(ChannelInterface $channel, string $code): void
|
||||
{
|
||||
$shopBillingDataId = $this->iriConverter->getIriFromResource($channel->getShopBillingData());
|
||||
|
||||
$this->client->addRequestData('shopBillingData', ['@id' => $shopBillingDataId, 'countryCode' => $code]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I specify shop billing address as :street, :postcode :city, :country
|
||||
*/
|
||||
|
|
@ -180,6 +294,15 @@ final class ManagingChannelsContext implements Context
|
|||
$this->shopBillingData['countryCode'] = $country->getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I save it
|
||||
*/
|
||||
public function iSaveIt(): void
|
||||
{
|
||||
$this->iAddIt();
|
||||
$this->client->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I select the :taxCalculationStrategy as tax calculation strategy
|
||||
*/
|
||||
|
|
@ -280,6 +403,74 @@ final class ManagingChannelsContext implements Context
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should not be able to edit its code
|
||||
*/
|
||||
public function iShouldNotBeAbleToEditItsCode(): void
|
||||
{
|
||||
$this->client->updateRequestData(['code' => 'NEW_CODE']);
|
||||
|
||||
Assert::false($this->responseChecker->hasValue($this->client->update(), 'code', 'NEW_CODE'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the base currency field should be disabled
|
||||
* @Then I should not be able to edit its base currency
|
||||
*/
|
||||
public function theBaseCurrencyFieldShouldBeDisabled(): void
|
||||
{
|
||||
$this->client->updateRequestData(['baseCurrency' => 'PLN']);
|
||||
|
||||
Assert::false($this->responseChecker->hasValue($this->client->update(), 'baseCurrency', 'PLN'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this channel) name should be "([^"]*)"$/
|
||||
*/
|
||||
public function thisChannelNameShouldBe(ChannelInterface $channel, string $name): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->hasValue($this->client->show(Resources::CHANNELS, $channel->getCode()), 'name', $name),
|
||||
sprintf('Its Channel does not have name %s.', $name),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the :channel channel should no longer exist in the registry
|
||||
*/
|
||||
public function theChannelShouldNoLongerExistInTheRegistry(string $name): void
|
||||
{
|
||||
Assert::false(
|
||||
$this->responseChecker->hasItemWithValue($this->client->index(Resources::CHANNELS), 'name', $name),
|
||||
sprintf('Channel with name %s exists', $name),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that it has been successfully deleted
|
||||
*/
|
||||
public function iShouldBeNotifiedThatChannelHasBeenDeleted(): void
|
||||
{
|
||||
Assert::true($this->responseChecker->isDeletionSuccessful($this->client->getLastResponse()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this channel) menu (taxon should be "([^"]+)")$/
|
||||
*/
|
||||
public function thisChannelMenuTaxonShouldBe(ChannelInterface $channel, TaxonInterface $taxon): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->hasValue(
|
||||
$this->client->show(
|
||||
Resources::CHANNELS,
|
||||
$channel->getCode(),
|
||||
),
|
||||
'menuTaxon',
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($taxon, 'admin'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should see :count channels in the list
|
||||
*/
|
||||
|
|
@ -299,4 +490,225 @@ final class ManagingChannelsContext implements Context
|
|||
$type === 'shipping',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that it cannot be deleted
|
||||
*/
|
||||
public function iShouldBeNotifiedThatItCannotBeDeleted(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'The channel cannot be deleted. At least one enabled channel is required.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that at least one channel has to be defined
|
||||
*/
|
||||
public function iShouldBeNotifiedThatAtLeastOneChannelHasToBeDefined(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'Must have at least one enabled entity',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then channel with name :channel should still be enabled
|
||||
* @Then /^(this channel) should be enabled$/
|
||||
*/
|
||||
public function channelWithNameShouldStillBeEnabled(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->hasValue($this->client->show(Resources::CHANNELS, $channel->getCode()), 'enabled', true),
|
||||
sprintf('Channel with name %s does not exists', $channel->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then this channel should still be named :channel
|
||||
*/
|
||||
public function thisChannelShouldStillBeNamed(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->hasValue($this->client->show(Resources::CHANNELS, $channel->getCode()), 'name', $channel->getName()),
|
||||
sprintf('Channel with name %s does not exists', $channel->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then paying in :currency should be possible for the :channel channel
|
||||
*/
|
||||
public function payingInCurrencyShouldBePossibleForTheChannel(CurrencyInterface $currency, ChannelInterface $channel): void
|
||||
{
|
||||
$currencies = $this->responseChecker->getValue(
|
||||
$this->client->show(Resources::CHANNELS, $channel->getCode()),
|
||||
'currencies',
|
||||
);
|
||||
|
||||
Assert::true(in_array($this->sectionAwareIriConverter->getIriFromResourceInSection($currency, 'admin'), $currencies));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then channel :channel should not have default tax zone
|
||||
*/
|
||||
public function channelShouldNotHaveDefaultTaxZone(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::same(
|
||||
$this->responseChecker->getValue($this->client->show(Resources::CHANNELS, $channel->getCode()), 'defaultTaxZone'),
|
||||
null,
|
||||
sprintf('Channel %s has default tax zone', $channel->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the default tax zone for the :channel channel should be :zone
|
||||
*/
|
||||
public function theDefaultTaxZoneForTheChannelShouldBe(ChannelInterface $channel, ZoneInterface $zone): void
|
||||
{
|
||||
Assert::same(
|
||||
$this->responseChecker->getValue($this->client->show(Resources::CHANNELS, $channel->getCode()), 'defaultTaxZone'),
|
||||
$this->sectionAwareIriConverter->getIriFromResourceInSection($zone, 'admin'),
|
||||
sprintf('Channel %s does not have %s default tax zone', $channel->getName(), $zone),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the channel :channel should be available in :locale
|
||||
*/
|
||||
public function theChannelShouldBeAvailableIn(ChannelInterface $channel, LocaleInterface $locale): void
|
||||
{
|
||||
$locales = $this->responseChecker->getValue(
|
||||
$this->client->show(Resources::CHANNELS, $channel->getCode()),
|
||||
'locales',
|
||||
);
|
||||
|
||||
Assert::true(in_array($this->sectionAwareIriConverter->getIriFromResourceInSection($locale, 'admin'), $locales));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that the default locale has to be enabled
|
||||
*/
|
||||
public function iShouldBeNotifiedThatTheDefaultLocaleHasToBeEnabled(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'defaultLocale: Default locale has to be enabled.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this channel) should still be in the registry$/
|
||||
*/
|
||||
public function thisChannelShouldStillBeInTheRegistry(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->hasItemWithValue($this->client->index(Resources::CHANNELS), 'code', $channel->getCode()),
|
||||
sprintf('Channel with code %s does not exists', $channel->getCode()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the tax calculation strategy for the :channel channel should be :taxCalculationStrategy
|
||||
*/
|
||||
public function theTaxCalculationStrategyForTheChannelShouldBe(
|
||||
ChannelInterface $channel,
|
||||
string $taxCalculationStrategy,
|
||||
): void {
|
||||
Assert::same(
|
||||
$this->responseChecker->getValue($this->client->show(Resources::CHANNELS, $channel->getCode()), 'taxCalculationStrategy'),
|
||||
StringInflector::nameToLowercaseCode($taxCalculationStrategy),
|
||||
sprintf('Channel %s does not have %s tax calculation strategy', $channel->getName(), $taxCalculationStrategy),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^(this channel) should be disabled$/
|
||||
*/
|
||||
public function thisChannelShouldBeDisabled(ChannelInterface $channel): void
|
||||
{
|
||||
Assert::same(
|
||||
$this->responseChecker->getValue($this->client->show(Resources::CHANNELS, $channel->getCode()), 'enabled'),
|
||||
false,
|
||||
sprintf('Channel %s is enabled', $channel->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that :element is required
|
||||
*/
|
||||
public function iShouldBeNotifiedThatIsRequired(string $element): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
sprintf('%s: Please enter channel %s.', $element, $element),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that base currency is required
|
||||
*/
|
||||
public function iShouldBeNotifiedThatBaseCurrencyIsRequired(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'Expected IRI or nested document for attribute "baseCurrency", "NULL" given.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that default locale is required
|
||||
*/
|
||||
public function iShouldBeNotifiedThatDefaultLocaleIsRequired(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'Expected IRI or nested document for attribute "defaultLocale", "NULL" given.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then channel with :element :value should not be added
|
||||
*/
|
||||
public function channelWithShouldNotBeAdded(string $element, string $value): void
|
||||
{
|
||||
Assert::false(
|
||||
$this->responseChecker->hasItemWithValue($this->client->index(Resources::CHANNELS), $element, $value),
|
||||
sprintf('Channel with %s: %s exists', $element, $value),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that channel with this code already exists
|
||||
*/
|
||||
public function iShouldBeNotifiedThatChannelWithThisCodeAlreadyExists(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'code: Channel code has to be unique.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then there should still be only one channel with :element :value
|
||||
*/
|
||||
public function thereShouldStillBeOnlyOneChannelWithCode(string $element, string $value): void
|
||||
{
|
||||
Assert::same(
|
||||
count($this->responseChecker->getCollectionItemsWithValue($this->client->index(Resources::CHANNELS), $element, $value)),
|
||||
1,
|
||||
sprintf('There is more than one channel with %s: %s', $element, $value),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified that it is not a valid country
|
||||
*/
|
||||
public function iShouldBeNotifiedThatItIsNotAValidCountryCode(): void
|
||||
{
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
'countryCode: This value is not a valid country.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ final class ManagingProductsContext implements Context
|
|||
public function iSetItsNonTranslatableAttributeTo(ProductAttributeInterface $attribute, string $value): void
|
||||
{
|
||||
$this->client->addSubResourceData(
|
||||
'attributes',
|
||||
'attributes',
|
||||
[
|
||||
'attribute' => $this->iriConverter->getIriFromResource($attribute),
|
||||
'value' => $this->getAttributeValueInProperType($attribute, $value),
|
||||
|
|
@ -290,7 +290,7 @@ final class ManagingProductsContext implements Context
|
|||
public function iSetTheInvalidIntegerValueOfTheNonTranslatableAttributeTo(ProductAttributeInterface $attribute, int $value): void
|
||||
{
|
||||
$this->client->addSubResourceData(
|
||||
'attributes',
|
||||
'attributes',
|
||||
[
|
||||
'attribute' => $this->iriConverter->getIriFromResource($attribute),
|
||||
'value' => $value,
|
||||
|
|
@ -304,7 +304,7 @@ final class ManagingProductsContext implements Context
|
|||
public function iSetTheInvalidStringValueOfTheNonTranslatableAttributeTo(ProductAttributeInterface $attribute, string $value): void
|
||||
{
|
||||
$this->client->addSubResourceData(
|
||||
'attributes',
|
||||
'attributes',
|
||||
[
|
||||
'attribute' => $this->iriConverter->getIriFromResource($attribute),
|
||||
'value' => $value,
|
||||
|
|
@ -320,7 +320,7 @@ final class ManagingProductsContext implements Context
|
|||
public function iSetItsAttributeTo(
|
||||
ProductAttributeInterface $attribute,
|
||||
?string $value = null,
|
||||
string $localeCode = 'en_US'
|
||||
string $localeCode = 'en_US',
|
||||
): void {
|
||||
$this->client->addSubResourceData(
|
||||
'attributes',
|
||||
|
|
@ -363,7 +363,7 @@ final class ManagingProductsContext implements Context
|
|||
public function iSelectValueInForTheAttribute(
|
||||
string $value,
|
||||
string $localeCode,
|
||||
ProductAttributeInterface $attribute
|
||||
ProductAttributeInterface $attribute,
|
||||
): void {
|
||||
$this->client->addSubResourceData(
|
||||
'attributes',
|
||||
|
|
@ -380,7 +380,7 @@ final class ManagingProductsContext implements Context
|
|||
*/
|
||||
public function iSelectValueForTheAttribute(
|
||||
string $value,
|
||||
ProductAttributeInterface $attribute
|
||||
ProductAttributeInterface $attribute,
|
||||
): void {
|
||||
$this->client->addSubResourceData(
|
||||
'attributes',
|
||||
|
|
@ -678,7 +678,7 @@ final class ManagingProductsContext implements Context
|
|||
public function nonTranslatableAttributeOfProductShouldBe(
|
||||
ProductAttributeInterface $attribute,
|
||||
ProductInterface $product,
|
||||
string $value
|
||||
string $value,
|
||||
): void {
|
||||
$this->client->show(Resources::PRODUCTS, $product->getCode());
|
||||
|
||||
|
|
@ -702,7 +702,7 @@ final class ManagingProductsContext implements Context
|
|||
ProductAttributeInterface $attribute,
|
||||
ProductInterface $product,
|
||||
string $value,
|
||||
string $localeCode = 'en_US'
|
||||
string $localeCode = 'en_US',
|
||||
): void {
|
||||
$this->client->show(Resources::PRODUCTS, $product->getCode());
|
||||
|
||||
|
|
@ -718,7 +718,7 @@ final class ManagingProductsContext implements Context
|
|||
foreach ($attributes as $attributeValue) {
|
||||
if ($attributeValue['attribute'] === $this->sectionAwareIriConverter->getIriFromResourceInSection($attribute, 'admin')) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Product %s have attribute %s', $product->getName(), $attribute->getName())
|
||||
sprintf('Product %s have attribute %s', $product->getName(), $attribute->getName()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -780,7 +780,7 @@ final class ManagingProductsContext implements Context
|
|||
public function iShouldBeNotifiedThatTheAttributeInShouldBeLongerThan(
|
||||
string $attributeName,
|
||||
string $localeCode,
|
||||
int $number
|
||||
int $number,
|
||||
): void {
|
||||
Assert::contains(
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
|
|
@ -861,7 +861,7 @@ final class ManagingProductsContext implements Context
|
|||
|
||||
private function getAttributeValueInProperType(
|
||||
ProductAttributeInterface $productAttribute,
|
||||
string $value
|
||||
string $value,
|
||||
): string|bool|float|int {
|
||||
switch ($productAttribute->getStorageType()) {
|
||||
case AttributeValueInterface::STORAGE_BOOLEAN:
|
||||
|
|
@ -887,7 +887,7 @@ final class ManagingProductsContext implements Context
|
|||
}
|
||||
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Value "%s" not found in attribute "%s"', $value, $attribute->getName())
|
||||
sprintf('Value "%s" not found in attribute "%s"', $value, $attribute->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -908,7 +908,7 @@ final class ManagingProductsContext implements Context
|
|||
}
|
||||
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('The given product does not have attribute %s', $attribute->getName())
|
||||
sprintf('The given product does not have attribute %s', $attribute->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,22 @@ final class ManagingChannelsBillingDataContext implements Context
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I specify shop billing data for this channel as :company, :street, :postcode, :city, :taxId tax ID and :country country
|
||||
*/
|
||||
public function iSpecifyNewShopBillingDataForChannelAs(
|
||||
string $company,
|
||||
string $street,
|
||||
string $postcode,
|
||||
string $city,
|
||||
string $taxId,
|
||||
CountryInterface $country,
|
||||
): void {
|
||||
$this->shopBillingDataElement->specifyCompany($company);
|
||||
$this->shopBillingDataElement->specifyTaxId($taxId);
|
||||
$this->shopBillingDataElement->specifyBillingAddress($street, $postcode, $city, $country->getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I specify company as :company
|
||||
*/
|
||||
|
|
@ -69,6 +85,7 @@ final class ManagingChannelsBillingDataContext implements Context
|
|||
}
|
||||
|
||||
/**
|
||||
* @Then this channel shop billing address should be :street, :postcode :city and :country country
|
||||
* @Then this channel shop billing address should be :street, :postcode :city, :country
|
||||
*/
|
||||
public function thisChannelShopBillingAddressShouldBe(
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ final class ManagingChannelsContext implements Context
|
|||
* @Given I am modifying a channel :channel
|
||||
* @When I want to modify a channel :channel
|
||||
* @When /^I want to modify (this channel)$/
|
||||
* @When I want to modify a billing data of channel :channel
|
||||
*/
|
||||
public function iWantToModifyChannel(ChannelInterface $channel): void
|
||||
{
|
||||
|
|
@ -315,6 +316,7 @@ final class ManagingChannelsContext implements Context
|
|||
/**
|
||||
* @When I save my changes
|
||||
* @When I try to save my changes
|
||||
* @When I save it
|
||||
*/
|
||||
public function iSaveMyChanges(): void
|
||||
{
|
||||
|
|
@ -332,7 +334,7 @@ final class ManagingChannelsContext implements Context
|
|||
/**
|
||||
* @Then there should still be only one channel with :element :value
|
||||
*/
|
||||
public function thereShouldStillBeOnlyOneChannelWithCode(string $element, string $value)
|
||||
public function thereShouldStillBeOnlyOneChannelWithCode(string $element, string $value): void
|
||||
{
|
||||
$this->iWantToBrowseChannels();
|
||||
|
||||
|
|
@ -375,6 +377,7 @@ final class ManagingChannelsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then the code field should be disabled
|
||||
* @Then I should not be able to edit its code
|
||||
*/
|
||||
public function theCodeFieldShouldBeDisabled(): void
|
||||
{
|
||||
|
|
@ -461,7 +464,7 @@ final class ManagingChannelsContext implements Context
|
|||
/**
|
||||
* @Then paying in :currencyCode should be possible for the :channel channel
|
||||
*/
|
||||
public function payingInEuroShouldBePossibleForTheChannel(string $currencyCode, ChannelInterface $channel): void
|
||||
public function payingInCurrencyShouldBePossibleForTheChannel(string $currencyCode, ChannelInterface $channel): void
|
||||
{
|
||||
$this->updatePage->open(['id' => $channel->getId()]);
|
||||
|
||||
|
|
@ -607,6 +610,7 @@ final class ManagingChannelsContext implements Context
|
|||
|
||||
/**
|
||||
* @Then the base currency field should be disabled
|
||||
* @Then I should not be able to edit its base currency
|
||||
*/
|
||||
public function theBaseCurrencyFieldShouldBeDisabled(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@
|
|||
<!-- <argument type="service" id="sylius.behat.section_iri_converter" />-->
|
||||
<!-- </service>-->
|
||||
|
||||
<service id="Sylius\Behat\Context\Api\Admin\ManagingChannelsBillingDataContext">
|
||||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
<argument type="service" id="api_platform.iri_converter" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.api.admin.managing_countries" class="Sylius\Behat\Context\Api\Admin\ManagingCountriesContext">
|
||||
<argument type="service" id="sylius.behat.api_platform_client.admin" />
|
||||
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ default:
|
|||
- sylius.behat.context.api.admin.managing_channels
|
||||
- sylius.behat.context.api.admin.response
|
||||
- sylius.behat.context.api.admin.save
|
||||
- Sylius\Behat\Context\Api\Admin\ManagingChannelsBillingDataContext
|
||||
- Sylius\Behat\Context\Api\Admin\ManagingChannelPriceHistoryConfigsContext
|
||||
|
||||
filters:
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\AddressingBundle\DependencyInjection;
|
||||
|
||||
use Sylius\Bundle\AddressingBundle\Controller\ProvinceController;
|
||||
use Sylius\Bundle\AddressingBundle\Doctrine\ORM\AddressRepository;
|
||||
use Sylius\Bundle\AddressingBundle\Doctrine\ORM\CountryRepository;
|
||||
use Sylius\Bundle\AddressingBundle\Doctrine\ORM\ProvinceRepository;
|
||||
use Sylius\Bundle\AddressingBundle\Doctrine\ORM\ZoneMemberRepository;
|
||||
use Sylius\Bundle\AddressingBundle\Form\Type\AddressType;
|
||||
use Sylius\Bundle\AddressingBundle\Form\Type\CountryType;
|
||||
use Sylius\Bundle\AddressingBundle\Form\Type\ProvinceType;
|
||||
|
|
@ -77,7 +81,7 @@ final class Configuration implements ConfigurationInterface
|
|||
->scalarNode('model')->defaultValue(Address::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('interface')->defaultValue(AddressInterface::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->defaultValue(AddressRepository::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('factory')->defaultValue(Factory::class)->end()
|
||||
->scalarNode('form')->defaultValue(AddressType::class)->cannotBeEmpty()->end()
|
||||
->end()
|
||||
|
|
@ -109,7 +113,7 @@ final class Configuration implements ConfigurationInterface
|
|||
->scalarNode('model')->defaultValue(Country::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('interface')->defaultValue(CountryInterface::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->defaultValue(CountryRepository::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('factory')->defaultValue(Factory::class)->end()
|
||||
->scalarNode('form')->defaultValue(CountryType::class)->cannotBeEmpty()->end()
|
||||
->end()
|
||||
|
|
@ -126,7 +130,7 @@ final class Configuration implements ConfigurationInterface
|
|||
->scalarNode('model')->defaultValue(Province::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('interface')->defaultValue(ProvinceInterface::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('controller')->defaultValue(ProvinceController::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->defaultValue(ProvinceRepository::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('factory')->defaultValue(Factory::class)->end()
|
||||
->scalarNode('form')->defaultValue(ProvinceType::class)->cannotBeEmpty()->end()
|
||||
->end()
|
||||
|
|
@ -160,7 +164,7 @@ final class Configuration implements ConfigurationInterface
|
|||
->scalarNode('model')->defaultValue(ZoneMember::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('interface')->defaultValue(ZoneMemberInterface::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->cannotBeEmpty()->end()
|
||||
->scalarNode('repository')->defaultValue(ZoneMemberRepository::class)->cannotBeEmpty()->end()
|
||||
->scalarNode('factory')->defaultValue(Factory::class)->end()
|
||||
->scalarNode('form')->defaultValue(ZoneMemberType::class)->cannotBeEmpty()->end()
|
||||
->end()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\AddressingBundle\Doctrine\ORM;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
|
||||
use Sylius\Component\Addressing\Model\AddressInterface;
|
||||
use Sylius\Component\Addressing\Repository\AddressRepositoryInterface;
|
||||
|
||||
/**
|
||||
* @implements AddressRepositoryInterface<AddressInterface>
|
||||
*/
|
||||
class AddressRepository extends EntityRepository implements AddressRepositoryInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\AddressingBundle\Doctrine\ORM;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
|
||||
use Sylius\Component\Addressing\Model\CountryInterface;
|
||||
use Sylius\Component\Addressing\Repository\CountryRepositoryInterface;
|
||||
|
||||
/**
|
||||
* @implements CountryRepositoryInterface<CountryInterface>
|
||||
*/
|
||||
class CountryRepository extends EntityRepository implements CountryRepositoryInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\AddressingBundle\Doctrine\ORM;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
|
||||
use Sylius\Component\Addressing\Model\ProvinceInterface;
|
||||
use Sylius\Component\Addressing\Repository\ProvinceRepositoryInterface;
|
||||
|
||||
/**
|
||||
* @implements ProvinceRepositoryInterface<ProvinceInterface>
|
||||
*/
|
||||
class ProvinceRepository extends EntityRepository implements ProvinceRepositoryInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\AddressingBundle\Doctrine\ORM;
|
||||
|
||||
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
|
||||
use Sylius\Component\Addressing\Model\ZoneMemberInterface;
|
||||
use Sylius\Component\Addressing\Repository\ZoneMemberRepositoryInterface;
|
||||
|
||||
/**
|
||||
* @implements ZoneMemberRepositoryInterface<ZoneMemberInterface>
|
||||
*/
|
||||
class ZoneMemberRepository extends EntityRepository implements ZoneMemberRepositoryInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ final readonly class RenderResetPasswordPageAction
|
|||
->add('error', 'sylius.admin.password_reset.token_expired')
|
||||
;
|
||||
|
||||
$attributes = $request->attributes->get('_sylius');
|
||||
$attributes = $request->attributes->get('_sylius', []);
|
||||
$redirect = $attributes['redirect'] ?? 'sylius_admin_login';
|
||||
|
||||
if (is_array($redirect)) {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ final readonly class RequestPasswordResetAction
|
|||
->add('success', 'sylius.admin.request_reset_password.success')
|
||||
;
|
||||
|
||||
$options = $request->attributes->get('_sylius');
|
||||
$options = $request->attributes->get('_sylius', []);
|
||||
$redirectRoute = $options['redirect'] ?? 'sylius_admin_login';
|
||||
|
||||
if (is_array($redirectRoute)) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ final class ResetPasswordAction
|
|||
->add('success', 'sylius.admin.password_reset.success')
|
||||
;
|
||||
|
||||
$attributes = $request->attributes->get('_sylius');
|
||||
$attributes = $request->attributes->get('_sylius', []);
|
||||
$redirect = $attributes['redirect'] ?? 'sylius_admin_login';
|
||||
|
||||
if (is_array($redirect)) {
|
||||
|
|
|
|||
|
|
@ -34,9 +34,11 @@ final class RemoveAvatarAction
|
|||
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$userId = $request->attributes->get('id');
|
||||
$userId = $request->attributes->get('id', '');
|
||||
|
||||
if (!$this->csrfTokenManager->isTokenValid(new CsrfToken($userId, (string) $request->query->get('_csrf_token')))) {
|
||||
if (!$this->csrfTokenManager->isTokenValid(
|
||||
new CsrfToken($userId, (string) $request->query->get('_csrf_token', '')),
|
||||
)) {
|
||||
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,11 @@ final readonly class ResendOrderConfirmationEmailAction
|
|||
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$orderId = $request->attributes->get('id');
|
||||
$orderId = $request->attributes->get('id', '');
|
||||
|
||||
if (!$this->csrfTokenManager->isTokenValid(new CsrfToken($orderId, (string) $request->query->get('_csrf_token')))) {
|
||||
if (!$this->csrfTokenManager->isTokenValid(
|
||||
new CsrfToken($orderId, (string) $request->query->get('_csrf_token', '')),
|
||||
)) {
|
||||
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,11 @@ final readonly class ResendShipmentConfirmationEmailAction
|
|||
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$shipmentId = $request->attributes->get('id');
|
||||
$shipmentId = $request->attributes->get('id', '');
|
||||
|
||||
if (!$this->csrfTokenManager->isTokenValid(new CsrfToken($shipmentId, (string) $request->query->get('_csrf_token')))) {
|
||||
if (!$this->csrfTokenManager->isTokenValid(
|
||||
new CsrfToken($shipmentId, (string) $request->query->get('_csrf_token', '')),
|
||||
)) {
|
||||
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,13 @@ final class RemoveCatalogPromotionAction
|
|||
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$catalogPromotionCode = $request->attributes->get('code');
|
||||
if (null === $catalogPromotionCode) {
|
||||
throw new NotFoundHttpException('The catalog promotion has not been found');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->catalogPromotionRemovalProcessor->removeCatalogPromotion($request->attributes->get('code'));
|
||||
$this->catalogPromotionRemovalProcessor->removeCatalogPromotion($catalogPromotionCode);
|
||||
|
||||
/** @var Session $session */
|
||||
$session = $request->getSession();
|
||||
|
|
|
|||
|
|
@ -44,17 +44,12 @@ final class AdminFilterSubscriber implements EventSubscriberInterface
|
|||
}
|
||||
|
||||
$requestAttributes = $eventRequest->attributes;
|
||||
$originalRoute = $requestAttributes->get('_route');
|
||||
|
||||
if (!$this->isIndexResourceRoute($originalRoute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isAdminSection($requestAttributes->get('_sylius', []))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $requestAttributes->get('_controller')) {
|
||||
if (
|
||||
null === $requestAttributes->get('_controller') ||
|
||||
!$this->isIndexResourceRoute($requestAttributes->get('_route', '')) ||
|
||||
!$this->isAdminSection($requestAttributes->get('_sylius', []))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +75,6 @@ final class AdminFilterSubscriber implements EventSubscriberInterface
|
|||
|
||||
private function isAdminSection(array $syliusParameters): bool
|
||||
{
|
||||
return array_key_exists('section', $syliusParameters) && 'admin' === $syliusParameters['section'];
|
||||
return isset($syliusParameters['section']) && 'admin' === $syliusParameters['section'];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ final readonly class ResourceDeleteSubscriber implements EventSubscriberInterfac
|
|||
|
||||
$eventRequest = $event->getRequest();
|
||||
$requestAttributes = $eventRequest->attributes;
|
||||
$originalRoute = $requestAttributes->get('_route');
|
||||
$originalRoute = $requestAttributes->get('_route', '');
|
||||
|
||||
if (!$this->isMethodDelete($eventRequest) ||
|
||||
!$this->isSyliusRoute($originalRoute) ||
|
||||
|
|
|
|||
|
|
@ -16,24 +16,16 @@ namespace Sylius\Bundle\AdminBundle\Tests\MessageHandler\Admin;
|
|||
use Sylius\Bundle\CoreBundle\Message\Admin\Account\SendResetPasswordEmail;
|
||||
use Sylius\Bundle\CoreBundle\MessageHandler\Admin\Account\SendResetPasswordEmailHandler;
|
||||
use Sylius\Component\Core\Model\AdminUser;
|
||||
use Sylius\Component\Core\Test\SwiftmailerAssertionTrait;
|
||||
use Sylius\Component\Mailer\Sender\SenderInterface;
|
||||
use Sylius\Component\User\Repository\UserRepositoryInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final class SendResetPasswordEmailHandlerTest extends KernelTestCase
|
||||
{
|
||||
use SwiftmailerAssertionTrait;
|
||||
|
||||
/** @test */
|
||||
public function it_sends_password_reset_token_email(): void
|
||||
{
|
||||
if (self::isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment without swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::bootKernel()->getContainer();
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
|
|
@ -67,57 +59,4 @@ final class SendResetPasswordEmailHandlerTest extends KernelTestCase
|
|||
$translator->trans('sylius.email.admin_password_reset.to_reset_your_password', [], null, 'en_US'),
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_sends_password_reset_token_email_with_swiftmailer(): void
|
||||
{
|
||||
if (!self::isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment with swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::bootKernel()->getContainer();
|
||||
|
||||
self::setSpoolDirectory($container->getParameter('kernel.cache_dir') . '/spool');
|
||||
|
||||
/** @var Filesystem $filesystem */
|
||||
$filesystem = $container->get('test.filesystem.public');
|
||||
|
||||
$filesystem->remove(self::getSpoolDirectory());
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
$translator = $container->get('translator');
|
||||
|
||||
/** @var SenderInterface $emailSender */
|
||||
$emailSender = $container->get('sylius.email_sender');
|
||||
|
||||
$adminUser = new AdminUser();
|
||||
$adminUser->setEmail('sylius@example.com');
|
||||
$adminUser->setPasswordResetToken('my_reset_token');
|
||||
|
||||
$adminUserRepository = $this->createMock(UserRepositoryInterface::class);
|
||||
$adminUserRepository
|
||||
->method('findOneByEmail')
|
||||
->with('sylius@example.com')
|
||||
->willReturn($adminUser)
|
||||
;
|
||||
|
||||
$resetPasswordEmailHandler = new SendResetPasswordEmailHandler($adminUserRepository, $emailSender);
|
||||
$resetPasswordEmailHandler(new SendResetPasswordEmail(
|
||||
'sylius@example.com',
|
||||
'en_US',
|
||||
));
|
||||
|
||||
self::assertSpooledMessagesCountWithRecipient(1, 'sylius@example.com');
|
||||
self::assertSpooledMessageWithContentHasRecipient(
|
||||
$translator->trans('sylius.email.admin_password_reset.to_reset_your_password', [], null, 'en_US'),
|
||||
'sylius@example.com',
|
||||
);
|
||||
}
|
||||
|
||||
private static function isItSwiftmailerTestEnv(): bool
|
||||
{
|
||||
$env = self::getContainer()->getParameter('kernel.environment');
|
||||
|
||||
return $env === 'test_with_swiftmailer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ final class RequestPasswordResetActionSpec extends ObjectBehavior
|
|||
|
||||
$flashBag->add('success', 'sylius.admin.request_reset_password.success')->shouldBeCalled();
|
||||
|
||||
$attributesBag->get('_sylius')->shouldBeCalled()->willReturn([
|
||||
$attributesBag->get('_sylius', [])->shouldBeCalled()->willReturn([
|
||||
'redirect' => 'my_custom_route',
|
||||
]);
|
||||
$request->attributes = $attributesBag->getWrappedObject();
|
||||
|
|
@ -120,7 +120,7 @@ final class RequestPasswordResetActionSpec extends ObjectBehavior
|
|||
$parameters = [
|
||||
'my_parameter' => 'my_value',
|
||||
];
|
||||
$attributesBag->get('_sylius')->shouldBeCalled()->willReturn([
|
||||
$attributesBag->get('_sylius', [])->shouldBeCalled()->willReturn([
|
||||
'redirect' => [
|
||||
'route' => $route,
|
||||
'params' => $parameters,
|
||||
|
|
@ -158,7 +158,7 @@ final class RequestPasswordResetActionSpec extends ObjectBehavior
|
|||
|
||||
$flashBag->add('success', 'sylius.admin.request_reset_password.success')->shouldBeCalled();
|
||||
|
||||
$attributesBag->get('_sylius')->shouldBeCalled()->willReturn(null);
|
||||
$attributesBag->get('_sylius', [])->shouldBeCalled()->willReturn([]);
|
||||
$request->attributes = $attributesBag->getWrappedObject();
|
||||
|
||||
$router->generate('sylius_admin_login')->shouldBeCalled()->willReturn('/login');
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ final class AdminFilterSubscriberSpec extends ObjectBehavior
|
|||
$event->isMainRequest()->willReturn(true);
|
||||
$request->getRequestFormat()->willReturn('html');
|
||||
|
||||
$attributes->get('_route')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_route', '')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_sylius', [])->willReturn(['section' => 'admin']);
|
||||
$attributes->get('_controller')->willReturn('Sylius\Bundle\AdminBundle\Controller\ProductController::indexAction');
|
||||
$request->attributes = $attributes;
|
||||
|
|
@ -82,7 +82,7 @@ final class AdminFilterSubscriberSpec extends ObjectBehavior
|
|||
$event->isMainRequest()->willReturn(true);
|
||||
$request->getRequestFormat()->willReturn('json');
|
||||
|
||||
$attributes->get('_route')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_route', '')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_sylius', [])->willReturn(['section' => 'admin']);
|
||||
$attributes->get('_controller')->willReturn('Sylius\Bundle\AdminBundle\Controller\ProductController::indexAction');
|
||||
$request->attributes = $attributes;
|
||||
|
|
@ -109,7 +109,7 @@ final class AdminFilterSubscriberSpec extends ObjectBehavior
|
|||
$event->isMainRequest()->willReturn(true);
|
||||
$request->getRequestFormat()->willReturn('json');
|
||||
|
||||
$attributes->get('_route')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_route', '')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_sylius', [])->willReturn(['section' => 'shop']);
|
||||
$attributes->get('_controller')->willReturn('Sylius\Bundle\AdminBundle\Controller\ProductController::indexAction');
|
||||
$request->attributes = $attributes;
|
||||
|
|
@ -136,7 +136,7 @@ final class AdminFilterSubscriberSpec extends ObjectBehavior
|
|||
$event->isMainRequest()->willReturn(true);
|
||||
$request->getRequestFormat()->willReturn('json');
|
||||
|
||||
$attributes->get('_route')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_route', '')->willReturn('sylius_admin_product_index');
|
||||
$attributes->get('_sylius', [])->willReturn(['section' => 'shop']);
|
||||
$attributes->get('_controller')->willReturn(null);
|
||||
$request->attributes = $attributes;
|
||||
|
|
@ -153,6 +153,33 @@ final class AdminFilterSubscriberSpec extends ObjectBehavior
|
|||
$this->onKernelRequest($event);
|
||||
}
|
||||
|
||||
function it_does_not_add_filter_to_filter_storage_if_route_is_missing(
|
||||
RequestEvent $event,
|
||||
Request $request,
|
||||
ParameterBag $attributes,
|
||||
ParameterBag $query,
|
||||
FilterStorageInterface $filterStorage,
|
||||
): void {
|
||||
$event->isMainRequest()->willReturn(true);
|
||||
$request->getRequestFormat()->willReturn('json');
|
||||
|
||||
$attributes->get('_route', '')->willReturn('');
|
||||
$attributes->get('_sylius', [])->willReturn(['section' => 'shop']);
|
||||
$attributes->get('_controller')->willReturn('Sylius\Bundle\AdminBundle\Controller\ProductController::indexAction');
|
||||
$request->attributes = $attributes;
|
||||
|
||||
$query->all()->willReturn(['filter' => 'foo']);
|
||||
$request->query = $query;
|
||||
|
||||
$filterStorage->all()->willReturn([]);
|
||||
|
||||
$event->getRequest()->willReturn($request);
|
||||
|
||||
$filterStorage->set(Argument::any())->shouldNotBeCalled();
|
||||
|
||||
$this->onKernelRequest($event);
|
||||
}
|
||||
|
||||
function it_does_not_add_filter_to_filter_storage_if_it_is_not_a_index_resource_route(
|
||||
RequestEvent $event,
|
||||
Request $request,
|
||||
|
|
@ -163,7 +190,7 @@ final class AdminFilterSubscriberSpec extends ObjectBehavior
|
|||
$event->isMainRequest()->willReturn(true);
|
||||
$request->getRequestFormat()->willReturn('json');
|
||||
|
||||
$attributes->get('_route')->willReturn('sylius_admin_product_update');
|
||||
$attributes->get('_route', '')->willReturn('sylius_admin_product_update');
|
||||
$attributes->get('_sylius', [])->willReturn(['section' => 'shop']);
|
||||
$attributes->get('_controller')->willReturn('Sylius\Bundle\AdminBundle\Controller\ProductController::indexAction');
|
||||
$request->attributes = $attributes;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ declare(strict_types=1);
|
|||
return [
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['test_with_swiftmailer' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
services:
|
||||
test.filesystem.public:
|
||||
alias: filesystem
|
||||
public: true
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
swiftmailer:
|
||||
disable_delivery: true
|
||||
logging: true
|
||||
spool:
|
||||
type: file
|
||||
path: "%kernel.cache_dir%/spool"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
sylius_mailer:
|
||||
sender_adapter: sylius.email_sender.adapter.swiftmailer
|
||||
|
|
@ -33,9 +33,11 @@ final readonly class DeleteOrderItemAction
|
|||
{
|
||||
$orderItemId = $request->attributes->get('itemId');
|
||||
$tokenValue = $request->attributes->get('tokenValue');
|
||||
if (null === $orderItemId || null === $tokenValue) {
|
||||
throw new OrderItemNotFoundException();
|
||||
}
|
||||
|
||||
$orderItem = $this->orderItemRepository->findOneByIdAndCartTokenValue($orderItemId, $tokenValue);
|
||||
|
||||
if ($orderItem === null) {
|
||||
throw new OrderItemNotFoundException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,14 @@ final readonly class GetPaymentConfiguration
|
|||
|
||||
public function __invoke(Request $request): JsonResponse
|
||||
{
|
||||
$paymentId = $request->attributes->get('paymentId');
|
||||
$tokenValue = $request->attributes->get('tokenValue');
|
||||
if (null === $paymentId || null === $tokenValue) {
|
||||
throw new PaymentNotFoundException();
|
||||
}
|
||||
|
||||
/** @var PaymentInterface|null $payment */
|
||||
$payment = $this->paymentRepository->findOneByOrderToken(
|
||||
$request->attributes->get('paymentId'),
|
||||
$request->attributes->get('tokenValue'),
|
||||
);
|
||||
$payment = $this->paymentRepository->findOneByOrderToken($paymentId, $tokenValue);
|
||||
|
||||
if ($payment === null) {
|
||||
throw new PaymentNotFoundException();
|
||||
|
|
|
|||
|
|
@ -30,8 +30,13 @@ final readonly class RemoveCatalogPromotionAction
|
|||
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$catalogPromotionCode = $request->attributes->get('code');
|
||||
if (null === $catalogPromotionCode) {
|
||||
return new JsonResponse(status: Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->catalogPromotionRemovalProcessor->removeCatalogPromotion($request->attributes->get('code'));
|
||||
$this->catalogPromotionRemovalProcessor->removeCatalogPromotion($catalogPromotionCode);
|
||||
|
||||
return new Response(status: Response::HTTP_ACCEPTED);
|
||||
} catch (CatalogPromotionNotFoundException) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\ApiBundle\DataPersister;
|
||||
|
||||
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
|
||||
use Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved;
|
||||
use Sylius\Component\Channel\Checker\ChannelDeletionCheckerInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
|
||||
final class ChannelDataPersister implements ContextAwareDataPersisterInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
private ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function supports($data, array $context = []): bool
|
||||
{
|
||||
return $data instanceof ChannelInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function persist($data, array $context = [])
|
||||
{
|
||||
return $this->decoratedDataPersister->persist($data, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
public function remove($data, array $context = []): void
|
||||
{
|
||||
if (!$this->channelDeletionChecker->isDeletable($data)) {
|
||||
throw new ChannelCannotBeRemoved('The channel cannot be deleted. At least one enabled channel is required.');
|
||||
}
|
||||
|
||||
$this->decoratedDataPersister->remove($data, $context);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\ApiBundle\DataPersister;
|
||||
|
||||
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
|
||||
use ApiPlatform\Core\DataPersister\ResumableDataPersisterInterface;
|
||||
use Sylius\Bundle\ApiBundle\Exception\TranslationInDefaultLocaleCannotBeRemoved;
|
||||
use Sylius\Component\Resource\Model\TranslatableInterface;
|
||||
use Sylius\Component\Resource\Translation\Provider\TranslationLocaleProviderInterface;
|
||||
|
||||
final class TranslatableDataPersister implements ContextAwareDataPersisterInterface, ResumableDataPersisterInterface
|
||||
{
|
||||
public function __construct(private TranslationLocaleProviderInterface $localeProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $context
|
||||
*/
|
||||
public function supports($data, array $context = []): bool
|
||||
{
|
||||
return $data instanceof TranslatableInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TranslatableInterface $data
|
||||
* @param array<array-key, mixed> $context
|
||||
*/
|
||||
public function persist($data, array $context = []): object
|
||||
{
|
||||
$defaultLocaleCode = $this->localeProvider->getDefaultLocaleCode();
|
||||
|
||||
if (!$data->getTranslations()->containsKey($defaultLocaleCode)) {
|
||||
throw new TranslationInDefaultLocaleCannotBeRemoved(
|
||||
sprintf('Translation in the default locale "%s" cannot be removed.', $defaultLocaleCode),
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TranslatableInterface $data
|
||||
* @param array<array-key, mixed> $context
|
||||
*/
|
||||
public function remove($data, array $context = []): mixed
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/** @param array<array-key, mixed> $context */
|
||||
public function resumable(array $context = []): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\ApiBundle\Exception;
|
||||
|
||||
/** @experimental */
|
||||
final class ChannelCannotBeRemoved extends \RuntimeException
|
||||
{
|
||||
public function __construct(
|
||||
string $message = 'Cannot remove, the channel is in use.',
|
||||
int $code = 0,
|
||||
\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\ApiBundle\Exception;
|
||||
|
||||
/** @experimental */
|
||||
final class TranslationInDefaultLocaleCannotBeRemoved extends \RuntimeException
|
||||
{
|
||||
public function __construct(
|
||||
string $message = 'Translation in the default locale cannot be removed.',
|
||||
int $code = 0,
|
||||
\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
|
@ -51,6 +51,7 @@ api_platform:
|
|||
# Sylius exception to status code mapping
|
||||
SM\SMException: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\CannotRemoveCurrentlyLoggedInUser: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\InvalidProductAttributeValueTypeException: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\OrderItemNotFoundException: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\OrderNoLongerEligibleForPromotion: 422
|
||||
|
|
@ -60,6 +61,7 @@ api_platform:
|
|||
Sylius\Bundle\ApiBundle\Exception\ProvinceCannotBeRemoved: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\ShippingMethodCannotBeRemoved: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\TaxonCannotBeRemoved: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\TranslationInDefaultLocaleCannotBeRemoved: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\ZoneCannotBeRemoved: 422
|
||||
Sylius\Bundle\ApiBundle\Exception\CannotRemoveMenuTaxonException: 409
|
||||
Sylius\Bundle\LocaleBundle\Checker\Exception\LocaleIsUsedException: 422
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@
|
|||
</attribute>
|
||||
<attribute name="validation_groups">sylius</attribute>
|
||||
</itemOperation>
|
||||
|
||||
<itemOperation name="admin_delete">
|
||||
<attribute name="method">DELETE</attribute>
|
||||
<attribute name="path">/admin/channels/{code}</attribute>
|
||||
</itemOperation>
|
||||
</itemOperations>
|
||||
|
||||
<subresourceOperations>
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@
|
|||
<attribute name="en_US">
|
||||
<attribute name="name">string</attribute>
|
||||
<attribute name="slug">string</attribute>
|
||||
<attribute name="locale">string</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@
|
|||
<attribute name="en_US">
|
||||
<attribute name="name">string</attribute>
|
||||
<attribute name="description">string</attribute>
|
||||
<attribute name="locale">string</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@
|
|||
<attribute name="example">
|
||||
<attribute name="en_US">
|
||||
<attribute name="name">string</attribute>
|
||||
<attribute name="locale">string</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@
|
|||
<attribute name="example">
|
||||
<attribute name="en_US">
|
||||
<attribute name="name">string</attribute>
|
||||
<attribute name="locale">string</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -140,7 +140,6 @@
|
|||
<attribute name="en_US">
|
||||
<attribute name="name">string</attribute>
|
||||
<attribute name="description">string</attribute>
|
||||
<attribute name="locale">string</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@
|
|||
<attribute name="en_US">
|
||||
<attribute name="name">string</attribute>
|
||||
<attribute name="slug">string</attribute>
|
||||
<attribute name="locale">string</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -24,23 +24,28 @@
|
|||
<attribute name="name">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>shop:channel:read</group>
|
||||
</attribute>
|
||||
<attribute name="enabled">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="description">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="hostname">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="color">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="baseCurrency">
|
||||
<group>admin:channel:read</group>
|
||||
|
|
@ -50,53 +55,65 @@
|
|||
<attribute name="defaultLocale">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>shop:channel:read</group>
|
||||
</attribute>
|
||||
<attribute name="defaultTaxZone">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="taxCalculationStrategy">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="currencies">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>shop:channel:read</group>
|
||||
</attribute>
|
||||
<attribute name="locales">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>shop:channel:read</group>
|
||||
</attribute>
|
||||
<attribute name="countries">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="themeName">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="contactEmail">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="contactPhoneNumber">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="skippingShippingStepAllowed">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="skippingPaymentStepAllowed">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="accountVerificationRequired">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="shippingAddressInCheckoutRequired">
|
||||
<group>admin:channel:read</group>
|
||||
|
|
@ -106,14 +123,17 @@
|
|||
<attribute name="shopBillingData">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="menuTaxon">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
<attribute name="channelPriceHistoryConfig">
|
||||
<group>admin:channel:read</group>
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
</attribute>
|
||||
</class>
|
||||
</serializer>
|
||||
|
|
|
|||
|
|
@ -21,26 +21,32 @@
|
|||
</attribute>
|
||||
<attribute name="company">
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>admin:shop_billing_data:read</group>
|
||||
</attribute>
|
||||
<attribute name="taxId">
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>admin:shop_billing_data:read</group>
|
||||
</attribute>
|
||||
<attribute name="countryCode">
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>admin:shop_billing_data:read</group>
|
||||
</attribute>
|
||||
<attribute name="street">
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>admin:shop_billing_data:read</group>
|
||||
</attribute>
|
||||
<attribute name="city">
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>admin:shop_billing_data:read</group>
|
||||
</attribute>
|
||||
<attribute name="postcode">
|
||||
<group>admin:channel:create</group>
|
||||
<group>admin:channel:update</group>
|
||||
<group>admin:shop_billing_data:read</group>
|
||||
</attribute>
|
||||
</class>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@
|
|||
<tag name="api_platform.data_persister" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\DataPersister\ChannelDataPersister">
|
||||
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
|
||||
<argument type="service" id="sylius.checker.channel_deletion" />
|
||||
<tag name="api_platform.data_persister" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\DataPersister\MessengerDataPersister">
|
||||
<argument type="service" id="api_platform.messenger.data_persister" />
|
||||
<tag name="api_platform.data_persister" />
|
||||
|
|
@ -74,6 +80,11 @@
|
|||
<tag name="api_platform.data_persister" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\DataPersister\TranslatableDataPersister">
|
||||
<argument type="service" id="sylius.translation_locale_provider" />
|
||||
<tag name="api_platform.data_persister" priority="128" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\DataPersister\ZoneDataPersister">
|
||||
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
|
||||
<argument type="service" id="Sylius\Component\Addressing\Checker\ZoneDeletionCheckerInterface" />
|
||||
|
|
|
|||
|
|
@ -105,6 +105,11 @@
|
|||
<!-- <tag name="serializer.normalizer" priority="64" />-->
|
||||
<!-- </service>-->
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\Serializer\TranslatableDenormalizer">
|
||||
<argument type="service" id="sylius.translation_locale_provider" />
|
||||
<tag name="serializer.normalizer" priority="64" />
|
||||
</service>
|
||||
|
||||
<service id="date_time_normalizer" class="Symfony\Component\Serializer\Normalizer\DateTimeNormalizer">
|
||||
<argument type="collection">
|
||||
<argument key="datetime_format">Y-m-d H:i:s</argument>
|
||||
|
|
@ -122,12 +127,17 @@
|
|||
|
||||
<!-- <service id="Sylius\Bundle\ApiBundle\Serializer\ChannelDenormalizer">-->
|
||||
<!-- <argument type="service" id="sylius.factory.channel_price_history_config" />-->
|
||||
<!-- <argument type="service" id="sylius.factory.shop_billing_data" />-->
|
||||
<!-- <tag name="serializer.normalizer" priority="64" />-->
|
||||
<!-- </service>-->
|
||||
|
||||
<!-- <service id="Sylius\Bundle\ApiBundle\Serializer\CustomerDenormalizer">-->
|
||||
<!-- <argument type="service" id="clock" />-->
|
||||
<!-- <tag name="serializer.normalizer" priority="64" />-->
|
||||
<!-- </service>-->
|
||||
|
||||
<!-- <service id="Sylius\Bundle\ApiBundle\Serializer\TranslatableLocaleKeyDenormalizer">-->
|
||||
<!-- <tag name="serializer.normalizer" priority="96" />-->
|
||||
<!-- </service>-->
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
This file is part of the Sylius package.
|
||||
|
||||
(c) Sylius Sp. z o.o.
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
|
||||
-->
|
||||
|
||||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd">
|
||||
<class name="Sylius\Component\Core\Model\Channel">
|
||||
<property name="shopBillingData">
|
||||
<constraint name="Valid">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
This file is part of the Sylius package.
|
||||
|
||||
(c) Sylius Sp. z o.o.
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
|
||||
-->
|
||||
|
||||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd">
|
||||
<class name="Sylius\Component\Core\Model\ShopBillingData">
|
||||
<property name="countryCode">
|
||||
<constraint name="Country">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
<constraint name="NotBlank" >
|
||||
<option name="allowNull">true</option>
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
@ -15,6 +15,7 @@ namespace Sylius\Bundle\ApiBundle\Serializer;
|
|||
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ChannelPriceHistoryConfigInterface;
|
||||
use Sylius\Component\Core\Model\ShopBillingDataInterface;
|
||||
use Sylius\Component\Resource\Factory\FactoryInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
|
|
@ -28,8 +29,14 @@ final class ChannelDenormalizer implements ContextAwareDenormalizerInterface, De
|
|||
|
||||
private const ALREADY_CALLED = 'sylius_channel_denormalizer_already_called';
|
||||
|
||||
public function __construct(private FactoryInterface $channelPriceHistoryConfigFactory)
|
||||
{
|
||||
/**
|
||||
* @param FactoryInterface<ChannelPriceHistoryConfigInterface> $channelPriceHistoryConfigFactory
|
||||
* @param FactoryInterface<ShopBillingDataInterface> $shopBillingDataFactory
|
||||
*/
|
||||
public function __construct(
|
||||
private FactoryInterface $channelPriceHistoryConfigFactory,
|
||||
private FactoryInterface $shopBillingDataFactory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
|
||||
|
|
@ -54,6 +61,12 @@ final class ChannelDenormalizer implements ContextAwareDenormalizerInterface, De
|
|||
$channel->setChannelPriceHistoryConfig($channelPriceHistoryConfig);
|
||||
}
|
||||
|
||||
if (null === $channel->getShopBillingData()) {
|
||||
/** @var ShopBillingDataInterface $shopBillingData */
|
||||
$shopBillingData = $this->shopBillingDataFactory->createNew();
|
||||
$channel->setShopBillingData($shopBillingData);
|
||||
}
|
||||
|
||||
return $channel;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\ApiBundle\Serializer;
|
||||
|
||||
use Sylius\Component\Resource\Model\TranslatableInterface;
|
||||
use Sylius\Component\Resource\Translation\Provider\TranslationLocaleProviderInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
|
||||
|
||||
/** @experimental */
|
||||
final class TranslatableDenormalizer implements ContextAwareDenormalizerInterface, DenormalizerAwareInterface
|
||||
{
|
||||
use DenormalizerAwareTrait;
|
||||
|
||||
private const ALREADY_CALLED = 'sylius_translatable_denormalizer_already_called_for_%s';
|
||||
|
||||
public function __construct(
|
||||
private TranslationLocaleProviderInterface $localeProvider,
|
||||
) {
|
||||
}
|
||||
|
||||
public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
|
||||
{
|
||||
$context[self::getAlreadyCalledKey($type)] = true;
|
||||
|
||||
$defaultLocaleCode = $this->localeProvider->getDefaultLocaleCode();
|
||||
|
||||
if (!$this->hasDefaultTranslation($data['translations'] ?? [], $defaultLocaleCode)) {
|
||||
$data['translations'][$defaultLocaleCode] = [
|
||||
'locale' => $defaultLocaleCode,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->denormalizer->denormalize($data, $type, $format, $context);
|
||||
}
|
||||
|
||||
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
|
||||
{
|
||||
return
|
||||
Request::METHOD_POST === ($context[ContextKeys::HTTP_REQUEST_METHOD_TYPE] ?? null) &&
|
||||
!isset($context[self::getAlreadyCalledKey($type)]) &&
|
||||
is_a($type, TranslatableInterface::class, true)
|
||||
;
|
||||
}
|
||||
|
||||
private static function getAlreadyCalledKey(string $class): string
|
||||
{
|
||||
return sprintf(self::ALREADY_CALLED, $class);
|
||||
}
|
||||
|
||||
private function hasDefaultTranslation(array $translations, string $defaultLocale): bool
|
||||
{
|
||||
return
|
||||
isset($translations[$defaultLocale]['locale']) &&
|
||||
$defaultLocale === $translations[$defaultLocale]['locale']
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\ApiBundle\Serializer;
|
||||
|
||||
use Sylius\Component\Resource\Model\TranslatableInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
|
||||
|
||||
/** @experimental */
|
||||
final class TranslatableLocaleKeyDenormalizer implements ContextAwareDenormalizerInterface, DenormalizerAwareInterface
|
||||
{
|
||||
use DenormalizerAwareTrait;
|
||||
|
||||
private const ALREADY_CALLED = 'sylius_translatable_locale_key_denormalizer_already_called';
|
||||
|
||||
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
|
||||
{
|
||||
return
|
||||
!isset($context[self::ALREADY_CALLED]) &&
|
||||
is_array($data) &&
|
||||
is_a($type, TranslatableInterface::class, true)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array{ translations: array<mixed> }> $data
|
||||
*/
|
||||
public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
|
||||
{
|
||||
$context[self::ALREADY_CALLED] = true;
|
||||
|
||||
if (array_key_exists('translations', $data)) {
|
||||
foreach ($data['translations'] as $key => &$translation) {
|
||||
$translation['locale'] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->denormalizer->denormalize($data, $type, $format, $context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
APP_ENV=test_with_swiftmailer
|
||||
|
|
@ -13,7 +13,6 @@ declare(strict_types=1);
|
|||
|
||||
return [
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['test_with_swiftmailer' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
|
|
@ -54,9 +53,9 @@ return [
|
|||
Sylius\Bundle\PayumBundle\SyliusPayumBundle::class => ['all' => true],
|
||||
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
|
||||
Sylius\Bundle\ApiBundle\SyliusApiBundle::class => ['all' => true],
|
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true, 'test_with_swiftmailer' => true],
|
||||
Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true, 'test_with_swiftmailer' => true],
|
||||
Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true, 'test_with_swiftmailer' => true],
|
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true],
|
||||
Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true],
|
||||
Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true],
|
||||
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
|
||||
SyliusLabs\DoctrineMigrationsExtraBundle\SyliusLabsDoctrineMigrationsExtraBundle::class => ['all' => true],
|
||||
SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true],
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
services:
|
||||
test.filesystem.public:
|
||||
alias: filesystem
|
||||
public: true
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
swiftmailer:
|
||||
disable_delivery: true
|
||||
logging: true
|
||||
spool:
|
||||
type: file
|
||||
path: "%kernel.cache_dir%/spool"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
sylius_mailer:
|
||||
sender_adapter: sylius.email_sender.adapter.swiftmailer
|
||||
|
|
@ -19,25 +19,18 @@ use Sylius\Bundle\ApiBundle\Command\Account\SendAccountRegistrationEmail;
|
|||
use Sylius\Bundle\ApiBundle\CommandHandler\Account\SendAccountRegistrationEmailHandler;
|
||||
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Test\SwiftmailerAssertionTrait;
|
||||
use Sylius\Component\User\Model\UserInterface;
|
||||
use Sylius\Component\User\Repository\UserRepositoryInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final class SendAccountRegistrationEmailHandlerTest extends KernelTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
use SwiftmailerAssertionTrait;
|
||||
|
||||
/** @test */
|
||||
public function it_sends_account_registration_email(): void
|
||||
{
|
||||
if ($this->isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment without swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::getContainer();
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
|
|
@ -82,71 +75,4 @@ final class SendAccountRegistrationEmailHandlerTest extends KernelTestCase
|
|||
self::assertEmailAddressContains($email, 'To', 'user@example.com');
|
||||
self::assertEmailHtmlBodyContains($email, $translator->trans('sylius.email.user_registration.start_shopping', [], null, 'en_US'));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_sends_account_registration_email_with_swiftmailer(): void
|
||||
{
|
||||
if (!$this->isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment with swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::getContainer();
|
||||
|
||||
self::setSpoolDirectory($container->getParameter('kernel.cache_dir') . '/spool');
|
||||
|
||||
/** @var Filesystem $filesystem */
|
||||
$filesystem = $container->get('test.filesystem.public');
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
$translator = $container->get('translator');
|
||||
|
||||
$filesystem->remove(self::getSpoolDirectory());
|
||||
|
||||
$emailSender = $container->get('sylius.email_sender');
|
||||
|
||||
/** @var ChannelRepositoryInterface|ObjectProphecy $channelRepository */
|
||||
$channelRepository = $this->prophesize(ChannelRepositoryInterface::class);
|
||||
/** @var UserRepositoryInterface|ObjectProphecy $userRepository */
|
||||
$userRepository = $this->prophesize(UserRepositoryInterface::class);
|
||||
/** @var ChannelInterface|ObjectProphecy $channel */
|
||||
$channel = $this->prophesize(ChannelInterface::class);
|
||||
/** @var UserInterface|ObjectProphecy $user */
|
||||
$user = $this->prophesize(UserInterface::class);
|
||||
|
||||
$channel->isAccountVerificationRequired()->willReturn(false);
|
||||
$channel->getHostname()->willReturn('example.com');
|
||||
|
||||
$user->getUsername()->willReturn('username');
|
||||
$user->getEmailVerificationToken()->willReturn('token');
|
||||
|
||||
$channelRepository->findOneByCode('CHANNEL_CODE')->willReturn($channel->reveal());
|
||||
$userRepository->findOneByEmail('user@example.com')->willReturn($user->reveal());
|
||||
|
||||
$sendAccountRegistrationEmailHandler = new SendAccountRegistrationEmailHandler(
|
||||
$userRepository->reveal(),
|
||||
$channelRepository->reveal(),
|
||||
$emailSender,
|
||||
);
|
||||
|
||||
$sendAccountRegistrationEmailHandler(
|
||||
new SendAccountRegistrationEmail(
|
||||
'user@example.com',
|
||||
'en_US',
|
||||
'CHANNEL_CODE',
|
||||
),
|
||||
);
|
||||
|
||||
self::assertSpooledMessagesCountWithRecipient(1, 'user@example.com');
|
||||
self::assertSpooledMessageWithContentHasRecipient(
|
||||
$translator->trans('sylius.email.user_registration.start_shopping', [], null, 'en_US'),
|
||||
'user@example.com',
|
||||
);
|
||||
}
|
||||
|
||||
private function isItSwiftmailerTestEnv(): bool
|
||||
{
|
||||
$env = self::getContainer()->getParameter('kernel.environment');
|
||||
|
||||
return $env === 'test_with_swiftmailer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@ final class SendAccountVerificationEmailHandlerTest extends KernelTestCase
|
|||
/** @test */
|
||||
public function it_sends_account_verification_token_email(): void
|
||||
{
|
||||
if ($this->isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment without swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::getContainer();
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
|
|
@ -81,11 +77,4 @@ final class SendAccountVerificationEmailHandlerTest extends KernelTestCase
|
|||
$translator->trans('sylius.email.verification_token.message', [], null, 'en_US'),
|
||||
);
|
||||
}
|
||||
|
||||
private function isItSwiftmailerTestEnv(): bool
|
||||
{
|
||||
$env = self::getContainer()->getParameter('kernel.environment');
|
||||
|
||||
return $env === 'test_with_swiftmailer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,6 @@ final class SendContactRequestHandlerTest extends KernelTestCase
|
|||
/** @test */
|
||||
public function it_sends_contact_request(): void
|
||||
{
|
||||
if ($this->isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment without swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::getContainer();
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
|
|
@ -68,11 +64,4 @@ final class SendContactRequestHandlerTest extends KernelTestCase
|
|||
self::assertEmailAddressContains($email, 'To', 'shop@example.com');
|
||||
self::assertEmailHtmlBodyContains($email, $translator->trans('sylius.email.contact_request.content', [], null, 'en_US'));
|
||||
}
|
||||
|
||||
private function isItSwiftmailerTestEnv(): bool
|
||||
{
|
||||
$env = self::getContainer()->getParameter('kernel.environment');
|
||||
|
||||
return $env === 'test_with_swiftmailer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,6 @@ final class SendOrderConfirmationEmailHandlerTest extends KernelTestCase
|
|||
/** @test */
|
||||
public function it_sends_order_confirmation_email(): void
|
||||
{
|
||||
if ($this->isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment without swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::bootKernel()->getContainer();
|
||||
$emailSender = $container->get('sylius.email_sender');
|
||||
|
||||
|
|
@ -70,11 +66,4 @@ final class SendOrderConfirmationEmailHandlerTest extends KernelTestCase
|
|||
$this->assertEmailAddressContains($email, 'To', 'johnny.bravo@email.com');
|
||||
$this->assertEmailHtmlBodyContains($email, '#000001');
|
||||
}
|
||||
|
||||
private function isItSwiftmailerTestEnv(): bool
|
||||
{
|
||||
$env = $this->getContainer()->getParameter('kernel.environment');
|
||||
|
||||
return $env === 'test_with_swiftmailer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@ final class SendResetPasswordEmailHandlerTest extends KernelTestCase
|
|||
/** @test */
|
||||
public function it_sends_password_reset_token_email(): void
|
||||
{
|
||||
if ($this->isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment without swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::getContainer();
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
|
|
@ -79,11 +75,4 @@ final class SendResetPasswordEmailHandlerTest extends KernelTestCase
|
|||
$translator->trans('sylius.email.password_reset.to_reset_your_password_token', [], null, 'en_US'),
|
||||
);
|
||||
}
|
||||
|
||||
private function isItSwiftmailerTestEnv(): bool
|
||||
{
|
||||
$env = self::getContainer()->getParameter('kernel.environment');
|
||||
|
||||
return $env === 'test_with_swiftmailer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,6 @@ final class SendResetPasswordEmailHandlerTest extends KernelTestCase
|
|||
/** @test */
|
||||
public function it_sends_password_reset_token_email(): void
|
||||
{
|
||||
if ($this->isItSwiftmailerTestEnv()) {
|
||||
$this->markTestSkipped('Test is relevant only for the environment without swiftmailer');
|
||||
}
|
||||
|
||||
$container = self::getContainer();
|
||||
|
||||
/** @var TranslatorInterface $translator */
|
||||
|
|
@ -66,11 +62,4 @@ final class SendResetPasswordEmailHandlerTest extends KernelTestCase
|
|||
$translator->trans('sylius.email.admin_password_reset.to_reset_your_password_token', [], null, 'en_US'),
|
||||
);
|
||||
}
|
||||
|
||||
private function isItSwiftmailerTestEnv(): bool
|
||||
{
|
||||
$env = self::getContainer()->getParameter('kernel.environment');
|
||||
|
||||
return $env === 'test_with_swiftmailer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\ApiBundle\DataPersister;
|
||||
|
||||
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\ApiBundle\Exception\ChannelCannotBeRemoved;
|
||||
use Sylius\Component\Channel\Checker\ChannelDeletionCheckerInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
|
||||
final class ChannelDataPersisterSpec extends ObjectBehavior
|
||||
{
|
||||
function let(
|
||||
ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
): void {
|
||||
$this->beConstructedWith($decoratedDataPersister, $channelDeletionChecker);
|
||||
}
|
||||
|
||||
function it_supports_only_channel_entity(ChannelInterface $channel, \stdClass $object): void
|
||||
{
|
||||
$this->supports($channel)->shouldReturn(true);
|
||||
$this->supports($object)->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_persists_channel(
|
||||
ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$decoratedDataPersister->persist($channel, [])->willReturn($channel);
|
||||
|
||||
$this->persist($channel, [])->shouldReturn($channel);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_channel_is_not_deletable(
|
||||
ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$channelDeletionChecker->isDeletable($channel)->willReturn(false);
|
||||
|
||||
$this
|
||||
->shouldThrow(ChannelCannotBeRemoved::class)
|
||||
->during('remove', [$channel])
|
||||
;
|
||||
}
|
||||
|
||||
function it_removes_channel(
|
||||
ContextAwareDataPersisterInterface $decoratedDataPersister,
|
||||
ChannelDeletionCheckerInterface $channelDeletionChecker,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$channelDeletionChecker->isDeletable($channel)->willReturn(true);
|
||||
$decoratedDataPersister->remove($channel, [])->willReturn(null);
|
||||
|
||||
$this->remove($channel, [])->shouldReturn(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\ApiBundle\DataPersister;
|
||||
|
||||
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
|
||||
use ApiPlatform\Core\DataPersister\ResumableDataPersisterInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Bundle\ApiBundle\Exception\TranslationInDefaultLocaleCannotBeRemoved;
|
||||
use Sylius\Component\Resource\Model\TranslatableInterface;
|
||||
use Sylius\Component\Resource\Translation\Provider\TranslationLocaleProviderInterface;
|
||||
|
||||
final class TranslatableDataPersisterSpec extends ObjectBehavior
|
||||
{
|
||||
function let(TranslationLocaleProviderInterface $localeProvider): void
|
||||
{
|
||||
$this->beConstructedWith($localeProvider);
|
||||
}
|
||||
|
||||
function it_is_a_context_aware_data_persister(): void
|
||||
{
|
||||
$this->shouldImplement(ContextAwareDataPersisterInterface::class);
|
||||
}
|
||||
|
||||
function it_is_a_resumable_data_persister(): void
|
||||
{
|
||||
$this->shouldImplement(ResumableDataPersisterInterface::class);
|
||||
}
|
||||
|
||||
function it_supports_only_translatable(TranslatableInterface $translatable): void
|
||||
{
|
||||
$this->supports(new \stdClass())->shouldReturn(false);
|
||||
$this->supports($translatable)->shouldReturn(true);
|
||||
}
|
||||
|
||||
function it_does_nothing_if_there_is_a_translation_in_default_locale(
|
||||
TranslationLocaleProviderInterface $localeProvider,
|
||||
TranslatableInterface $translatable,
|
||||
TranslatableInterface $translation,
|
||||
): void {
|
||||
$localeProvider->getDefaultLocaleCode()->willReturn('en_US');
|
||||
$translatable->getTranslations()->willReturn(new ArrayCollection(['en_US' => $translation]));
|
||||
|
||||
$this->persist($translatable)->shouldReturn($translatable);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_there_is_no_translation_in_default_locale(
|
||||
TranslationLocaleProviderInterface $localeProvider,
|
||||
TranslatableInterface $translatable,
|
||||
TranslatableInterface $translation,
|
||||
): void {
|
||||
$localeProvider->getDefaultLocaleCode()->willReturn('en_US');
|
||||
$translatable->getTranslations()->willReturn(new ArrayCollection(['de_DE' => $translation]));
|
||||
|
||||
$this->shouldThrow(TranslationInDefaultLocaleCannotBeRemoved::class)->during('persist', [$translatable]);
|
||||
}
|
||||
|
||||
function it_does_nothing_during_removing_object(TranslatableInterface $translatable): void
|
||||
{
|
||||
$this->remove($translatable)->shouldReturn($translatable);
|
||||
}
|
||||
|
||||
function it_is_resumable(): void
|
||||
{
|
||||
$this->resumable()->shouldReturn(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ use PhpSpec\ObjectBehavior;
|
|||
use Prophecy\Argument;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ChannelPriceHistoryConfigInterface;
|
||||
use Sylius\Component\Core\Model\ShopBillingDataInterface;
|
||||
use Sylius\Component\Resource\Factory\FactoryInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
|
|
@ -24,9 +25,9 @@ final class ChannelDenormalizerSpec extends ObjectBehavior
|
|||
{
|
||||
private const ALREADY_CALLED = 'sylius_channel_denormalizer_already_called';
|
||||
|
||||
function let(FactoryInterface $configFactory): void
|
||||
function let(FactoryInterface $configFactory, FactoryInterface $shopBillingDataFactory): void
|
||||
{
|
||||
$this->beConstructedWith($configFactory);
|
||||
$this->beConstructedWith($configFactory, $shopBillingDataFactory);
|
||||
}
|
||||
|
||||
function it_does_not_support_denormalization_when_the_denormalizer_has_already_been_called(): void
|
||||
|
|
@ -54,15 +55,17 @@ final class ChannelDenormalizerSpec extends ObjectBehavior
|
|||
$this->shouldThrow(\InvalidArgumentException::class)->during('denormalize', [[], 'string']);
|
||||
}
|
||||
|
||||
function it_returns_channel_as_is_when_channel_price_history_config_has_already_been_set(
|
||||
function it_returns_channel_as_is_when_shop_billing_data_and_channel_price_history_config_has_already_been_set(
|
||||
DenormalizerInterface $denormalizer,
|
||||
FactoryInterface $configFactory,
|
||||
ShopBillingDataInterface $shopBillingData,
|
||||
ChannelPriceHistoryConfigInterface $config,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$this->setDenormalizer($denormalizer);
|
||||
|
||||
$channel->getChannelPriceHistoryConfig()->willReturn($config);
|
||||
$channel->getShopBillingData()->willReturn($shopBillingData);
|
||||
|
||||
$channel->setChannelPriceHistoryConfig(Argument::any())->shouldNotBeCalled();
|
||||
$configFactory->createNew()->shouldNotBeCalled();
|
||||
|
|
@ -75,12 +78,14 @@ final class ChannelDenormalizerSpec extends ObjectBehavior
|
|||
function it_adds_a_new_channel_price_history_config_when_channel_has_none(
|
||||
DenormalizerInterface $denormalizer,
|
||||
FactoryInterface $configFactory,
|
||||
ShopBillingDataInterface $shopBillingData,
|
||||
ChannelPriceHistoryConfigInterface $config,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$this->setDenormalizer($denormalizer);
|
||||
|
||||
$channel->getChannelPriceHistoryConfig()->willReturn(null);
|
||||
$channel->getShopBillingData()->willReturn($shopBillingData);
|
||||
|
||||
$configFactory->createNew()->willReturn($config);
|
||||
$channel->setChannelPriceHistoryConfig($config)->shouldBeCalled();
|
||||
|
|
@ -89,4 +94,24 @@ final class ChannelDenormalizerSpec extends ObjectBehavior
|
|||
|
||||
$this->denormalize([], ChannelInterface::class)->shouldReturn($channel);
|
||||
}
|
||||
|
||||
function it_adds_a_new_shop_billing_data_when_channel_has_none(
|
||||
DenormalizerInterface $denormalizer,
|
||||
FactoryInterface $shopBillingDataFactory,
|
||||
ShopBillingDataInterface $shopBillingData,
|
||||
ChannelPriceHistoryConfigInterface $config,
|
||||
ChannelInterface $channel,
|
||||
): void {
|
||||
$this->setDenormalizer($denormalizer);
|
||||
|
||||
$channel->getChannelPriceHistoryConfig()->willReturn($config);
|
||||
$channel->getShopBillingData()->willReturn(null);
|
||||
|
||||
$shopBillingDataFactory->createNew()->willReturn($shopBillingData);
|
||||
$channel->setShopBillingData($shopBillingData)->shouldBeCalled();
|
||||
|
||||
$denormalizer->denormalize([], ChannelInterface::class, null, [self::ALREADY_CALLED => true])->willReturn($channel);
|
||||
|
||||
$this->denormalize([], ChannelInterface::class)->shouldReturn($channel);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ final class ProductAttributeValueDenormalizerSpec extends ObjectBehavior
|
|||
['attribute' => '/attributes/material', 'value' => 'ceramic'],
|
||||
ProductAttributeValueInterface::class,
|
||||
null,
|
||||
[self::ALREADY_CALLED => true]
|
||||
[self::ALREADY_CALLED => true],
|
||||
)
|
||||
->shouldBeCalled()
|
||||
;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue