From 595f9438dafd4182385802a0fc65626a18f680e0 Mon Sep 17 00:00:00 2001 From: Vincent QUATREVIEUX Date: Thu, 27 Mar 2025 12:24:47 +0100 Subject: [PATCH 1/2] feat: Add argument resolver for automatically submit form (#FRAM-191) --- DependencyInjection/FormExtension.php | 4 + Http/InvalidFormException.php | 16 ++ Http/PayloadSource.php | 61 +++++ Http/Submit/SubmitForm.php | 101 +++++++++ Http/Submit/SubmitFormValueResolver.php | 104 +++++++++ README.md | 22 ++ Resources/config/http.yaml | 9 + Tests/Http/Form/PersonDto.php | 10 + Tests/Http/Form/PersonForm.php | 39 ++++ Tests/Http/FunctionSubmitFormTest.php | 130 +++++++++++ Tests/Http/PayloadSourceTest.php | 69 ++++++ .../Submit/SubmitFormValueResolverTest.php | 208 ++++++++++++++++++ Tests/Http/TestKernel.php | 92 ++++++++ Tests/Http/conf.yaml | 16 ++ composer.json | 1 + phpunit.xml.dist | 4 + 16 files changed, 886 insertions(+) create mode 100644 Http/InvalidFormException.php create mode 100644 Http/PayloadSource.php create mode 100644 Http/Submit/SubmitForm.php create mode 100644 Http/Submit/SubmitFormValueResolver.php create mode 100644 Resources/config/http.yaml create mode 100644 Tests/Http/Form/PersonDto.php create mode 100644 Tests/Http/Form/PersonForm.php create mode 100644 Tests/Http/FunctionSubmitFormTest.php create mode 100644 Tests/Http/PayloadSourceTest.php create mode 100644 Tests/Http/Submit/SubmitFormValueResolverTest.php create mode 100644 Tests/Http/TestKernel.php create mode 100644 Tests/Http/conf.yaml diff --git a/DependencyInjection/FormExtension.php b/DependencyInjection/FormExtension.php index 30da283..6b10f40 100644 --- a/DependencyInjection/FormExtension.php +++ b/DependencyInjection/FormExtension.php @@ -31,6 +31,10 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('attribute.yaml'); } + if (PHP_VERSION_ID >= 80100) { + $loader->load('http.yaml'); + } + $container ->registerForAutoconfiguration(ElementBuilderInterface::class) ->addTag('form.custom_builder') diff --git a/Http/InvalidFormException.php b/Http/InvalidFormException.php new file mode 100644 index 0000000..8c7af92 --- /dev/null +++ b/Http/InvalidFormException.php @@ -0,0 +1,16 @@ +error->global() ?? 'Invalid form data'); + } +} diff --git a/Http/PayloadSource.php b/Http/PayloadSource.php new file mode 100644 index 0000000..cbedf0a --- /dev/null +++ b/Http/PayloadSource.php @@ -0,0 +1,61 @@ + self::extractFromHttpMethod($request), + self::QueryString => $request->query->all(), + self::Body => $request->getPayload()->all(), + self::Attributes => $request->attributes->all(), + }; + } + + private static function extractFromHttpMethod(Request $request): array + { + return match ($request->getMethod()) { + 'POST', 'PUT', 'PATCH' => $request->getPayload()->all(), + default => $request->query->all(), + }; + } +} diff --git a/Http/Submit/SubmitForm.php b/Http/Submit/SubmitForm.php new file mode 100644 index 0000000..61a242d --- /dev/null +++ b/Http/Submit/SubmitForm.php @@ -0,0 +1,101 @@ +valid()); // Always true + * // ... + * } + * + * public function manualValidation(#[SubmitForm(validate: false)] MyForm $form): Response + * { + * // Work like the previous example, but the form is not validated + * // So you have to call $form->validate() manually + * + * if (!$form->valid()) { + * // Handle the error + * } + * + * // ... + * } + * + * public function useValue(#[SubmitForm(form: MyForm::class)] MyValue $value): Response + * { + * // The form is submitted, validated, and it's value is generated using the FormInterface::value() method + * } + * + * public function withCustomSources(#[SubmitForm(source: [PayloadSource::Attributes, PayloadSource::Body])] MyForm $form): Response + * { + * // You can define the source of the payload using the `source` parameter, instead of relying on the HTTP method + * // Multiple sources can be defined, and all values will be merged. The first source takes the priority over following ones, + * // So field that are defined in multiple sources will not be overridden. + * } + * } + * ``` + */ +#[\Attribute(\Attribute::TARGET_PARAMETER)] +final class SubmitForm extends ValueResolver +{ + public ArgumentMetadata $metadata; + + public function __construct( + /** + * The request payload source. + * + * By default, it will be determined based on the HTTP method. + * If an array is given, all sources will be merged, with the first one taking precedence. + * + * @var PayloadSource|PayloadSource[] + */ + public PayloadSource|array $source = PayloadSource::Auto, + + /** + * The form class to use. + * If null, it will be determined based on the argument type. + * + * @var class-string|null + */ + public ?string $form = null, + + /** + * If true, the form will be validated, and {@see InvalidFormException} will be thrown if the form is invalid. + */ + public bool $validate = true, + + /** + * The error message to return if the form is invalid. + */ + public string $validateMessage = 'The JSON contains invalid data.', + + /** + * Get the value instead of the form instance. + * + * If null, this flag will be resolved based on the parameter type (i.e. if the controller parameter type is `FormInterface`, it will be set to false). + * + * Note: if this flag is set to true, the form class must be defined on {@see SubmitForm::$form} parameter. + * + * @see FormInterface::value() will be called to get the value. + */ + public ?bool $value = null, + ) { + parent::__construct(SubmitFormValueResolver::class); + } +} diff --git a/Http/Submit/SubmitFormValueResolver.php b/Http/Submit/SubmitFormValueResolver.php new file mode 100644 index 0000000..210b65e --- /dev/null +++ b/Http/Submit/SubmitFormValueResolver.php @@ -0,0 +1,104 @@ +getAttributesOfType(SubmitForm::class)[0] ?? null; + + if (null === $attribute) { + return []; + } + + $type = $argument->getType(); + + $attribute->value ??= $type && !\is_subclass_of($type, ElementInterface::class); + + if (null === $attribute->form) { + if (true === $attribute->value) { + throw new \LogicException('The form class must be defined when the value is requested'); + } + + if (null === $type) { + throw new \LogicException('The form class must be defined as controller parameter type, or in the SubmitForm attribute'); + } + + $attribute->form = $type; + } + + $attribute->metadata = $argument; + + return [$attribute]; + } + + public function onKernelControllerArguments(ControllerArgumentsEvent $event): void + { + $arguments = $event->getArguments(); + $hasChanged = false; + + foreach ($arguments as $i => $argument) { + if (!$argument instanceof SubmitForm) { + continue; + } + + $payload = $this->extractPayload($event->getRequest(), $argument->source); + $form = $this->registry->elementBuilder($argument->form)->buildElement(); + $form->submit($payload); + + if ($argument->validate && !$form->valid()) { + throw new InvalidFormException($form->error(), $this->translator ? $this->translator->trans($argument->validateMessage) : $argument->validateMessage); + } + + $arguments[$i] = $argument->value ? $form->value() : $form; + $hasChanged = true; + } + + if ($hasChanged) { + $event->setArguments($arguments); + } + } + + private function extractPayload(Request $request, PayloadSource|array $sources): array + { + if (!\is_array($sources)) { + return $sources->extract($request); + } + + $payload = []; + + foreach ($sources as $source) { + $payload += $source->extract($request); + } + + return $payload; + } + + #[\Override] + public static function getSubscribedEvents(): array + { + return [ + KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments', + ]; + } +} diff --git a/README.md b/README.md index 1409702..7e7c2e7 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,28 @@ class MyController extends AbstractController return new Reponse('ok'); } + + public function withArgumentResolver(#[SubmitForm(validate: false)] MyForm $form) + { + // You can also use symfony argument resolver to automatically inject the form and submit it + // If you want the form to be validated automatically, you can set the `validate` parameter to its default value + // In this case, a InvalidFormException will be thrown if the form is invalid + if (!$form->valid()) { + throw new FormError($form->error()); + } + + $this->service->save($form->value()); + + return new Reponse('ok'); + } + + public function withArgumentResolverValue(#[SubmitForm(form: MyForm::class)] MyDto $value) + { + // The argument resolver can also submit, validate and generate the value automatically + $this->service->save($value); + + return new Reponse('ok'); + } } ``` diff --git a/Resources/config/http.yaml b/Resources/config/http.yaml new file mode 100644 index 0000000..beab95a --- /dev/null +++ b/Resources/config/http.yaml @@ -0,0 +1,9 @@ +services: + Bdf\Form\Bundle\Http\Submit\SubmitFormValueResolver: + class: 'Bdf\Form\Bundle\Http\Submit\SubmitFormValueResolver' + arguments: + - '@Bdf\Form\Registry\RegistryInterface' + - '@?translator.default' + tags: + - 'controller.argument_value_resolver' + - 'kernel.event_subscriber' diff --git a/Tests/Http/Form/PersonDto.php b/Tests/Http/Form/PersonDto.php new file mode 100644 index 0000000..91a8796 --- /dev/null +++ b/Tests/Http/Form/PersonDto.php @@ -0,0 +1,10 @@ + + */ +class PersonForm extends CustomForm +{ + protected function configure(FormBuilderInterface $builder): void + { + $builder->generates(PersonDto::class); + + $builder + ->integer('id') + ->getset() + ->required() + ; + + $builder + ->string('firstName') + ->trim() + ->regex('/^[a-zA-Z-]{3,25}$/') + ->required() + ->getset() + ; + + $builder + ->string('lastName') + ->trim() + ->regex('/^[a-zA-Z-]{3,25}$/') + ->required() + ->getset() + ; + } +} diff --git a/Tests/Http/FunctionSubmitFormTest.php b/Tests/Http/FunctionSubmitFormTest.php new file mode 100644 index 0000000..d2ee48e --- /dev/null +++ b/Tests/Http/FunctionSubmitFormTest.php @@ -0,0 +1,130 @@ +kernel = new TestKernel(); + $this->client = new HttpKernelBrowser($this->kernel); + } + + protected function tearDown(): void + { + (new Filesystem())->remove(__DIR__.'/../../var'); + } + + public function testValue() + { + $this->client->request('POST', '/value', [ + 'id' => 1, + 'firstName' => 'John', + 'lastName' => 'Doe', + ]); + + $content = \json_decode($this->client->getResponse()->getContent(), true); + $this->assertSame([ + 'id' => 1, + 'firstName' => 'John', + 'lastName' => 'Doe', + ], $content); + } + + public function testValueError() + { + $this->client->request('POST', '/value', [ + 'firstName' => '@@@@@@', + 'lastName' => 'Doe', + ]); + + $this->assertEquals(400, $this->client->getResponse()->getStatusCode()); + $this->assertSame([ + 'message' => 'The JSON contains invalid data.', + 'fields' => [ + 'id' => 'This value should not be blank.', + 'firstName' => 'This value is not valid.', + ], + ], \json_decode($this->client->getResponse()->getContent(), true)); + } + + public function testInjectFormValid() + { + $this->client->request('POST', '/form', [ + 'id' => 1, + 'firstName' => 'John', + 'lastName' => 'Doe', + ]); + + $content = \json_decode($this->client->getResponse()->getContent(), true); + $this->assertSame([ + 'value' => [ + 'id' => 1, + 'firstName' => 'John', + 'lastName' => 'Doe', + ], + 'errors' => [], + ], $content); + } + + public function testInjectFormError() + { + $this->client->request('POST', '/form', [ + 'firstName' => '#####', + 'lastName' => 'Doe', + ]); + + $content = \json_decode($this->client->getResponse()->getContent(), true); + $this->assertSame([ + 'value' => [ + 'id' => null, + 'firstName' => '#####', + 'lastName' => 'Doe', + ], + 'errors' => [ + 'id' => 'This value should not be blank.', + 'firstName' => 'This value is not valid.', + ], + ], $content); + } + + public function testMultiplePayloadSource() + { + $this->client->request('PUT', '/person/42', [ + 'firstName' => 'John', + 'lastName' => 'Doe', + ]); + + $content = \json_decode($this->client->getResponse()->getContent(), true); + $this->assertSame([ + 'id' => 42, + 'firstName' => 'John', + 'lastName' => 'Doe', + ], $content); + } + + public function testMultiplePayloadSourcePriority() + { + $this->client->request('PUT', '/person/42', [ + 'id' => 1, + 'firstName' => 'John', + 'lastName' => 'Doe', + ]); + + $content = \json_decode($this->client->getResponse()->getContent(), true); + $this->assertSame([ + 'id' => 42, + 'firstName' => 'John', + 'lastName' => 'Doe', + ], $content); + } +} diff --git a/Tests/Http/PayloadSourceTest.php b/Tests/Http/PayloadSourceTest.php new file mode 100644 index 0000000..724d3a7 --- /dev/null +++ b/Tests/Http/PayloadSourceTest.php @@ -0,0 +1,69 @@ +request = new Request( + [ + 'foo' => 123, + 'bar' => 'azerty', + ], + [ + 'baz' => 456, + 'qux' => 'ytreza', + ], + [ + 'quux' => 789, + 'corge' => 'qwerty', + ], + server: ['REQUEST_METHOD' => 'POST'] + ); + } + + public function testAuto() + { + $this->assertSame([ + 'baz' => 456, + 'qux' => 'ytreza', + ], PayloadSource::Auto->extract($this->request)); + + $this->request->setMethod('GET'); + $this->assertSame([ + 'foo' => 123, + 'bar' => 'azerty', + ], PayloadSource::Auto->extract($this->request)); + } + + public function testQueryString() + { + $this->assertSame([ + 'foo' => 123, + 'bar' => 'azerty', + ], PayloadSource::QueryString->extract($this->request)); + } + + public function testBody() + { + $this->assertSame([ + 'baz' => 456, + 'qux' => 'ytreza', + ], PayloadSource::Body->extract($this->request)); + } + + public function testAttribute() + { + $this->assertSame([ + 'quux' => 789, + 'corge' => 'qwerty', + ], PayloadSource::Attributes->extract($this->request)); + } +} diff --git a/Tests/Http/Submit/SubmitFormValueResolverTest.php b/Tests/Http/Submit/SubmitFormValueResolverTest.php new file mode 100644 index 0000000..71f1fc6 --- /dev/null +++ b/Tests/Http/Submit/SubmitFormValueResolverTest.php @@ -0,0 +1,208 @@ +resolver = new SubmitFormValueResolver(new Registry()); + } + + public function testResolveNoAttributeSubmitForm() + { + $metadata = new ArgumentMetadata('foo', null, false, false, null, false, [new \stdClass()]); + $this->assertSame([], $this->resolver->resolve(new Request(), $metadata)); + } + + public function testResolveFormTypeFromArgumentType() + { + $metadata = new ArgumentMetadata('foo', PersonForm::class, false, false, null, false, [new SubmitForm()]); + $actual = $this->resolver->resolve(new Request(), $metadata)[0]; + + $expected = new SubmitForm(form: PersonForm::class); + $expected->metadata = $metadata; + $expected->value = false; + $this->assertEquals($expected, $actual); + $this->assertFalse($actual->value); + } + + public function testResolveWithValueArgument() + { + $metadata = new ArgumentMetadata('foo', PersonDto::class, false, false, null, false, [new SubmitForm(form: PersonForm::class)]); + $actual = $this->resolver->resolve(new Request(), $metadata)[0]; + + $expected = new SubmitForm(form: PersonForm::class); + $expected->metadata = $metadata; + $expected->value = true; + $this->assertEquals($expected, $actual); + $this->assertTrue($actual->value); + } + + public function testResolveWithValueArgumentMissingForm() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The form class must be defined when the value is requested'); + + $metadata = new ArgumentMetadata('foo', PersonDto::class, false, false, null, false, [new SubmitForm()]); + $this->resolver->resolve(new Request(), $metadata)[0]; + } + + public function testResolveCannotResolveForm() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The form class must be defined as controller parameter type, or in the SubmitForm attribute'); + + $metadata = new ArgumentMetadata('foo', null, false, false, null, false, [new SubmitForm()]); + $this->resolver->resolve(new Request(), $metadata)[0]; + } + + public function testResolveExplicitlyDefinedParameters() + { + $metadata = new ArgumentMetadata('foo', null, false, false, null, false, [new SubmitForm( + form: PersonForm::class, + value: true, + validateMessage: 'My error', + )]); + $actual = $this->resolver->resolve(new Request(), $metadata)[0]; + + $expected = new SubmitForm( + form: PersonForm::class, + value: true, + validateMessage: 'My error', + ); + $expected->metadata = $metadata; + $expected->value = true; + $this->assertEquals($expected, $actual); + $this->assertTrue($actual->value); + } + + public function testOnKernelControllerArgumentsSuccess() + { + $kernel = $this->createMock(HttpKernelInterface::class); + $controller = fn () => null; + $request = new Request(['id' => 1, 'firstName' => 'John', 'lastName' => 'Doe']); + $argument = new SubmitForm(form: PersonForm::class); + $argument->metadata = new ArgumentMetadata('foo', PersonForm::class, false, false, null, false, [new SubmitForm()]); + $event = new ControllerArgumentsEvent($kernel, $controller, [$argument], $request, HttpKernelInterface::MAIN_REQUEST); + $resolver = new SubmitFormValueResolver(new Registry()); + + $resolver->onKernelControllerArguments($event); + + $this->assertInstanceOf(PersonForm::class, $event->getArguments()[0]); + $this->assertSame(1, $event->getArguments()[0]['id']->element()->value()); + $this->assertSame('John', $event->getArguments()[0]['firstName']->element()->value()); + $this->assertSame('Doe', $event->getArguments()[0]['lastName']->element()->value()); + } + + public function testOnKernelControllerArgumentsValidationError() + { + $kernel = $this->createMock(HttpKernelInterface::class); + $controller = fn () => null; + $request = new Request(['firstName' => 'John', 'lastName' => 'Doe']); + $argument = new SubmitForm(form: PersonForm::class); + $argument->metadata = new ArgumentMetadata('foo', PersonForm::class, false, false, null, false, [new SubmitForm()]); + $event = new ControllerArgumentsEvent($kernel, $controller, [$argument], $request, HttpKernelInterface::MAIN_REQUEST); + $resolver = new SubmitFormValueResolver(new Registry()); + + try { + $resolver->onKernelControllerArguments($event); + $this->fail('Expected exception not thrown'); + } catch (InvalidFormException $e) { + $this->assertSame(['id' => 'This value should not be blank.'], $e->error->toArray()); + } + } + + public function testOnKernelControllerArgumentsNotValidate() + { + $kernel = $this->createMock(HttpKernelInterface::class); + $controller = fn () => null; + $request = new Request(['firstName' => 'John', 'lastName' => 'Doe']); + $argument = new SubmitForm(form: PersonForm::class, validate: false); + $argument->metadata = new ArgumentMetadata('foo', PersonForm::class, false, false, null, false, [new SubmitForm()]); + $event = new ControllerArgumentsEvent($kernel, $controller, [$argument], $request, HttpKernelInterface::MAIN_REQUEST); + $resolver = new SubmitFormValueResolver(new Registry()); + $resolver->onKernelControllerArguments($event); + + $this->assertInstanceOf(PersonForm::class, $event->getArguments()[0]); + $this->assertNull($event->getArguments()[0]['id']->element()->value()); + $this->assertSame('John', $event->getArguments()[0]['firstName']->element()->value()); + $this->assertSame('Doe', $event->getArguments()[0]['lastName']->element()->value()); + $this->assertSame(['id' => 'This value should not be blank.'], $event->getArguments()[0]->error()->toArray()); + } + + public function testOnKernelControllerArgumentsValue() + { + $kernel = $this->createMock(HttpKernelInterface::class); + $controller = fn () => null; + $request = new Request(['id' => 1, 'firstName' => 'John', 'lastName' => 'Doe']); + $argument = new SubmitForm(form: PersonForm::class, value: true); + $argument->metadata = new ArgumentMetadata('foo', PersonDto::class, false, false, null, false, [new SubmitForm()]); + $event = new ControllerArgumentsEvent($kernel, $controller, [$argument], $request, HttpKernelInterface::MAIN_REQUEST); + $resolver = new SubmitFormValueResolver(new Registry()); + + $resolver->onKernelControllerArguments($event); + + $this->assertInstanceOf(PersonDto::class, $event->getArguments()[0]); + $this->assertSame(1, $event->getArguments()[0]->id); + $this->assertSame('John', $event->getArguments()[0]->firstName); + $this->assertSame('Doe', $event->getArguments()[0]->lastName); + } + + public function testOnKernelControllerArgumentsSuccessMultipleSource() + { + $kernel = $this->createMock(HttpKernelInterface::class); + $controller = fn () => null; + $request = new Request( + request: ['id' => 1, 'firstName' => 'John', 'lastName' => 'Doe'], + attributes: ['id' => 42], + ); + $argument = new SubmitForm(source: [PayloadSource::Attributes, PayloadSource::Body], form: PersonForm::class); + $argument->metadata = new ArgumentMetadata('foo', PersonForm::class, false, false, null, false, [new SubmitForm()]); + $event = new ControllerArgumentsEvent($kernel, $controller, [$argument], $request, HttpKernelInterface::MAIN_REQUEST); + $resolver = new SubmitFormValueResolver(new Registry()); + + $resolver->onKernelControllerArguments($event); + + $this->assertInstanceOf(PersonForm::class, $event->getArguments()[0]); + $this->assertSame(42, $event->getArguments()[0]['id']->element()->value()); + $this->assertSame('John', $event->getArguments()[0]['firstName']->element()->value()); + $this->assertSame('Doe', $event->getArguments()[0]['lastName']->element()->value()); + } + + public function testOnKernelControllerArgumentsSuccessJsonBody() + { + $kernel = $this->createMock(HttpKernelInterface::class); + $controller = fn () => null; + $request = new Request( + server: ['REQUEST_METHOD' => 'POST'], + content: \json_encode(['id' => 42, 'firstName' => 'John', 'lastName' => 'Doe']), + ); + $argument = new SubmitForm(form: PersonForm::class); + $argument->metadata = new ArgumentMetadata('foo', PersonForm::class, false, false, null, false, [new SubmitForm()]); + $event = new ControllerArgumentsEvent($kernel, $controller, [$argument], $request, HttpKernelInterface::MAIN_REQUEST); + $resolver = new SubmitFormValueResolver(new Registry()); + + $resolver->onKernelControllerArguments($event); + + $this->assertInstanceOf(PersonForm::class, $event->getArguments()[0]); + $this->assertSame(42, $event->getArguments()[0]['id']->element()->value()); + $this->assertSame('John', $event->getArguments()[0]['firstName']->element()->value()); + $this->assertSame('Doe', $event->getArguments()[0]['lastName']->element()->value()); + } +} diff --git a/Tests/Http/TestKernel.php b/Tests/Http/TestKernel.php new file mode 100644 index 0000000..fd3914a --- /dev/null +++ b/Tests/Http/TestKernel.php @@ -0,0 +1,92 @@ + $exception->getMessage(), + 'fields' => $exception->error->toArray(), + ], + $exception->getStatusCode(), + ['Content-Type' => 'application/json'] + ); + } + + if ($exception instanceof HttpException) { + return new Response( + $exception->getMessage(), + $exception->getStatusCode(), + $exception->getHeaders() + ); + } + + return new Response($exception->getMessage(), 500, ['Content-Type' => 'text/plain']); + } + + protected function configureRoutes(RoutingConfigurator $routes) + { + $routes->import(__FILE__, 'attribute'); + } + + protected function configureContainer(ContainerConfigurator $container, LoaderInterface $loader, ContainerBuilder $builder) + { + $loader->load(__DIR__.'/conf.yaml'); + } + + #[Route('/value', methods: ['POST'])] + public function value(#[SubmitForm(form: PersonForm::class)] PersonDto $dto): JsonResponse + { + return new JsonResponse($dto); + } + + #[Route('/form', methods: ['POST'])] + public function form(#[SubmitForm(validate: false)] PersonForm $form): JsonResponse + { + return new JsonResponse([ + 'value' => $form->valid() ? $form->value() : $form->httpValue(), + 'errors' => $form->error()->toArray(), + ]); + } + + #[Route('/person/{id}', methods: ['PUT'])] + public function person(#[SubmitForm(source: [PayloadSource::Attributes, PayloadSource::Body], form: PersonForm::class)] PersonDto $dto): JsonResponse + { + return new JsonResponse($dto); + } +} diff --git a/Tests/Http/conf.yaml b/Tests/Http/conf.yaml new file mode 100644 index 0000000..8080f42 --- /dev/null +++ b/Tests/Http/conf.yaml @@ -0,0 +1,16 @@ +framework: + csrf_protection: + enabled: true + + session: + handler_id: null + + error_controller: Bdf\Form\Bundle\Tests\Http\TestKernel::error + +services: + _defaults: + autowire: true + autoconfigure: true + + Bdf\Form\Bundle\Tests\Http\Form\: + resource: './Form/*' diff --git a/composer.json b/composer.json index 203eaa6..bb70106 100755 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "symfony/phpunit-bridge": "~5.0|~6.0|~7.0", "symfony/yaml": "~5.0|~6.0|~7.0", "symfony/security-csrf": "~5.0|~6.0|~7.0", + "symfony/browser-kit": "~5.0|~6.0|~7.0", "friendsofphp/php-cs-fixer": "~3.0" }, "suggest": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ffc625f..02dfeca 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -17,6 +17,10 @@ ./Tests/ + ./Tests/Http + + + Tests/Http From 7973134549cbe3c457c05b051817601dd847609f Mon Sep 17 00:00:00 2001 From: Vincent QUATREVIEUX Date: Thu, 27 Mar 2025 12:28:16 +0100 Subject: [PATCH 2/2] ci: run php-cs-fixer on php 8.1 --- .github/workflows/code-style.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml index d25e5a3..ac28830 100644 --- a/.github/workflows/code-style.yml +++ b/.github/workflows/code-style.yml @@ -9,14 +9,14 @@ on: jobs: run: runs-on: ubuntu-latest - name: PHP 8.0 + name: PHP 8.1 steps: - uses: actions/checkout@v2 - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: "8.0" + php-version: "8.1" extensions: json ini-values: post_max_size=256M coverage: xdebug