mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
[ProductVariant][API] Add possibility to POST variant translations
This commit is contained in:
parent
5264b484c1
commit
8feb851ece
15 changed files with 443 additions and 14 deletions
|
|
@ -11,7 +11,7 @@ Feature: Adding a new product variant
|
|||
And the store has "Fragile" shipping category
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding a new product variant
|
||||
When I want to create a new variant of this product
|
||||
And I specify its code as "VODKA_WYBOROWA_PREMIUM"
|
||||
|
|
@ -19,9 +19,9 @@ Feature: Adding a new product variant
|
|||
And I add it
|
||||
Then I should be notified that it has been successfully created
|
||||
And the "VODKA_WYBOROWA_PREMIUM" variant of the "Wyborowa Vodka" product should appear in the store
|
||||
And the variant with code "VODKA_WYBOROWA_PREMIUM" should be priced at $100.00 for channel "United States"
|
||||
And the variant with code "VODKA_WYBOROWA_PREMIUM" should be priced at "$100.00" for channel "United States"
|
||||
|
||||
@ui
|
||||
@api @ui
|
||||
Scenario: Adding a new product variant with name
|
||||
Given the store is also available in "Polish (Poland)"
|
||||
When I want to create a new variant of this product
|
||||
|
|
@ -32,7 +32,7 @@ Feature: Adding a new product variant
|
|||
And I add it
|
||||
Then I should be notified that it has been successfully created
|
||||
And the "VODKA_WYBOROWA_PREMIUM" variant of the "Wyborowa Vodka" product should appear in the store
|
||||
And the variant with code "VODKA_WYBOROWA_PREMIUM" should be priced at $100.00 for channel "United States"
|
||||
And the variant with code "VODKA_WYBOROWA_PREMIUM" should be priced at "$100.00" for channel "United States"
|
||||
And the variant with code "VODKA_WYBOROWA_PREMIUM" should be named "Vodka Wyborowa Premium" in "English (United States)" locale
|
||||
And the variant with code "VODKA_WYBOROWA_PREMIUM" should be named "Wódka Wyborowa Premium" in "Polish (Poland)" locale
|
||||
|
||||
|
|
|
|||
|
|
@ -54,13 +54,17 @@ final class ResponseChecker implements ResponseCheckerInterface
|
|||
return $translations[$localeCode][$key];
|
||||
}
|
||||
|
||||
public function getError(Response $response): string
|
||||
public function getError(Response $response): ?string
|
||||
{
|
||||
if ($this->hasKey($response, 'message')) {
|
||||
return $this->getValue($response, 'message');
|
||||
}
|
||||
|
||||
return $this->getResponseContentValue($response, 'hydra:description');
|
||||
if ($this->hasKey($response, 'hydra:description')) {
|
||||
return $this->getResponseContentValue($response, 'hydra:description');
|
||||
}
|
||||
|
||||
return $response->getContent();
|
||||
}
|
||||
|
||||
public function isAccepted(Response $response): bool
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ interface ResponseCheckerInterface
|
|||
|
||||
public function getTranslationValue(Response $response, string $key, ?string $localeCode): string;
|
||||
|
||||
public function getError(Response $response): string;
|
||||
public function getError(Response $response): ?string;
|
||||
|
||||
public function isAccepted(Response $response): bool;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ use Webmozart\Assert\Assert;
|
|||
|
||||
final class ManagingProductVariantsContext implements Context
|
||||
{
|
||||
private const FIRST_COLLECTION_ITEM = 0;
|
||||
|
||||
public function __construct(
|
||||
private ApiClientInterface $client,
|
||||
private ResponseCheckerInterface $responseChecker,
|
||||
|
|
@ -50,6 +52,19 @@ final class ManagingProductVariantsContext implements Context
|
|||
$this->client->addRequestData('code', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I name it :name in :language
|
||||
*/
|
||||
public function iNameItIn($name, $language)
|
||||
{
|
||||
$this->client->addRequestData('translations', [
|
||||
$language => [
|
||||
'locale' => $language,
|
||||
'name' => $name,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^I set its price to ("[^"]+") for ("[^"]+" channel)$/
|
||||
*/
|
||||
|
|
@ -167,7 +182,10 @@ final class ManagingProductVariantsContext implements Context
|
|||
{
|
||||
Assert::true(
|
||||
$this->responseChecker->isCreationSuccessful($this->client->getLastResponse()),
|
||||
'Product Variant could not be created',
|
||||
sprintf(
|
||||
'Product Variant could not be created: %s',
|
||||
$this->responseChecker->getError($this->client->getLastResponse()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +199,27 @@ final class ManagingProductVariantsContext implements Context
|
|||
Assert::true($this->responseChecker->hasItemWithValue($response, 'code', $productVariantCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the (?:variant with code "[^"]+") should be named "([^"]+)" in ("([^"]+)" locale)$/
|
||||
*/
|
||||
public function theVariantWithCodeShouldBeNamedIn(string $name, string $language)
|
||||
{
|
||||
$response = $this->responseChecker->getCollection($this->client->index(Resources::PRODUCT_VARIANTS));
|
||||
|
||||
$expectedTranslation = [
|
||||
'locale' => $language,
|
||||
'name' => $name,
|
||||
];
|
||||
|
||||
$translationInLocale = $response[self::FIRST_COLLECTION_ITEM]['translations'][$language];
|
||||
|
||||
Assert::allInArray(
|
||||
$expectedTranslation,
|
||||
$translationInLocale,
|
||||
sprintf('Expected translation %s, got %s', $expectedTranslation['name'], $translationInLocale['name']),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the (variant with code "[^"]+") should be priced at ("[^"]+") for (channel "([^"]+)")$/
|
||||
*/
|
||||
|
|
@ -188,7 +227,7 @@ final class ManagingProductVariantsContext implements Context
|
|||
{
|
||||
$response = $this->responseChecker->getCollection($this->client->index(Resources::PRODUCT_VARIANTS));
|
||||
|
||||
Assert::same($response[0]['channelPricings'][$channel->getCode()]['price'], $price);
|
||||
Assert::same($response[self::FIRST_COLLECTION_ITEM]['channelPricings'][$channel->getCode()]['price'], $price);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -198,7 +237,7 @@ final class ManagingProductVariantsContext implements Context
|
|||
{
|
||||
$response = $this->responseChecker->getCollection($this->client->index(Resources::PRODUCT_VARIANTS));
|
||||
|
||||
Assert::same($response[0]['channelPricings'][$channel->getCode()]['minimumPrice'], $minimumPrice);
|
||||
Assert::same($response[self::FIRST_COLLECTION_ITEM]['channelPricings'][$channel->getCode()]['minimumPrice'], $minimumPrice);
|
||||
}
|
||||
|
||||
private function updateChannelPricingField(
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@
|
|||
<attribute name="example">
|
||||
<attribute name="en_US">
|
||||
<attribute name="name">string</attribute>
|
||||
<attribute name="slug">string</attribute>
|
||||
<attribute name="locale">string</attribute>
|
||||
</attribute>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<group>shop:product_variant:read</group>
|
||||
</attribute>
|
||||
<attribute name="translations">
|
||||
<group>admin:product_variant:create</group>
|
||||
<group>admin:product_variant:read</group>
|
||||
</attribute>
|
||||
<attribute name="optionValues">
|
||||
|
|
|
|||
|
|
@ -25,12 +25,16 @@
|
|||
<attribute name="name">
|
||||
<group>shop:order:account:read</group>
|
||||
<group>admin:order:read</group>
|
||||
<group>admin:product_variant:create</group>
|
||||
<group>admin:product_variant:update</group>
|
||||
<group>admin:product_variant:read</group>
|
||||
<group>shop:product_variant:read</group>
|
||||
</attribute>
|
||||
<attribute name="locale">
|
||||
<group>shop:order:account:read</group>
|
||||
<group>admin:order:read</group>
|
||||
<group>admin:product_variant:create</group>
|
||||
<group>admin:product_variant:update</group>
|
||||
<group>admin:product_variant:read</group>
|
||||
<group>shop:product_variant:read</group>
|
||||
</attribute>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@
|
|||
<service id="sylius.validator.has_all_variant_prices_defined" class="Sylius\Bundle\CoreBundle\Validator\Constraints\HasAllVariantPricesDefinedValidator">
|
||||
<tag name="validator.constraint_validator" alias="sylius_has_all_variant_prices_defined" />
|
||||
</service>
|
||||
<service id="sylius.validator.translation_for_existing_locales" class="Sylius\Bundle\CoreBundle\Validator\Constraints\TranslationForExistingLocalesValidator">
|
||||
<argument type="service" id="sylius.repository.locale" />
|
||||
<tag name="validator.constraint_validator" alias="sylius_translation_for_existing_locales" />
|
||||
</service>
|
||||
<service id="sylius.validator.unique_reviewer_email" class="Sylius\Bundle\CoreBundle\Validator\Constraints\UniqueReviewerEmailValidator">
|
||||
<argument type="service" id="sylius.repository.shop_user" />
|
||||
<argument type="service" id="security.token_storage" />
|
||||
|
|
|
|||
|
|
@ -60,8 +60,14 @@
|
|||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\HasAllPricesDefined">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\TranslationForExistingLocales">
|
||||
<option name="groups">sylius</option>
|
||||
</constraint>
|
||||
<property name="channelPricings">
|
||||
<constraint name="Valid" />
|
||||
</property>
|
||||
<property name="translations">
|
||||
<constraint name="Valid" />
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
|
|||
|
|
@ -124,6 +124,9 @@ sylius:
|
|||
file:
|
||||
max_size: The image is too big - {{ size }}{{ suffix }}. Maximum allowed size is {{ limit }}{{ suffix }}.
|
||||
upload_ini_size: The image is too big. Maximum allowed size is {{ limit }}{{ suffix }}.
|
||||
translation:
|
||||
locale_code:
|
||||
invalid: 'Please choose one of the available locales: %locales%'
|
||||
user:
|
||||
email:
|
||||
unique: This email is already used.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* 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\CoreBundle\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
final class TranslationForExistingLocales extends Constraint
|
||||
{
|
||||
public string $message = 'sylius.translation.locale_code.invalid';
|
||||
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return 'sylius_translation_for_existing_locales';
|
||||
}
|
||||
|
||||
public function getTargets(): string
|
||||
{
|
||||
return Constraint::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* 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\CoreBundle\Validator\Constraints;
|
||||
|
||||
use Sylius\Component\Locale\Model\LocaleInterface;
|
||||
use Sylius\Component\Resource\Model\TranslatableInterface;
|
||||
use Sylius\Component\Resource\Model\TranslationInterface;
|
||||
use Sylius\Component\Resource\Repository\RepositoryInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class TranslationForExistingLocalesValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(private RepositoryInterface $localeRepository)
|
||||
{
|
||||
}
|
||||
|
||||
public function validate($value, Constraint $constraint): void
|
||||
{
|
||||
Assert::isInstanceOf($value, TranslatableInterface::class);
|
||||
Assert::isInstanceOf($constraint, TranslationForExistingLocales::class);
|
||||
|
||||
/** @var LocaleInterface[] $locales */
|
||||
$locales = $this->localeRepository->findAll();
|
||||
|
||||
if (empty($locales)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$localeCodes = array_map(fn (LocaleInterface $locale) => $locale->getCode(), $locales);
|
||||
|
||||
$translations = $value->getTranslations();
|
||||
|
||||
/** @var TranslationInterface $translation */
|
||||
foreach ($translations as $key => $translation) {
|
||||
if (!in_array($translation->getLocale(), $localeCodes, true)) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('%locales%', implode(', ', $localeCodes))
|
||||
->atPath(sprintf('translations[%s]', $key))
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* 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\CoreBundle\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\CoreBundle\Validator\Constraints\TranslationForExistingLocales;
|
||||
use Sylius\Component\Locale\Model\LocaleInterface;
|
||||
use Sylius\Component\Resource\Model\TranslatableInterface;
|
||||
use Sylius\Component\Resource\Model\TranslationInterface;
|
||||
use Sylius\Component\Resource\Repository\RepositoryInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidatorInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
|
||||
|
||||
final class TranslationForExistingLocalesValidatorSpec extends ObjectBehavior
|
||||
{
|
||||
function let(RepositoryInterface $localeRepository, ExecutionContextInterface $context): void
|
||||
{
|
||||
$this->beConstructedWith($localeRepository);
|
||||
|
||||
$this->initialize($context);
|
||||
}
|
||||
|
||||
function it_is_a_constraint_validator(): void
|
||||
{
|
||||
$this->shouldImplement(ConstraintValidatorInterface::class);
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_value_is_not_translatable(
|
||||
RepositoryInterface $localeRepository,
|
||||
ExecutionContextInterface $context,
|
||||
TranslatableInterface $value,
|
||||
TranslationInterface $translation,
|
||||
): void {
|
||||
$localeRepository->findAll()->shouldNotBeCalled();
|
||||
|
||||
$value->getTranslations()->shouldNotBeCalled();
|
||||
$translation->getLocale()->shouldNotBeCalled();
|
||||
|
||||
$context->buildViolation((new TranslationForExistingLocales())->message)->shouldNotBeCalled();
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->during('validate', [new \stdClass(), new TranslationForExistingLocales()])
|
||||
;
|
||||
}
|
||||
|
||||
function it_throws_an_exception_if_constraint_is_not_translation_for_existing_locales_constraint(
|
||||
RepositoryInterface $localeRepository,
|
||||
ExecutionContextInterface $context,
|
||||
Constraint $constraint,
|
||||
TranslatableInterface $value,
|
||||
TranslationInterface $translation,
|
||||
): void {
|
||||
$localeRepository->findAll()->shouldNotBeCalled();
|
||||
|
||||
$value->getTranslations()->shouldNotBeCalled();
|
||||
$translation->getLocale()->shouldNotBeCalled();
|
||||
|
||||
$context->buildViolation((new TranslationForExistingLocales())->message)->shouldNotBeCalled();
|
||||
|
||||
$this
|
||||
->shouldThrow(\InvalidArgumentException::class)
|
||||
->during('validate', [$value, $constraint])
|
||||
;
|
||||
}
|
||||
|
||||
function it_does_nothing_if_there_is_no_locales(
|
||||
RepositoryInterface $localeRepository,
|
||||
ExecutionContextInterface $context,
|
||||
TranslatableInterface $value,
|
||||
TranslationInterface $translation,
|
||||
): void {
|
||||
$localeRepository->findAll()->willReturn([]);
|
||||
|
||||
$value->getTranslations()->shouldNotBeCalled();
|
||||
$translation->getLocale()->shouldNotBeCalled();
|
||||
|
||||
$context->buildViolation((new TranslationForExistingLocales())->message)->shouldNotBeCalled();
|
||||
|
||||
$this->validate($value, new TranslationForExistingLocales());
|
||||
}
|
||||
|
||||
function it_adds_a_violation_if_any_translations_locale_in_the_translatable_object_is_not_included_in_the_available_locales(
|
||||
RepositoryInterface $localeRepository,
|
||||
ExecutionContextInterface $context,
|
||||
ConstraintViolationBuilderInterface $constraintViolationBuilder,
|
||||
TranslatableInterface $value,
|
||||
TranslationInterface $firstTranslation,
|
||||
TranslationInterface $secondTranslation,
|
||||
LocaleInterface $availableLocale,
|
||||
): void {
|
||||
$availableLocale->getCode()->willReturn('en_US');
|
||||
$localeRepository->findAll()->willReturn([$availableLocale]);
|
||||
|
||||
$value->getTranslations()->willReturn(new ArrayCollection([$firstTranslation, $secondTranslation]));
|
||||
$firstTranslation->getLocale()->willReturn('en_US');
|
||||
$secondTranslation->getLocale()->willReturn('NON_EXISTING_LOCALE');
|
||||
|
||||
$constraintViolationBuilder->addViolation()->shouldBeCalled();
|
||||
$constraintViolationBuilder->atPath(Argument::any())->willReturn($constraintViolationBuilder);
|
||||
$constraintViolationBuilder->setParameter(Argument::cetera())->willReturn($constraintViolationBuilder);
|
||||
|
||||
$context->buildViolation((new TranslationForExistingLocales())->message)->willReturn($constraintViolationBuilder);
|
||||
|
||||
$this->validate($value, new TranslationForExistingLocales());
|
||||
}
|
||||
|
||||
function it_does_not_add_violation_if_the_translations_in_the_translatable_object_are_included_in_the_available_locales(
|
||||
RepositoryInterface $localeRepository,
|
||||
ExecutionContextInterface $context,
|
||||
ConstraintViolationBuilderInterface $constraintViolationBuilder,
|
||||
TranslatableInterface $value,
|
||||
TranslationInterface $firstTranslation,
|
||||
TranslationInterface $secondTranslation,
|
||||
LocaleInterface $firstAvailableLocale,
|
||||
LocaleInterface $secondAvailableLocale,
|
||||
): void {
|
||||
$firstAvailableLocale->getCode()->willReturn('en_US');
|
||||
$secondAvailableLocale->getCode()->willReturn('pl_PL');
|
||||
$localeRepository->findAll()->willReturn([$firstAvailableLocale, $secondAvailableLocale]);
|
||||
|
||||
$value
|
||||
->getTranslations()
|
||||
->willReturn(new ArrayCollection([$firstTranslation->getWrappedObject(), $secondTranslation->getWrappedObject()]))
|
||||
;
|
||||
$firstTranslation->getLocale()->willReturn('en_US');
|
||||
$secondTranslation->getLocale()->willReturn('pl_PL');
|
||||
|
||||
$context->buildViolation((new TranslationForExistingLocales())->message)->shouldNotBeCalled();
|
||||
|
||||
$this->validate($value, new TranslationForExistingLocales());
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
}
|
||||
|
||||
/** @test */
|
||||
public function it_updates_channel_pricing_of_product_variant(): void
|
||||
public function it_updates_channel_pricing_and_translation_of_a_product_variant(): void
|
||||
{
|
||||
$fixtures = $this->loadFixturesFromFiles(['channel.yaml', 'product/product_variant.yaml', 'authentication/api_administrator.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
|
@ -90,7 +90,19 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
'@id' => sprintf('/api/v2/admin/channel-pricings/%s', $productVariant->getChannelPricingForChannel($channel)->getId()),
|
||||
'price' => 3000,
|
||||
'originalPrice' => 4000,
|
||||
]]
|
||||
'minimumPrice' => 210,
|
||||
]],
|
||||
'translations' => [
|
||||
'pl_PL' => [
|
||||
'@id' => sprintf('/api/v2/admin/product-variant-translations/%s', $productVariant->getTranslation('pl_PL')->getId()),
|
||||
'locale' => 'pl_PL',
|
||||
'name' => 'Pomarańczowy kubek',
|
||||
],
|
||||
'de_DE' => [
|
||||
'locale' => 'de_DE',
|
||||
'name' => 'Orange Tasse',
|
||||
],
|
||||
]
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
|
|
@ -124,6 +136,12 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
'originalPrice' => 5000,
|
||||
'minimumPrice' => 2000,
|
||||
]],
|
||||
'translations' => [
|
||||
'en_US' => [
|
||||
'locale' => 'en_US',
|
||||
'name' => 'Yellow mug',
|
||||
],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
|
|
@ -167,4 +185,111 @@ final class ProductVariantsTest extends JsonApiTestCase
|
|||
Response::HTTP_CREATED,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_allow_to_create_product_variant_with_invalid_channel_code(): void
|
||||
{
|
||||
$fixtures = $this->loadFixturesFromFiles(['channel.yaml', 'product/product_variant.yaml', 'authentication/api_administrator.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
/** @var ProductInterface $product */
|
||||
$product = $fixtures['product'];
|
||||
|
||||
$this->client->request(
|
||||
method: 'POST',
|
||||
uri: '/api/v2/admin/product-variants',
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'code' => 'CUP',
|
||||
'product' => sprintf('/api/v2/admin/products/%s', $product->getCode()),
|
||||
'channelPricings' => ['NON-EXISTING-CHANNEL' => [
|
||||
'channelCode' => 'NON-EXISTING-CHANNEL',
|
||||
'price' => 4000,
|
||||
]],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_allow_to_create_product_variant_without_channel_code(): void
|
||||
{
|
||||
$fixtures = $this->loadFixturesFromFiles(['channel.yaml', 'product/product_variant.yaml', 'authentication/api_administrator.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
/** @var ProductInterface $product */
|
||||
$product = $fixtures['product'];
|
||||
|
||||
$this->client->request(
|
||||
method: 'POST',
|
||||
uri: '/api/v2/admin/product-variants',
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'code' => 'CUP',
|
||||
'product' => sprintf('/api/v2/admin/products/%s', $product->getCode()),
|
||||
'channelPricings' => [
|
||||
'NON-EXISTING-CHANNEL' => ['price' => 4000]
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_allow_to_create_product_variant_without_product(): void
|
||||
{
|
||||
$this->loadFixturesFromFiles(['channel.yaml', 'product/product_variant.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-variants',
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'code' => 'CUP',
|
||||
'channelPricings' => ['WEB' => [
|
||||
'channelCode' => 'WEB',
|
||||
'price' => 4000,
|
||||
]],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_does_not_allow_to_create_product_variant_with_invalid_locale_code(): void
|
||||
{
|
||||
$fixtures = $this->loadFixturesFromFiles(['channel.yaml', 'product/product_variant.yaml', 'authentication/api_administrator.yaml']);
|
||||
$header = array_merge($this->logInAdminUser('api@example.com'), self::CONTENT_TYPE_HEADER);
|
||||
|
||||
/** @var ProductInterface $product */
|
||||
$product = $fixtures['product'];
|
||||
|
||||
$this->client->request(
|
||||
method: 'POST',
|
||||
uri: '/api/v2/admin/product-variants',
|
||||
server: $header,
|
||||
content: json_encode([
|
||||
'code' => 'CUP',
|
||||
'product' => sprintf('/api/v2/admin/products/%s', $product->getCode()),
|
||||
'channelPricings' => ['WEB' => [
|
||||
'channelCode' => 'WEB',
|
||||
'price' => 4000,
|
||||
'originalPrice' => 5000,
|
||||
'minimumPrice' => 2000,
|
||||
]],
|
||||
'translations' => [
|
||||
'NON-EXISTING-LOCALE-CODE' => [
|
||||
'locale' => 'NON-EXISTING-LOCALE-CODE',
|
||||
'name' => 'Yellow mug',
|
||||
],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,5 +17,13 @@
|
|||
},
|
||||
"enabled": true,
|
||||
"optionValues": [],
|
||||
"translations": []
|
||||
"translations": {
|
||||
"en_US": {
|
||||
"@id":"\/api\/v2\/admin\/product-variant-translation\/@integer@",
|
||||
"@type":"ProductVariantTranslation",
|
||||
"id":"@integer@",
|
||||
"name":"Yellow mug",
|
||||
"locale":"en_US"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue