diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3ea19c4..c8311f5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,17 +36,16 @@ jobs: - deps: 'dev' php: 8.3 symfony: '6.x' - laravel: '11.x' + laravel: '10.x' experimental: true composer: preview - # TODO enable when Laravel allows Symfony 7 - #- deps: 'dev' - # php: 8.3 - # symfony: '7.x' - # laravel: '11.x' - # experimental: true - # composer: preview + - deps: 'dev' + php: 8.3 + symfony: '7.x' + laravel: '11.x' + experimental: true + composer: preview steps: - uses: actions/checkout@v2 @@ -85,12 +84,12 @@ jobs: echo "LARAVEL_REQUIRE=$(echo '${{ matrix.laravel }}' | tr x \\*)" >> $GITHUB_ENV fi - if [ "${{ matrix.laravel }}" = "7.x" ]; then - echo "SYMFONY_DEPRECATIONS_HELPER=max[direct]=5" >> $GITHUB_ENV - fi + if [ "${{ matrix.symfony }}" = "7.x" ]; then + # Psalm Symfony Plugin doesn't support Symfony 7 yet + composer remove --no-update --dev vimeo/psalm psalm/plugin-symfony - if [ "${{ matrix.php }}" = "8.1" ]; then - echo "SYMFONY_PHPUNIT_VERSION=9.5" >> $GITHUB_ENV + # Carbon needs version 3 + composer require --no-update nesbot/carbon:"3.x-dev as 2.99.0" fi echo "PHP_VERSION=${{ matrix.php }}" >> $GITHUB_ENV diff --git a/composer.json b/composer.json index 54ca2a2..394a64a 100644 --- a/composer.json +++ b/composer.json @@ -10,29 +10,29 @@ "illuminate/database": "^9.0 || ^10.0", "illuminate/events": "^9.0 || ^10.0", "illuminate/console": "^9.39 || ^10.0", - "symfony/framework-bundle": "^6.0", - "symfony/dependency-injection": "^6.0", + "symfony/framework-bundle": "^6.0 || ^7.0", + "symfony/dependency-injection": "^6.0 || ^7.0", "jdorn/sql-formatter": "^1.2.17" }, "require-dev": { "doctrine/annotations": "1.*", "symfony/maker-bundle": "^1.44", "mockery/mockery": "^1.6", - "symfony/console": "^6.0", - "symfony/event-dispatcher": "^6.0", - "symfony/http-kernel": "^6.0", - "symfony/finder": "^6.0", - "symfony/yaml": "^6.0", - "symfony/form": "^6.0", - "symfony/phpunit-bridge": "^6.0", - "symfony/browser-kit": "^6.0", - "symfony/dom-crawler": "^6.0", - "symfony/validator": "^6.0", - "symfony/security-bundle": "^6.0", - "symfony/twig-bundle": "^6.0", - "symfony/twig-bridge": "^6.0", - "symfony/var-dumper": "^6.0", - "symfony/process": "^6.0", + "symfony/console": "^6.0 || ^7.0", + "symfony/event-dispatcher": "^6.0 || ^7.0", + "symfony/http-kernel": "^6.0 || ^7.0", + "symfony/finder": "^6.0 || ^7.0", + "symfony/yaml": "^6.0 || ^7.0", + "symfony/form": "^6.0 || ^7.0", + "symfony/phpunit-bridge": "^6.0 || ^7.0", + "symfony/browser-kit": "^6.0 || ^7.0", + "symfony/dom-crawler": "^6.0 || ^7.0", + "symfony/validator": "^6.0 || ^7.0", + "symfony/security-bundle": "^6.0 || ^7.0", + "symfony/twig-bundle": "^6.0 || ^7.0", + "symfony/twig-bridge": "^6.0 || ^7.0", + "symfony/var-dumper": "^6.0 || ^7.0", + "symfony/process": "^6.0 || ^7.0", "twig/twig": "^1.26 || ^2.0 || ^3.0", "vimeo/psalm": "^3.18.2 || ^4.0", "psalm/plugin-symfony": "^1.5.0 || ^2.0 || ^3.0" diff --git a/tests/Command/SeedCommandTest.php b/tests/Command/SeedCommandTest.php index efb1454..b3207ea 100644 --- a/tests/Command/SeedCommandTest.php +++ b/tests/Command/SeedCommandTest.php @@ -54,7 +54,7 @@ public function it_executes_specified_classes() ->duringExecute() ; if (class_exists(Components\Task::class)) { - $test->outputsRegex('/'.preg_quote($seederClass).' \.+ \d+ms DONE\s+'.preg_quote($seeder1Class).' \.+ \d+ms DONE/'); + $test->outputsRegex('/'.preg_quote($seederClass).'[\s\.]* [\d\.]+ms DONE\s+'.preg_quote($seeder1Class).'[\s\.]* [\d\.]+ms DONE/'); } else { // BC Laravel <9.39 $test->outputsRegex('/RUNNING: '.preg_quote($seederClass).'\s+DONE: '.preg_quote($seederClass).' \(\d+ms\)/'); diff --git a/tests/Functional/FormTest.php b/tests/Functional/FormTest.php index 6b21de8..7a8b515 100644 --- a/tests/Functional/FormTest.php +++ b/tests/Functional/FormTest.php @@ -39,14 +39,20 @@ public function testFormTypeGuessing() $inputs[trim(str_replace('form_', '', $node->attr('id')), '[]')] = 'select'; }); - $this->assertEquals([ + $expectedTypes = [ 'name' => 'text', 'password' => 'text', - 'date_of_birth_year' => 'select', - 'date_of_birth_month' => 'select', - 'date_of_birth_day' => 'select', 'is_admin' => 'checkbox', - ], $inputs); + 'date_of_birth' => 'date', + ]; + if (!\array_key_exists('date_of_birth', $inputs)) { + $expectedTypes['date_of_birth_year'] = 'select'; + $expectedTypes['date_of_birth_month'] = 'select'; + $expectedTypes['date_of_birth_day'] = 'select'; + unset($expectedTypes['date_of_birth']); + } + + $this->assertEquals($expectedTypes, $inputs); } public function testFormSubmission() @@ -54,14 +60,20 @@ public function testFormSubmission() $birthDay = new \DateTimeImmutable('-3 years'); $formView = $this->client->request('GET', '/user/create'); - $form = $formView->selectButton('Submit')->form([ - 'form[name]' => 'John Doe', - 'form[password]' => 's3cr3t', - 'form[date_of_birth][year]' => $birthDay->format('Y'), - 'form[date_of_birth][month]' => $birthDay->format('n'), - 'form[date_of_birth][day]' => $birthDay->format('j'), - 'form[is_admin]' => false, - ]); + $form = $formView->selectButton('Submit')->form(); + + $form['form[name]'] = 'John Doe'; + $form['form[password]'] = 's3cr3t'; + if (!isset($form['form[date_of_birth][year]'])) { + $form['form[date_of_birth]'] = $birthDay->format('Y-n-j'); + } else { + // BC for Symfony <7 + $form['form[date_of_birth][year]'] = $birthDay->format('Y'); + $form['form[date_of_birth][month]'] = $birthDay->format('n'); + $form['form[date_of_birth][day]'] = $birthDay->format('j'); + } + $form['form[is_admin]'] = false; + $this->client->submit($form); $user = CastingUser::where(['name' => 'John Doe'])->first(); @@ -77,14 +89,18 @@ public function testFormSubmission() public function testFormValidation() { $formView = $this->client->request('GET', '/user/create'); - $form = $formView->selectButton('Submit')->form([ - 'form[name]' => '', - 'form[password]' => 's3cr3t', - 'form[date_of_birth][year]' => (new \DateTimeImmutable())->format('Y'), - 'form[date_of_birth][month]' => '10', - 'form[date_of_birth][day]' => '20', - 'form[is_admin]' => false, - ]); + $form = $formView->selectButton('Submit')->form(); + $form['form[name]'] = ''; + $form['form[password]'] = 's3cr3t'; + $form['form[is_admin]'] = false; + if (!isset($form['form[date_of_birth][year]'])) { + $form['form[date_of_birth]'] = (new \DateTimeImmutable())->format('Y').'-10-20'; + } else { + // BC for Symfony <7 + $form['form[date_of_birth][year]'] = (new \DateTimeImmutable())->format('Y'); + $form['form[date_of_birth][month]'] = '10'; + $form['form[date_of_birth][day]'] = '20'; + } $crawler = $this->client->submit($form); $this->assertCount(1, $crawler->filterXPath('//li[text()="The username should not be blank."]')); diff --git a/tests/Functional/app/TestKernel.php b/tests/Functional/app/TestKernel.php index 09256e7..6d088d6 100644 --- a/tests/Functional/app/TestKernel.php +++ b/tests/Functional/app/TestKernel.php @@ -12,11 +12,12 @@ use AppBundle\Controller\FormController; use AppBundle\Model\User; use AppBundle\Model\UserObserver; +use Symfony\Bundle\FrameworkBundle\Routing\AttributeRouteControllerLoader; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\HttpKernel\Log\Logger; -use Symfony\Component\PasswordHasher\PasswordHasherInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\Authentication\AuthenticatorManager; /** @@ -46,7 +47,7 @@ public function registerContainerConfiguration(LoaderInterface $loader): void $container->loadFromExtension('framework', [ 'secret' => 'abc123', 'router' => ['resource' => __DIR__.'/routes.yml', 'utf8' => true], - 'validation' => ['enable_annotations' => true], + 'validation' => [(class_exists(AttributeRouteControllerLoader::class) ? 'enable_attributes' : 'enable_annotations') => true], 'annotations' => PHP_VERSION_ID < 80000, 'test' => true, 'form' => true, @@ -73,13 +74,10 @@ public function registerContainerConfiguration(LoaderInterface $loader): void ], 'password_hashers' => [User::class => 'plaintext'], ]; - if (class_exists(AuthenticatorManager::class)) { + if (class_exists(AuthenticatorManager::class) && class_exists(Security::class)) { + // Symfony >5.4, <7.0 $securityConfig['enable_authenticator_manager'] = true; } - if (!interface_exists(PasswordHasherInterface::class)) { - $securityConfig['encoders'] = $securityConfig['password_hashers']; - unset($securityConfig['password_hashers']); - } $container->loadFromExtension('security', $securityConfig); $container->loadFromExtension('wouterj_eloquent', [