diff options
56 files changed, 582 insertions, 507 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f34230927327..6f2f29a15d35 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -281,7 +281,7 @@ default: _TARGET_PHP: "8.3-ubuntu" _TARGET_DB: "mysql-8.4" -'PHP 8.4 MySQL 9.2': +'PHP 8.4 MySQL 9.3': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.4-ubuntu" diff --git a/composer.lock b/composer.lock index ffcdc3b2a779..fa0d22b3c177 100644 --- a/composer.lock +++ b/composer.lock @@ -5755,16 +5755,16 @@ }, { "name": "mglaman/phpstan-drupal", - "version": "2.0.4", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/mglaman/phpstan-drupal.git", - "reference": "9a31538197bd14bddf6dc71032d0f083d05c91e7" + "reference": "c50f146e1dbb331c800ddfed38cdf482e4ea0052" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mglaman/phpstan-drupal/zipball/9a31538197bd14bddf6dc71032d0f083d05c91e7", - "reference": "9a31538197bd14bddf6dc71032d0f083d05c91e7", + "url": "https://api.github.com/repos/mglaman/phpstan-drupal/zipball/c50f146e1dbb331c800ddfed38cdf482e4ea0052", + "reference": "c50f146e1dbb331c800ddfed38cdf482e4ea0052", "shasum": "" }, "require": { @@ -5836,7 +5836,7 @@ "description": "Drupal extension and rules for PHPStan", "support": { "issues": "https://github.com/mglaman/phpstan-drupal/issues", - "source": "https://github.com/mglaman/phpstan-drupal/tree/2.0.4" + "source": "https://github.com/mglaman/phpstan-drupal/tree/2.0.5" }, "funding": [ { @@ -5852,7 +5852,7 @@ "type": "tidelift" } ], - "time": "2025-04-10T15:38:04+00:00" + "time": "2025-04-15T16:10:24+00:00" }, { "name": "micheh/phpcs-gitlab", @@ -7307,16 +7307,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.11", + "version": "2.1.12", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30" + "reference": "96dde49e967c0c22812bcfa7bda4ff82c09f3b0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30", - "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/96dde49e967c0c22812bcfa7bda4ff82c09f3b0c", + "reference": "96dde49e967c0c22812bcfa7bda4ff82c09f3b0c", "shasum": "" }, "require": { @@ -7361,7 +7361,7 @@ "type": "github" } ], - "time": "2025-03-24T13:45:00+00:00" + "time": "2025-04-16T13:19:18+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", diff --git a/composer/Metapackage/PinnedDevDependencies/composer.json b/composer/Metapackage/PinnedDevDependencies/composer.json index 81eb3d8aa60b..5c6ea5490260 100644 --- a/composer/Metapackage/PinnedDevDependencies/composer.json +++ b/composer/Metapackage/PinnedDevDependencies/composer.json @@ -26,7 +26,7 @@ "justinrainbow/json-schema": "5.3.0", "lullabot/mink-selenium2-driver": "v1.7.4", "lullabot/php-webdriver": "v2.0.6", - "mglaman/phpstan-drupal": "2.0.4", + "mglaman/phpstan-drupal": "2.0.5", "micheh/phpcs-gitlab": "1.1.0", "mikey179/vfsstream": "v1.6.12", "myclabs/deep-copy": "1.12.1", @@ -51,7 +51,7 @@ "phpspec/prophecy-phpunit": "v2.3.0", "phpstan/extension-installer": "1.4.3", "phpstan/phpdoc-parser": "1.33.0", - "phpstan/phpstan": "2.1.11", + "phpstan/phpstan": "2.1.12", "phpstan/phpstan-deprecation-rules": "2.0.1", "phpstan/phpstan-phpunit": "2.0.6", "phpunit/php-code-coverage": "10.1.16", diff --git a/core/.deprecation-ignore.txt b/core/.deprecation-ignore.txt index 72eb56119715..0e70057ddac8 100644 --- a/core/.deprecation-ignore.txt +++ b/core/.deprecation-ignore.txt @@ -33,7 +33,7 @@ %Since symfony/http-foundation 7.2: NativeSessionStorage's "sid_length" option is deprecated and will be ignored in Symfony 8.0.% %Since symfony/http-foundation 7.2: NativeSessionStorage's "sid_bits_per_character" option is deprecated and will be ignored in Symfony 8.0.% -# PHPUnit 12. +# Drupal 12. %The "Drupal\\Core\\Database\\Query\\Select::hasAllTags\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface% %The "Drupal\\Core\\Database\\Query\\Select::hasAnyTag\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface% %The "Drupal\\Core\\Database\\Query\\SelectExtender::hasAllTags\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface% diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php index 6f4a52139f11..588cde82f739 100644 --- a/core/.phpstan-baseline.php +++ b/core/.phpstan-baseline.php @@ -12508,78 +12508,6 @@ $ignoreErrors[] = [ 'path' => __DIR__ . '/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5DialogTest\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5DialogTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5DialogTest\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5DialogTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5DialogTest\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5DialogTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5DialogTest\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5DialogTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5MarkupTest\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5MarkupTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5MarkupTest\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5MarkupTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5MarkupTest\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5MarkupTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5MarkupTest\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5MarkupTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5Test\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5Test\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5Test\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5Test\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php', -]; -$ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\CKEditor5TestBase\\:\\:assertHtmlEsqueFieldValueEquals\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', 'count' => 1, @@ -12604,60 +12532,12 @@ $ignoreErrors[] = [ 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5TestBase.php', ]; $ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\EmphasisTest\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/EmphasisTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\EmphasisTest\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/EmphasisTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\EmphasisTest\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/EmphasisTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\EmphasisTest\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/EmphasisTest.php', -]; -$ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\ImageTestBase\\:\\:addImage\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', 'count' => 1, 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTestBase.php', ]; $ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\ImageTestBase\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\ImageTestBase\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\ImageTestBase\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\ImageTestBase\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTestBase.php', -]; -$ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\ImageTestProviderTest\\:\\:providerAlignment\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', 'count' => 1, @@ -12676,132 +12556,12 @@ $ignoreErrors[] = [ 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/ImageUrlProviderTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaLibraryTest\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaLibraryTest\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaLibraryTest\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaLibraryTest\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php', -]; -$ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaPreviewTest\\:\\:previewAccessProvider\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', 'count' => 1, 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaPreviewTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaTestBase\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaTestBase\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaTestBase\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\MediaTestBase\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\SourceEditingTestBase\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/SourceEditingTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\SourceEditingTestBase\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/SourceEditingTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\SourceEditingTestBase\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/SourceEditingTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\SourceEditingTestBase\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/SourceEditingTestBase.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\StyleTest\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/StyleTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\StyleTest\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/StyleTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\StyleTest\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/StyleTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\StyleTest\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/StyleTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\TableTest\\:\\:assertEditorButtonDisabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/TableTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\TableTest\\:\\:assertEditorButtonEnabled\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/TableTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\TableTest\\:\\:pressEditorButton\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/TableTest.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\FunctionalJavascript\\\\TableTest\\:\\:waitForEditor\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/modules/ckeditor5/tests/src/FunctionalJavascript/TableTest.php', -]; -$ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\Tests\\\\ckeditor5\\\\Kernel\\\\CKEditor5PluginManagerTest\\:\\:assertConfigSchema\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', 'count' => 1, diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php index 41bd043c5c8f..489b2f5f94d1 100644 --- a/core/lib/Drupal/Core/Database/Connection.php +++ b/core/lib/Drupal/Core/Database/Connection.php @@ -1315,8 +1315,8 @@ abstract class Connection { * @param string $url * The URL. * @param string $root - * The root directory of the Drupal installation. Some database drivers, - * like for example SQLite, need this information. + * (deprecated) The root directory of the Drupal installation. Some + * database drivers, like for example SQLite, need this information. * * @return array * The connection options. @@ -1332,6 +1332,10 @@ abstract class Connection { * @see \Drupal\Core\Database\Database::convertDbUrlToConnectionInfo() */ public static function createConnectionOptionsFromUrl($url, $root) { + if ($root !== NULL) { + @trigger_error("Passing the \$root value to " . __METHOD__ . "() is deprecated in drupal:11.2.0 and will be removed in drupal:12.0.0. There is no replacement. See https://www.drupal.org/node/3511287", E_USER_DEPRECATED); + } + $url_components = parse_url($url); if (!isset($url_components['scheme'], $url_components['host'], $url_components['path'])) { throw new \InvalidArgumentException("The database connection URL '$url' is invalid. The minimum requirement is: 'driver://host/database'"); diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php index 18c893d40164..e76bc2d6991f 100644 --- a/core/lib/Drupal/Core/Database/Database.php +++ b/core/lib/Drupal/Core/Database/Database.php @@ -559,7 +559,7 @@ abstract class Database { $additional_class_loader->register(TRUE); - $options = $connection_class::createConnectionOptionsFromUrl($url, $root); + $options = $connection_class::createConnectionOptionsFromUrl($url, NULL); // Add the necessary information to autoload code. // @see \Drupal\Core\Site\Settings::initialize() diff --git a/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php b/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php index 066bee922b40..4d0b72d49d88 100644 --- a/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php +++ b/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php @@ -949,21 +949,6 @@ class ExtensionMimeTypeGuesser implements MimeTypeGuesserInterface { * {@inheritdoc} */ public function guessMimeType($path): ?string { - if (!isset($this->fileSystem)) { - @trigger_error( - 'Calling ' . __METHOD__ . '() without the file_system service already injected is deprecated in drupal:11.2.0 and throws an exception in drupal:12.0.0. See https://www.drupal.org/node/3494040', - E_USER_DEPRECATED - ); - $this->fileSystem = \Drupal::service(FileSystemInterface::class); - } - if (!isset($this->map)) { - @trigger_error( - 'Calling ' . __METHOD__ . '() without the MimeTypeMapInterface service already injected is deprecated in drupal:11.2.0 and throws an exception in drupal:12.0.0. See https://www.drupal.org/node/3494040', - E_USER_DEPRECATED - ); - $this->map = \Drupal::service(MimeTypeMapInterface::class); - } - $extension = ''; $file_parts = explode('.', $this->fileSystem->basename($path)); diff --git a/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php new file mode 100644 index 000000000000..b038960ed808 --- /dev/null +++ b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraint.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Menu\Plugin\Validation\Constraint; + +use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\Core\Validation\Attribute\Constraint; +use Drupal\Core\Validation\Plugin\Validation\Constraint\RangeConstraint; + +/** + * Validates the link depth of a menu tree. + */ +#[Constraint( + id: 'MenuLinkDepth', + label: new TranslatableMarkup('Menu link depth', options: ['context' => 'Validation']), + type: ['integer'], +)] +class MenuLinkDepthConstraint extends RangeConstraint { + + /** + * The initial level of menu items that are being exposed (zero-based). + * + * @var string|int + */ + public string|int $baseLevel = 0; + +} diff --git a/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraintValidator.php b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraintValidator.php new file mode 100644 index 000000000000..e6733b22c194 --- /dev/null +++ b/core/lib/Drupal/Core/Menu/Plugin/Validation/Constraint/MenuLinkDepthConstraintValidator.php @@ -0,0 +1,68 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Menu\Plugin\Validation\Constraint; + +use Drupal\Core\Config\Schema\TypeResolver; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Menu\MenuLinkTreeInterface; +use Drupal\Core\Validation\Plugin\Validation\Constraint\RangeConstraintValidator; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\RuntimeException; + +/** + * Validates the MenuLinkDepthConstraint constraint. + */ +class MenuLinkDepthConstraintValidator extends RangeConstraintValidator implements ContainerInjectionInterface { + + public function __construct( + protected readonly MenuLinkTreeInterface $menuLinkTree, + ) {} + + /** + * {@inheritdoc} + */ + public function validate(mixed $value, Constraint $constraint): void { + assert($constraint instanceof MenuLinkDepthConstraint); + + // The depth can never exceed the maximum depth of a menu tree. + $constraint->max = $this->menuLinkTree->maxDepth(); + + $base_level = $constraint->baseLevel; + // The base level might be a dynamic name that needs to be resolved. + if (is_string($base_level)) { + $base_level = TypeResolver::resolveDynamicTypeName($base_level, $this->context->getObject()); + } + if (!is_numeric($base_level)) { + throw new RuntimeException('The `base` option must be a number, or an expression that resolves to one.'); + } + // Clamp $base_level (which is zero-based) to at least 0 and no more than + // the maximum depth of a menu tree. + $base_level = max(0, $base_level); + $base_level = min($base_level, $constraint->max); + + $constraint->max -= $base_level; + // We're validating a depth -- i.e., the number of levels in the menu tree + // that should be visible. 0 effectively means "show only one level", but + // it's not a valid value: we can't show 0 levels of the tree, after all ( + // the minimum number of levels we *could* show is 1). The discrepancy is + // due to $base_level being zero-based, so adjust for that. + if ($constraint->max === 0) { + $constraint->max = 1; + } + + parent::validate($value, $constraint); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container): static { + return new static( + $container->get(MenuLinkTreeInterface::class), + ); + } + +} diff --git a/core/lib/Drupal/Core/Password/PhpassHashedPasswordBase.php b/core/lib/Drupal/Core/Password/PhpassHashedPasswordBase.php index f659c367bb4e..9ec9e7990428 100644 --- a/core/lib/Drupal/Core/Password/PhpassHashedPasswordBase.php +++ b/core/lib/Drupal/Core/Password/PhpassHashedPasswordBase.php @@ -221,11 +221,8 @@ abstract class PhpassHashedPasswordBase implements PasswordInterface { break; default: - if (isset($this->corePassword)) { - return $this->corePassword->check($password, $stored_hash); - } + return $this->corePassword->check($password, $stored_hash); - return FALSE; } // Compare using hash_equals() instead of === to mitigate timing attacks. diff --git a/core/lib/Drupal/Core/Theme/Component/ComponentMetadata.php b/core/lib/Drupal/Core/Theme/Component/ComponentMetadata.php index f5c2866bd605..beccc9671089 100644 --- a/core/lib/Drupal/Core/Theme/Component/ComponentMetadata.php +++ b/core/lib/Drupal/Core/Theme/Component/ComponentMetadata.php @@ -125,7 +125,7 @@ class ComponentMetadata { $this->group = $metadata_info['group'] ?? $this->t('All Components'); // Save the schemas. - $this->parseSchemaInfo($metadata_info); + $this->schema = $this->parseSchemaInfo($metadata_info); $this->slots = $metadata_info['slots'] ?? []; } @@ -135,9 +135,12 @@ class ComponentMetadata { * @param array $metadata_info * The metadata information as decoded from the component definition file. * + * @return array|null + * The schema for the component props. + * * @throws \Drupal\Core\Render\Component\Exception\InvalidComponentException */ - private function parseSchemaInfo(array $metadata_info): void { + private function parseSchemaInfo(array $metadata_info): ?array { if (empty($metadata_info['props'])) { if ($this->mandatorySchemas) { throw new InvalidComponentException(sprintf('The component "%s" does not provide schema information. Schema definitions are mandatory for components declared in modules. For components declared in themes, schema definitions are only mandatory if the "enforce_prop_schemas" key is set to "true" in the theme info file.', $metadata_info['id'])); @@ -164,7 +167,7 @@ class ComponentMetadata { ]); } } - $this->schema = $schema; + return $schema; } /** diff --git a/core/modules/announcements_feed/tests/src/FunctionalJavascript/AlertsJsonFeedTest.php b/core/modules/announcements_feed/tests/src/FunctionalJavascript/AlertsJsonFeedTest.php index a4b09d7feaee..39edf53e90c1 100644 --- a/core/modules/announcements_feed/tests/src/FunctionalJavascript/AlertsJsonFeedTest.php +++ b/core/modules/announcements_feed/tests/src/FunctionalJavascript/AlertsJsonFeedTest.php @@ -41,6 +41,10 @@ class AlertsJsonFeedTest extends OffCanvasTestBase { * {@inheritdoc} */ public function setUp():void { + if ($this->name() === 'testAnnounceFeedUpdatedAndRemoved') { + $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); + } + parent::setUp(); $this->user = $this->drupalCreateUser( @@ -57,7 +61,6 @@ class AlertsJsonFeedTest extends OffCanvasTestBase { * Check the status of the announcements when the feed is updated and removed. */ public function testAnnounceFeedUpdatedAndRemoved(): void { - $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); $this->drupalLogin($this->user); $this->drupalGet('<front>'); $this->clickLink('Announcements'); diff --git a/core/modules/announcements_feed/tests/src/Kernel/AnnounceFetcherTest.php b/core/modules/announcements_feed/tests/src/Kernel/AnnounceFetcherTest.php index d2c70ae8d760..0d144f0878f9 100644 --- a/core/modules/announcements_feed/tests/src/Kernel/AnnounceFetcherTest.php +++ b/core/modules/announcements_feed/tests/src/Kernel/AnnounceFetcherTest.php @@ -17,6 +17,7 @@ class AnnounceFetcherTest extends AnnounceTestBase { * {@inheritdoc} */ protected function setUp(): void { + $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); parent::setUp(); $this->installConfig(['announcements_feed']); } @@ -31,7 +32,6 @@ class AnnounceFetcherTest extends AnnounceTestBase { * @dataProvider providerShowAnnouncements */ public function testShowAnnouncements(array $feed_item): void { - $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); $this->setFeedItems([$feed_item]); $feeds = $this->fetchFeedItems(); $this->assertCount(1, $feeds); @@ -45,7 +45,6 @@ class AnnounceFetcherTest extends AnnounceTestBase { * Tests feed fields. */ public function testFeedFields(): void { - $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); $feed_item_1 = [ 'id' => '1001', 'content_html' => 'Test teaser 1', diff --git a/core/modules/block/block.post_update.php b/core/modules/block/block.post_update.php index d1fd8e5344a4..8949a4bca5da 100644 --- a/core/modules/block/block.post_update.php +++ b/core/modules/block/block.post_update.php @@ -34,3 +34,22 @@ function block_post_update_make_weight_integer(array &$sandbox = []): void { return FALSE; }); } + +/** + * Updates the `depth` setting to NULL if it is 0 in any menu blocks. + */ +function block_post_update_set_menu_block_depth_to_null_if_zero(array &$sandbox = []): void { + \Drupal::classResolver(ConfigEntityUpdater::class) + ->update($sandbox, 'block', function (BlockInterface $block): bool { + if ($block->getPlugin()->getBaseId() === 'system_menu_block') { + $settings = $block->get('settings'); + // Use `empty()` to account for either integer 0, or '0'. + if (empty($settings['depth'])) { + $settings['depth'] = NULL; + } + $block->set('settings', $settings); + return TRUE; + } + return FALSE; + }); +} diff --git a/core/modules/block/migrations/d6_block.yml b/core/modules/block/migrations/d6_block.yml index 02a6890f724f..74922444e8df 100644 --- a/core/modules/block/migrations/d6_block.yml +++ b/core/modules/block/migrations/d6_block.yml @@ -127,6 +127,6 @@ destination: plugin: entity:block migration_dependencies: required: - - menu + - d6_menu - d6_custom_block - d6_user_role diff --git a/core/modules/block/src/Plugin/migrate/process/BlockSettings.php b/core/modules/block/src/Plugin/migrate/process/BlockSettings.php index e88997fdd975..988fe7a403ab 100644 --- a/core/modules/block/src/Plugin/migrate/process/BlockSettings.php +++ b/core/modules/block/src/Plugin/migrate/process/BlockSettings.php @@ -2,11 +2,14 @@ namespace Drupal\block\Plugin\migrate\process; +use Drupal\Core\Block\BlockManagerInterface; use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\migrate\Attribute\MigrateProcess; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\Row; +use Symfony\Component\DependencyInjection\ContainerInterface; // cspell:ignore whois @@ -14,7 +17,36 @@ use Drupal\migrate\Row; * Determines the block settings. */ #[MigrateProcess('block_settings')] -class BlockSettings extends ProcessPluginBase { +class BlockSettings extends ProcessPluginBase implements ContainerFactoryPluginInterface { + + /** + * The block manager service. + * + * @var \Drupal\Core\Block\BlockManagerInterface + */ + protected readonly BlockManagerInterface $blockManager; + + public function __construct(array $configuration, $plugin_id, $plugin_definition, ?BlockManagerInterface $blockManager = NULL) { + if (empty($blockManager)) { + @trigger_error('Calling ' . __METHOD__ . '() without the $blockManager parameter is deprecated in drupal:11.2.0 and must be provided in drupal:12.0.0. See https://www.drupal.org/node/3522023', E_USER_DEPRECATED); + $blockManager = \Drupal::service(BlockManagerInterface::class); + } + $this->blockManager = $blockManager; + + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get(BlockManagerInterface::class), + ); + } /** * {@inheritdoc} @@ -61,6 +93,11 @@ class BlockSettings extends ProcessPluginBase { $settings['items_per_page'] = $old_settings['user']['max_list_count']; break; } + + // Let the block plugin fill in any missing settings. + $settings = $this->blockManager->createInstance($plugin, $settings) + ->getConfiguration(); + return $settings; } diff --git a/core/modules/block/tests/fixtures/update/add-menu-block-with-zero-depth.php b/core/modules/block/tests/fixtures/update/add-menu-block-with-zero-depth.php new file mode 100644 index 000000000000..fbef4691481f --- /dev/null +++ b/core/modules/block/tests/fixtures/update/add-menu-block-with-zero-depth.php @@ -0,0 +1,26 @@ +<?php + +/** + * @file + * Adds a menu block with a `depth` setting of 0. + */ + +use Drupal\Core\Database\Database; + +$connection = Database::getConnection(); + +$data = $connection->select('config') + ->condition('name', 'block.block.olivero_account_menu') + ->fields('config', ['data']) + ->execute() + ->fetchField(); +$data = unserialize($data); +// Change the depth setting to 0, which the update hook should change to NULL. +// @see system_post_update_set_menu_block_depth_to_null_if_zero(). +$data['settings']['depth'] = 0; +$connection->update('config') + ->condition('name', 'block.block.olivero_account_menu') + ->fields([ + 'data' => serialize($data), + ]) + ->execute(); diff --git a/core/modules/block/tests/src/Functional/Update/MenuBlockDepthTest.php b/core/modules/block/tests/src/Functional/Update/MenuBlockDepthTest.php new file mode 100644 index 000000000000..31abf0c6455e --- /dev/null +++ b/core/modules/block/tests/src/Functional/Update/MenuBlockDepthTest.php @@ -0,0 +1,42 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\block\Functional\Update; + +use Drupal\block\Entity\Block; +use Drupal\FunctionalTests\Update\UpdatePathTestBase; + +/** + * Tests update path for the `depth` setting of menu blocks. + * + * @group system + */ +final class MenuBlockDepthTest extends UpdatePathTestBase { + + /** + * {@inheritdoc} + */ + protected function setDatabaseDumpFiles(): void { + $this->databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-10.3.0.bare.standard.php.gz', + __DIR__ . '/../../../fixtures/update/add-menu-block-with-zero-depth.php', + ]; + } + + /** + * Tests that menu blocks with a `depth` setting of 0 are changed to NULL. + */ + public function testUpdate(): void { + $settings = Block::load('olivero_account_menu')?->get('settings'); + $this->assertIsArray($settings); + $this->assertSame(0, $settings['depth']); + + $this->runUpdates(); + + $settings = Block::load('olivero_account_menu')?->get('settings'); + $this->assertIsArray($settings); + $this->assertNull($settings['depth']); + } + +} diff --git a/core/modules/block/tests/src/Kernel/BlockValidationTest.php b/core/modules/block/tests/src/Kernel/BlockValidationTest.php index 2456e7fb5764..26992413ac74 100644 --- a/core/modules/block/tests/src/Kernel/BlockValidationTest.php +++ b/core/modules/block/tests/src/Kernel/BlockValidationTest.php @@ -180,4 +180,92 @@ class BlockValidationTest extends ConfigEntityValidationTestBase { $this->entity->save(); } + /** + * Data provider for ::testMenuBlockLevelAndDepth(). + */ + public static function providerMenuBlockLevelAndDepth(): iterable { + yield 'OK: entire tree from first level' => [0, NULL, []]; + + yield 'OK: entire tree from third level' => [2, NULL, []]; + + yield 'OK: first three levels' => [0, 3, []]; + + yield 'INVALID: level is less than 0' => [ + -2, + NULL, + [ + 'settings.level' => 'This value should be between <em class="placeholder">0</em> and <em class="placeholder">9</em>.', + ], + ]; + + yield 'INVALID: level is greater than 9' => [ + 11, + NULL, + [ + 'settings.level' => 'This value should be between <em class="placeholder">0</em> and <em class="placeholder">9</em>.', + ], + ]; + + yield 'INVALID: depth too high' => [ + 0, + 12, + [ + 'settings.depth' => 'This value should be between <em class="placeholder">1</em> and <em class="placeholder">9</em>.', + ], + ]; + + yield 'INVALID: depth too low' => [ + 0, + 0, + [ + 'settings.depth' => 'This value should be between <em class="placeholder">1</em> and <em class="placeholder">9</em>.', + ], + ]; + + yield 'INVALID: start at third level, depth too high' => [ + 2, + 9, + [ + 'settings.depth' => 'This value should be between <em class="placeholder">1</em> and <em class="placeholder">7</em>.', + ], + ]; + + yield 'OK: deepest level only' => [9, 1, []]; + + yield 'INVALID: start at deepest level, depth too high' => [ + 9, + 2, + [ + 'settings.depth' => 'This value should be between <em class="placeholder">1</em> and <em class="placeholder">1</em>.', + ], + ]; + } + + /** + * Tests validating menu block `level` and `depth` settings. + * + * @dataProvider providerMenuBlockLevelAndDepth + */ + public function testMenuBlockLevelAndDepth(int $level, ?int $depth, array $expected_errors): void { + $this->installConfig('system'); + + $this->entity = Block::create([ + 'id' => 'account_menu', + 'theme' => 'stark', + 'plugin' => 'system_menu_block:account', + 'settings' => [ + 'id' => 'system_menu_block:account', + 'label' => 'Account Menu', + 'label_display' => FALSE, + 'provider' => 'system', + 'level' => $level, + 'depth' => $depth, + 'expand_all_items' => FALSE, + ], + 'region' => 'content', + ]); + + $this->assertValidationErrors($expected_errors); + } + } diff --git a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php index 9304a4db20b1..9c18ec030002 100644 --- a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php +++ b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockContentTranslationTest.php @@ -45,6 +45,7 @@ class MigrateBlockContentTranslationTest extends MigrateDrupal6TestBase { 'd6_filter_format', 'block_content_type', 'block_content_body_field', + 'd6_menu', 'd6_custom_block', 'd6_user_role', 'd6_block', diff --git a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php index eb3ad7721e9b..3f20b2148b8d 100644 --- a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php +++ b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php @@ -51,6 +51,7 @@ class MigrateBlockTest extends MigrateDrupal6TestBase { 'd6_filter_format', 'block_content_type', 'block_content_body_field', + 'd6_menu', 'd6_custom_block', 'd6_user_role', 'd6_block', @@ -189,10 +190,13 @@ class MigrateBlockTest extends MigrateDrupal6TestBase { // Check menu blocks. $settings = [ - 'id' => 'broken', + 'id' => 'system_menu_block', 'label' => '', - 'provider' => 'core', + 'provider' => 'system', 'label_display' => '0', + 'level' => 1, + 'depth' => NULL, + 'expand_all_items' => FALSE, ]; $this->assertEntity('menu', [], 'header', 'olivero', -5, $settings); diff --git a/core/modules/block/tests/src/Unit/Plugin/migrate/process/BlockSettingsTest.php b/core/modules/block/tests/src/Unit/Plugin/migrate/process/BlockSettingsTest.php index c8d209d1ca7e..126cb2691ff3 100644 --- a/core/modules/block/tests/src/Unit/Plugin/migrate/process/BlockSettingsTest.php +++ b/core/modules/block/tests/src/Unit/Plugin/migrate/process/BlockSettingsTest.php @@ -5,9 +5,12 @@ declare(strict_types=1); namespace Drupal\Tests\block\Unit\Plugin\migrate\process; use Drupal\block\Plugin\migrate\process\BlockSettings; +use Drupal\Core\Block\BlockManagerInterface; +use Drupal\Core\Block\BlockPluginInterface; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\Row; use Drupal\Tests\UnitTestCase; +use Prophecy\Argument; /** * @coversDefaultClass \Drupal\block\Plugin\migrate\process\BlockSettings @@ -32,7 +35,20 @@ class BlockSettingsTest extends UnitTestCase { ->reveal(); $row = $this->prophesize(Row::class)->reveal(); - $plugin = new BlockSettings([], 'block_settings', []); + // The block plugin should be asked to provide default configuration. + $expected['default'] = 'value'; + + $mock_plugin = $this->prophesize(BlockPluginInterface::class); + $mock_plugin->getConfiguration() + ->shouldBeCalled() + ->willReturn($expected); + + $block_manager = $this->prophesize(BlockManagerInterface::class); + $block_manager->createInstance($value[0], Argument::type('array')) + ->shouldBeCalled() + ->willReturn($mock_plugin->reveal()); + + $plugin = new BlockSettings([], 'block_settings', [], $block_manager->reveal()); $actual = $plugin->transform($value, $executable, $row, 'foo'); $this->assertSame($expected, $actual); } diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5CodeSyntaxTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5CodeSyntaxTest.php new file mode 100644 index 000000000000..a0d16cfb2c37 --- /dev/null +++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5CodeSyntaxTest.php @@ -0,0 +1,60 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\ckeditor5\FunctionalJavascript; + +use Behat\Mink\Element\NodeElement; +use Drupal\editor\Entity\Editor; +use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait; + +/** + * Tests code block configured languages are respected. + * + * @group ckeditor5 + * @internal + */ +class CKEditor5CodeSyntaxTest extends CKEditor5TestBase { + + use CKEditor5TestTrait; + + /** + * Tests code block configured languages are respected. + */ + public function testCKEditor5CodeSyntax(): void { + $this->addNewTextFormat(); + /** @var \Drupal\editor\Entity\Editor $editor */ + $editor = Editor::load('ckeditor5'); + $editor->setSettings([ + 'toolbar' => [ + 'items' => [ + 'codeBlock', + ], + ], + 'plugins' => [ + 'ckeditor5_codeBlock' => [ + 'languages' => [ + ['label' => 'Twig', 'language' => 'twig'], + ['label' => 'YML', 'language' => 'yml'], + ], + ], + ], + ])->save(); + $this->drupalGet('/node/add/page'); + + $this->waitForEditor(); + // Open code block dropdown, and verify that correct languages are present. + $assertSession = $this->assertSession(); + $page = $this->getSession()->getPage(); + $page->find('css', '.ck-code-block-dropdown .ck-dropdown__button .ck-splitbutton__arrow')->click(); + $codeBlockOptionsSelector = '.ck-code-block-dropdown .ck-dropdown__panel .ck-list__item .ck-button__label'; + $assertSession->waitForElementVisible('css', $codeBlockOptionsSelector); + $codeBlockOptions = $page->findAll('css', $codeBlockOptionsSelector); + $this->assertCount(2, $codeBlockOptions); + $this->assertEquals([ + 'Twig', + 'YML', + ], \array_map(static fn (NodeElement $el) => $el->getText(), $codeBlockOptions)); + } + +} diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php index ccf189d484a2..c95baf47e0c4 100644 --- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php +++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLibraryTest.php @@ -62,6 +62,10 @@ class MediaLibraryTest extends WebDriverTestBase { * {@inheritdoc} */ protected function setUp(): void { + if ($this->name() === 'testButton') { + $this->markTestSkipped('Skipped due to frequent random test failures. See https://www.drupal.org/i/3351597'); + } + parent::setUp(); FilterFormat::create([ @@ -156,9 +160,6 @@ class MediaLibraryTest extends WebDriverTestBase { * Tests using drupalMedia button to embed media into CKEditor 5. */ public function testButton(): void { - // Skipped due to frequent random test failures. - // @todo Fix this and stop skipping it at https://www.drupal.org/i/3351597. - $this->markTestSkipped(); $media_preview_selector = '.ck-content .ck-widget.drupal-media .media'; $this->drupalGet('/node/add/blog'); $this->waitForEditor(); diff --git a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js b/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js deleted file mode 100644 index 2bf3247862c7..000000000000 --- a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js +++ /dev/null @@ -1,157 +0,0 @@ -// cspell:ignore sourceediting - -module.exports = { - '@tags': ['core', 'ckeditor5'], - before(browser) { - browser.drupalInstall({ installProfile: 'testing' }); - }, - after(browser) { - browser.drupalUninstall(); - }, - 'Verify code block configured languages are respected': (browser) => { - browser.drupalLoginAsAdmin(() => { - browser - // Enable required modules. - .drupalRelativeURL('/admin/modules') - .click('[name="modules[ckeditor5][enable]"]') - .click('[name="modules[node][enable]"]') - .click('[name="modules[field_ui][enable]"]') - .submitForm('input[type="submit"]') // Submit module form. - .waitForElementVisible( - '.system-modules-confirm-form input[value="Continue"]', - ) - .submitForm('input[value="Continue"]') // Confirm installation of dependencies. - .waitForElementVisible('.system-modules', 10000) - - // Create new input format. - .drupalRelativeURL('/admin/config/content/formats/add') - .waitForElementVisible('[data-drupal-selector="edit-name"]') - .updateValue('[data-drupal-selector="edit-name"]', 'test') - .waitForElementVisible('#edit-name-machine-name-suffix') - .click( - '[data-drupal-selector="edit-editor-editor"] option[value=ckeditor5]', - ) - // Wait for CKEditor 5 settings to be visible. - .waitForElementVisible( - '[data-drupal-selector="edit-editor-settings-toolbar"]', - ) - .click('.ckeditor5-toolbar-button-sourceEditing') // Select the Source Editing button. - // Hit the down arrow key to move it to the toolbar. - .perform(function () { - return this.actions().sendKeys(browser.Keys.ARROW_DOWN); - }) - // Wait for new source editing vertical tab to be present before continuing. - .waitForElementVisible( - '[href*=edit-editor-settings-plugins-ckeditor5-sourceediting]', - ) - .click('.ckeditor5-toolbar-item-codeBlock') // Select the Code Block button. - // Hit the down arrow key to move it to the toolbar. - .perform(function () { - return this.actions().sendKeys(browser.Keys.ARROW_DOWN); - }) - // Wait for new code editing vertical tab to be present before continuing. - .waitForElementVisible( - '[href*=edit-editor-settings-plugins-ckeditor5-codeblock]', - ) - .click('[href*=edit-editor-settings-plugins-ckeditor5-codeblock]') - .setValue( - '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-codeblock-languages"]', - 'twig|Twig\nyml|YML', - ) - .submitForm('input[type="submit"]') - .waitForElementVisible('[data-drupal-messages]') - .assert.textContains('[data-drupal-messages]', 'Added text format') - - // Create a new content type. - .drupalRelativeURL('/admin/structure/types/add') - .waitForElementVisible('[data-drupal-selector="edit-name"]') - .updateValue('[data-drupal-selector="edit-name"]', 'test') - .waitForElementVisible('#edit-name-machine-name-suffix') // Wait for machine name to update. - .submitForm('input[type="submit"]') - .waitForElementVisible('[data-drupal-messages]') - .assert.textContains( - '[data-drupal-messages]', - 'The content type test has been added', - ) - - // Navigate to create new content. - .drupalRelativeURL('/node/add/test') - .waitForElementVisible('.ck-editor__editable') - - // Open code block dropdown, and verify that correct languages are present. - .click( - '.ck-code-block-dropdown .ck-dropdown__button .ck-splitbutton__arrow', - ) - .assert.textContains( - '.ck-code-block-dropdown .ck-dropdown__panel .ck-list__item:nth-child(1) .ck-button__label', - 'Twig', - ) - .assert.textContains( - '.ck-code-block-dropdown .ck-dropdown__panel .ck-list__item:nth-child(2) .ck-button__label', - 'YML', - ) - - // Click the first language (which should be 'Twig'). - .click( - '.ck-code-block-dropdown .ck-dropdown__panel .ck-list__item:nth-child(1) button', - ) - .waitForElementVisible('.ck-editor__main pre[data-language="Twig"]') - // Press 'X' to ensure there's data in CKEditor before switching to source view. - .perform(function () { - return this.actions().sendKeys('x'); - }) - .pause(50) - - // Go into source editing and verify that correct CSS class is added. - .click('.ck-source-editing-button') - .waitForElementVisible('.ck-source-editing-area') - .assert.valueContains( - '.ck-source-editing-area textarea', - '<pre><code class="language-twig">', - ) - - // Go back into WYSIWYG mode and hit enter three times to break out of code block. - .click('.ck-source-editing-button') // Disable source editing. - .waitForElementVisible('.ck-editor__editable:not(.ck-hidden)') - // Go to end of line. - .perform(function () { - return this.actions().sendKeys(browser.Keys.ARROW_RIGHT); - }) - .pause(50) - - // Hit Enter three times to break out of CKEditor's code block. - .perform(function () { - return this.actions().sendKeys(browser.Keys.ENTER); - }) - .pause(50) - .perform(function () { - return this.actions().sendKeys(browser.Keys.ENTER); - }) - .pause(50) - .perform(function () { - return this.actions().sendKeys(browser.Keys.ENTER); - }) - .pause(50) - - // Open up the code syntax dropdown, and click the 2nd item (which should be 'YML'). - .click( - '.ck-code-block-dropdown .ck-dropdown__button .ck-splitbutton__arrow', - ) - .click( - '.ck-code-block-dropdown .ck-dropdown__panel .ck-list__item:nth-child(2) button', - ) - // Press 'X' to ensure there's data in CKEditor before switching to source view. - .perform(function () { - return this.actions().sendKeys('x'); - }) - - // Go into source editing and verify that correct CSS class is added. - .click('.ck-source-editing-button') - .waitForElementVisible('.ck-source-editing-area') - .assert.valueContains( - '.ck-source-editing-area textarea', - '<pre><code class="language-yml">', - ); - }); - }, -}; diff --git a/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php b/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php index ab08838ccfe0..eb0a8b358ffe 100644 --- a/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php +++ b/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php @@ -49,7 +49,7 @@ JS; /** * Waits for CKEditor to initialize. */ - protected function waitForEditor() { + protected function waitForEditor(): void { $assert_session = $this->assertSession(); $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-editor')); } @@ -60,7 +60,7 @@ JS; * @param string $name * The name of the button, such as `drupallink`, `source`, etc. */ - protected function pressEditorButton($name) { + protected function pressEditorButton($name): void { $this->getEditorButton($name)->click(); } @@ -85,7 +85,7 @@ JS; * @param string $name * The name of the button, such as `drupallink`, `source`, etc. */ - protected function assertEditorButtonDisabled($name) { + protected function assertEditorButtonDisabled($name): void { $button = $this->getEditorButton($name); $this->assertTrue($button->hasAttribute('aria-disabled')); $this->assertTrue($button->hasClass('ck-disabled')); @@ -97,7 +97,7 @@ JS; * @param string $name * The name of the button, such as `drupallink`, `source`, etc. */ - protected function assertEditorButtonEnabled($name) { + protected function assertEditorButtonEnabled($name): void { $button = $this->getEditorButton($name); $this->assertFalse($button->hasAttribute('aria-disabled')); $this->assertFalse($button->hasClass('ck-disabled')); diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php index 63e67b7f351c..3034a3156948 100644 --- a/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php +++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationWorkflowsTest.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Drupal\Tests\content_translation\Functional; -use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Url; use Drupal\entity_test\Entity\EntityTestMulRevPub; @@ -249,10 +248,10 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase { foreach ($ops as $op => $label) { if ($op != $current_op) { - $this->assertSession()->linkNotExists($label, new FormattableMarkup('No %op link found.', ['%op' => $label])); + $this->assertSession()->linkNotExists($label); } else { - $this->assertSession()->linkExists($label, 0, new FormattableMarkup('%op link found.', ['%op' => $label])); + $this->assertSession()->linkExists($label, 0); } } } diff --git a/core/modules/file/tests/src/Functional/FileFieldTestBase.php b/core/modules/file/tests/src/Functional/FileFieldTestBase.php index bb321bb13757..702a35f0e4b2 100644 --- a/core/modules/file/tests/src/Functional/FileFieldTestBase.php +++ b/core/modules/file/tests/src/Functional/FileFieldTestBase.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Drupal\Tests\file\Functional; -use Drupal\Component\Render\FormattableMarkup; use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Entity\FieldConfig; use Drupal\file\FileInterface; @@ -222,7 +221,7 @@ abstract class FileFieldTestBase extends BrowserTestBase { public function assertFileEntryExists($file, $message = NULL) { $this->container->get('entity_type.manager')->getStorage('file')->resetCache(); $db_file = File::load($file->id()); - $message = $message ?? new FormattableMarkup('File %file exists in database at the correct path.', ['%file' => $file->getFileUri()]); + $message = $message ?? sprintf('File %s exists in database at the correct path.', $file->getFileUri()); $this->assertEquals($file->getFileUri(), $db_file->getFileUri(), $message); } @@ -231,7 +230,7 @@ abstract class FileFieldTestBase extends BrowserTestBase { */ public function assertFileEntryNotExists($file, $message) { $this->container->get('entity_type.manager')->getStorage('file')->resetCache(); - $message = $message ?? new FormattableMarkup('File %file exists in database at the correct path.', ['%file' => $file->getFileUri()]); + $message = $message ?? sprintf('File %s exists in database at the correct path.', $file->getFileUri()); $this->assertNull(File::load($file->id()), $message); } @@ -239,7 +238,7 @@ abstract class FileFieldTestBase extends BrowserTestBase { * Asserts that a file's status is set to permanent in the database. */ public function assertFileIsPermanent(FileInterface $file, $message = NULL) { - $message = $message ?? new FormattableMarkup('File %file is permanent.', ['%file' => $file->getFileUri()]); + $message = $message ?? sprintf('File %s is permanent.', $file->getFileUri()); $this->assertTrue($file->isPermanent(), $message); } diff --git a/core/modules/help/tests/src/Functional/HelpTest.php b/core/modules/help/tests/src/Functional/HelpTest.php index 3a954ac0eadd..920179bca8ad 100644 --- a/core/modules/help/tests/src/Functional/HelpTest.php +++ b/core/modules/help/tests/src/Functional/HelpTest.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Drupal\Tests\help\Functional; -use Drupal\Component\Render\FormattableMarkup; use Drupal\Tests\BrowserTestBase; /** @@ -99,7 +98,7 @@ class HelpTest extends BrowserTestBase { // Make sure links are properly added for modules implementing hook_help(). foreach ($this->getModuleList() as $module => $name) { - $this->assertSession()->linkExists($name, 0, new FormattableMarkup('Link properly added to @name (admin/help/@module)', ['@module' => $module, '@name' => $name])); + $this->assertSession()->linkExists($name, 0, "Link properly added to $name (admin/help/$module)"); } // Ensure a module which does not provide a module overview page is handled diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php index 46f15a177c2c..a9205d9f0dc3 100644 --- a/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php +++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php @@ -48,6 +48,10 @@ class LayoutBuilderUiTest extends WebDriverTestBase { * {@inheritdoc} */ protected function setUp(): void { + if ($this->name() === 'testAddHighlights') { + $this->markTestSkipped("Skipped temporarily for random fails."); + } + parent::setUp(); $this->createContentType(['type' => 'bundle_with_section_field']); @@ -299,7 +303,6 @@ class LayoutBuilderUiTest extends WebDriverTestBase { * Waits for the dialog to close and confirms no highlights are present. */ private function assertHighlightNotExists(): void { - $this->markTestSkipped("Skipped temporarily for random fails."); $assert_session = $this->assertSession(); $assert_session->assertNoElementAfterWait('css', '#drupal-off-canvas'); diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php index ba11617ffae7..df748df0e080 100644 --- a/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php +++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php @@ -39,6 +39,8 @@ class MoveBlockFormTest extends WebDriverTestBase { * {@inheritdoc} */ protected function setUp(): void { + $this->markTestSkipped("Skipped temporarily for random fails."); + parent::setUp(); $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); @@ -66,7 +68,6 @@ class MoveBlockFormTest extends WebDriverTestBase { '.block-extra-field-blocknodebundle-with-section-fieldlinks', '.block-field-blocknodebundle-with-section-fieldbody', ]; - $this->markTestSkipped("Skipped temporarily for random fails."); $this->assertRegionBlocksOrder(0, 'content', $expected_block_order); // Add a top section using the Two column layout. diff --git a/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php b/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php index 41a4a319d27c..392cd7c19c53 100644 --- a/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php +++ b/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php @@ -311,7 +311,11 @@ class MenuUiTest extends BrowserTestBase { $this->assertSession()->pageTextContains($label); // Enable the block. - $block = $this->drupalPlaceBlock('system_menu_block:' . $menu_name); + $block = $this->drupalPlaceBlock('system_menu_block:' . $menu_name, [ + 'level' => 1, + 'depth' => NULL, + 'expand_all_items' => FALSE, + ]); $this->blockPlacements[$menu_name] = $block->id(); return Menu::load($menu_name); } @@ -1184,7 +1188,7 @@ class MenuUiTest extends BrowserTestBase { $this->assertEquals(3, $settings['depth']); $this->assertEquals(2, $settings['level']); // Reset settings. - $block->getPlugin()->setConfigurationValue('depth', 0); + $block->getPlugin()->setConfigurationValue('depth', NULL); $block->getPlugin()->setConfigurationValue('level', 1); $block->save(); } diff --git a/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php b/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php index 85eb9eadcf1d..26c24424f9d1 100644 --- a/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php +++ b/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php @@ -34,6 +34,10 @@ class SettingsTrayBlockFormTest extends SettingsTrayTestBase { * {@inheritdoc} */ protected function setUp(): void { + if ($this->name() === 'testEditModeEnableDisable') { + $this->markTestSkipped("Skipped due to frequent random test failures. See https://www.drupal.org/project/drupal/issues/3317520"); + } + parent::setUp(); $user = $this->createUser([ @@ -231,7 +235,6 @@ class SettingsTrayBlockFormTest extends SettingsTrayTestBase { * Tests enabling and disabling Edit Mode. */ public function testEditModeEnableDisable(): void { - $this->markTestSkipped("Skipped due to frequent random test failures. See https://www.drupal.org/project/drupal/issues/3317520"); foreach (static::getTestThemes() as $theme) { $this->enableTheme($theme); $block = $this->placeBlock('system_powered_by_block'); diff --git a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php index 10c1d269da7f..5ae0466a6505 100644 --- a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php +++ b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Drupal\Tests\shortcut\Functional; use Drupal\block_content\Entity\BlockContentType; -use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Url; use Drupal\shortcut\Entity\Shortcut; use Drupal\shortcut\Entity\ShortcutSet; @@ -109,10 +108,10 @@ class ShortcutLinksTest extends ShortcutTestBase { $this->assertContains('internal:' . $test_path, $paths, 'Shortcut created: ' . $test_path); if (in_array($test_path, $test_cases_non_access)) { - $this->assertSession()->linkNotExists($title, new FormattableMarkup('Shortcut link %url not accessible on the page.', ['%url' => $test_path])); + $this->assertSession()->linkNotExists($title, "Shortcut link $test_path not accessible on the page."); } else { - $this->assertSession()->linkExists($title, 0, new FormattableMarkup('Shortcut link %url found on the page.', ['%url' => $test_path])); + $this->assertSession()->linkExists($title, 0, "Shortcut link $test_path found on the page."); } } $saved_set = ShortcutSet::load($set->id()); @@ -486,7 +485,7 @@ class ShortcutLinksTest extends ShortcutTestBase { */ protected function assertShortcutQuickLink(string $label, int $index = 0, string $message = ''): void { $links = $this->xpath('//a[normalize-space()=:label]', [':label' => $label]); - $message = ($message ?: (string) new FormattableMarkup('Shortcut quick link with label %label found.', ['%label' => $label])); + $message = ($message ?: "Shortcut quick link with label $label found."); $this->assertArrayHasKey($index, $links, $message); } diff --git a/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php b/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php index 0a353dceed05..8158de67c504 100644 --- a/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php +++ b/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php @@ -442,7 +442,10 @@ class Connection extends DatabaseConnection implements SupportsTemporaryTablesIn * {@inheritdoc} */ public static function createConnectionOptionsFromUrl($url, $root) { - $database = parent::createConnectionOptionsFromUrl($url, $root); + if ($root !== NULL) { + @trigger_error("Passing the \$root value to " . __METHOD__ . "() is deprecated in drupal:11.2.0 and will be removed in drupal:12.0.0. There is no replacement. See https://www.drupal.org/node/3511287", E_USER_DEPRECATED); + } + $database = parent::createConnectionOptionsFromUrl($url, NULL); // A SQLite database path with two leading slashes indicates a system path. // Otherwise the path is relative to the Drupal root. diff --git a/core/modules/sqlite/tests/src/Unit/ConnectionTest.php b/core/modules/sqlite/tests/src/Unit/ConnectionTest.php index 4e2417ef2a03..8340e627d484 100644 --- a/core/modules/sqlite/tests/src/Unit/ConnectionTest.php +++ b/core/modules/sqlite/tests/src/Unit/ConnectionTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\sqlite\Unit; use Drupal\sqlite\Driver\Database\sqlite\Connection; use Drupal\Tests\Core\Database\Stub\StubPDO; use Drupal\Tests\UnitTestCase; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; /** * @coversDefaultClass \Drupal\sqlite\Driver\Database\sqlite\Connection @@ -24,9 +25,8 @@ class ConnectionTest extends UnitTestCase { * Expected connection option. */ public function testCreateConnectionOptionsFromUrl(string $url, string $expected): void { - $root = dirname(__DIR__, 8); $sqlite_connection = new Connection($this->createMock(StubPDO::class), []); - $database = $sqlite_connection->createConnectionOptionsFromUrl($url, $root); + $database = $sqlite_connection->createConnectionOptionsFromUrl($url, NULL); $this->assertEquals('sqlite', $database['driver']); $this->assertEquals($expected, $database['database']); } @@ -47,4 +47,17 @@ class ConnectionTest extends UnitTestCase { ]; } + /** + * Confirms deprecation of the $root argument. + */ + #[IgnoreDeprecations] + public function testDeprecationOfRootInConnectionOptionsFromUrl(): void { + $this->expectDeprecation('Passing the $root value to Drupal\sqlite\Driver\Database\sqlite\Connection::createConnectionOptionsFromUrl() is deprecated in drupal:11.2.0 and will be removed in drupal:12.0.0. There is no replacement. See https://www.drupal.org/node/3511287'); + $root = dirname(__DIR__, 8); + $sqlite_connection = new Connection($this->createMock(StubPDO::class), []); + $database = $sqlite_connection->createConnectionOptionsFromUrl('sqlite://localhost/tmp/test', $root); + $this->assertEquals('sqlite', $database['driver']); + $this->assertEquals('tmp/test', $database['database']); + } + } diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 8d19b0dd2aa0..88f34652b986 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -448,13 +448,23 @@ block.settings.system_branding_block: block.settings.system_menu_block:*: type: block_settings label: 'Menu block' + constraints: + FullyValidatable: ~ mapping: level: type: integer label: 'Starting level' + constraints: + MenuLinkDepth: + min: 0 depth: type: integer label: 'Maximum number of levels' + nullable: true + constraints: + MenuLinkDepth: + baseLevel: '[%parent.level]' + min: 1 expand_all_items: type: boolean label: 'Expand all items' diff --git a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php index c85af0552342..f53bda307c3f 100644 --- a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php +++ b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php @@ -108,7 +108,7 @@ class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterfa $form['menu_levels']['depth'] = [ '#type' => 'select', '#title' => $this->t('Number of levels to display'), - '#default_value' => $config['depth'], + '#default_value' => $config['depth'] ?? 0, '#options' => $options, '#description' => $this->t('This maximum number includes the initial level.'), '#required' => TRUE, @@ -139,7 +139,7 @@ class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterfa */ public function blockSubmit($form, FormStateInterface $form_state) { $this->configuration['level'] = $form_state->getValue('level'); - $this->configuration['depth'] = $form_state->getValue('depth'); + $this->configuration['depth'] = $form_state->getValue('depth') ?: NULL; $this->configuration['expand_all_items'] = $form_state->getValue('expand_all_items'); } @@ -203,7 +203,7 @@ class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterfa public function defaultConfiguration() { return [ 'level' => 1, - 'depth' => 0, + 'depth' => NULL, 'expand_all_items' => FALSE, ]; } diff --git a/core/modules/system/tests/src/Functional/Module/DependencyTest.php b/core/modules/system/tests/src/Functional/Module/DependencyTest.php index ef30c4f713ff..7f2d218388a8 100644 --- a/core/modules/system/tests/src/Functional/Module/DependencyTest.php +++ b/core/modules/system/tests/src/Functional/Module/DependencyTest.php @@ -20,6 +20,16 @@ class DependencyTest extends ModuleTestBase { protected $defaultTheme = 'stark'; /** + * {@inheritdoc} + */ + protected function setUp(): void { + if ($this->name() === 'testCoreCompatibility') { + $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); + } + parent::setUp(); + } + + /** * Checks functionality of project namespaces for dependencies. */ public function testProjectNamespaceForDependencies(): void { @@ -173,7 +183,6 @@ class DependencyTest extends ModuleTestBase { * Tests enabling modules with different core version specifications. */ public function testCoreCompatibility(): void { - $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); $assert_session = $this->assertSession(); // Test incompatible 'core_version_requirement'. diff --git a/core/modules/system/tests/src/Functional/System/ThemeTest.php b/core/modules/system/tests/src/Functional/System/ThemeTest.php index a5fbadec916a..2b91b132371c 100644 --- a/core/modules/system/tests/src/Functional/System/ThemeTest.php +++ b/core/modules/system/tests/src/Functional/System/ThemeTest.php @@ -49,6 +49,10 @@ class ThemeTest extends BrowserTestBase { * {@inheritdoc} */ protected function setUp(): void { + if ($this->name() === 'testInstallAndSetAsDefault') { + $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); + } + parent::setUp(); $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']); @@ -527,7 +531,6 @@ class ThemeTest extends BrowserTestBase { * Tests installing a theme and setting it as default. */ public function testInstallAndSetAsDefault(): void { - $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); $themes = [ 'olivero' => 'Olivero', 'test_core_semver' => 'Theme test with semver core version', diff --git a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php index 210e41e840d5..f0f78b23c99c 100644 --- a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php +++ b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php @@ -64,6 +64,9 @@ class UpdateScriptTest extends BrowserTestBase { * {@inheritdoc} */ protected function setUp(): void { + if ($this->name() === 'testMissingExtension') { + $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); + } parent::setUp(); $this->updateUrl = Url::fromRoute('system.db_update'); $this->statusReportUrl = Url::fromRoute('system.status'); @@ -350,7 +353,6 @@ class UpdateScriptTest extends BrowserTestBase { * @dataProvider providerMissingExtension */ public function testMissingExtension(array $core, array $contrib): void { - $this->markTestSkipped('Skipped due to major version-specific logic. See https://www.drupal.org/project/drupal/issues/3359322'); $this->drupalLogin( $this->drupalCreateUser( [ diff --git a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php index 3c8ade61831e..e2ceead3475d 100644 --- a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php +++ b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php @@ -243,13 +243,13 @@ class SystemMenuBlockTest extends KernelTestBase { // All the different block instances we're going to test. $blocks = [ - 'all' => $place_block(1, 0), + 'all' => $place_block(1, NULL), 'level_1_only' => $place_block(1, 1), 'level_2_only' => $place_block(2, 1), 'level_3_only' => $place_block(3, 1), - 'level_1_and_beyond' => $place_block(1, 0), - 'level_2_and_beyond' => $place_block(2, 0), - 'level_3_and_beyond' => $place_block(3, 0), + 'level_1_and_beyond' => $place_block(1, NULL), + 'level_2_and_beyond' => $place_block(2, NULL), + 'level_3_and_beyond' => $place_block(3, NULL), ]; // Scenario 1: test all block instances when there's no active trail. @@ -346,7 +346,7 @@ class SystemMenuBlockTest extends KernelTestBase { 'id' => 'machine_name', 'theme' => 'stark', 'level' => $menu_block_level, - 'depth' => 0, + 'depth' => NULL, 'expand_all_items' => TRUE, ]); diff --git a/core/modules/views_ui/tests/src/Functional/FilterEntityReferenceWebTest.php b/core/modules/views_ui/tests/src/Functional/FilterEntityReferenceWebTest.php index 2cfc383f5c77..863ea9dfdf6a 100644 --- a/core/modules/views_ui/tests/src/Functional/FilterEntityReferenceWebTest.php +++ b/core/modules/views_ui/tests/src/Functional/FilterEntityReferenceWebTest.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Drupal\Tests\views_ui\Functional; -use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Entity\EntityInterface; use Drupal\Tests\views_ui\Traits\FilterEntityReferenceTrait; @@ -60,8 +59,7 @@ class FilterEntityReferenceWebTest extends UITestBase { }); $i = 0; foreach ($this->targetEntities as $entity) { - $message = (string) new FormattableMarkup('Expected target entity label found for option :option', [':option' => $i]); - $this->assertEquals($options[$i]['label'], $entity->label(), $message); + $this->assertEquals($options[$i]['label'], $entity->label(), "Expected target entity label found for option $i"); $i++; } @@ -79,8 +77,7 @@ class FilterEntityReferenceWebTest extends UITestBase { $options = $this->getUiOptions(); $i = 0; foreach ($this->targetEntities as $entity) { - $message = (string) new FormattableMarkup('Expected target entity label found for option :option', [':option' => $i]); - $this->assertEquals($options[$i]['label'], $entity->label(), $message); + $this->assertEquals($options[$i]['label'], $entity->label(), "Expected target entity label found for option $i"); $i++; } @@ -96,8 +93,7 @@ class FilterEntityReferenceWebTest extends UITestBase { $options = $this->getUiOptions(); $i = 0; foreach ($this->hostEntities + $this->targetEntities as $entity) { - $message = (string) new FormattableMarkup('Expected target entity label found for option :option', [':option' => $i]); - $this->assertEquals($options[$i]['label'], $entity->label(), $message); + $this->assertEquals($options[$i]['label'], $entity->label(), "Expected target entity label found for option $i"); $i++; } } diff --git a/core/profiles/demo_umami/config/install/block.block.umami_footer.yml b/core/profiles/demo_umami/config/install/block.block.umami_footer.yml index 91aacef37d43..5ff6dde500b7 100644 --- a/core/profiles/demo_umami/config/install/block.block.umami_footer.yml +++ b/core/profiles/demo_umami/config/install/block.block.umami_footer.yml @@ -19,6 +19,6 @@ settings: label_display: '1' provider: system level: 1 - depth: 0 + depth: null expand_all_items: false visibility: { } diff --git a/core/profiles/minimal/config/install/block.block.stark_admin.yml b/core/profiles/minimal/config/install/block.block.stark_admin.yml index b5a1006900d2..3f60f4c77464 100644 --- a/core/profiles/minimal/config/install/block.block.stark_admin.yml +++ b/core/profiles/minimal/config/install/block.block.stark_admin.yml @@ -19,6 +19,6 @@ settings: label_display: visible provider: system level: 1 - depth: 0 + depth: null expand_all_items: false visibility: { } diff --git a/core/profiles/minimal/config/install/block.block.stark_tools.yml b/core/profiles/minimal/config/install/block.block.stark_tools.yml index 854e3bda4e8b..36f6efdfa9ad 100644 --- a/core/profiles/minimal/config/install/block.block.stark_tools.yml +++ b/core/profiles/minimal/config/install/block.block.stark_tools.yml @@ -19,6 +19,6 @@ settings: label_display: visible provider: system level: 1 - depth: 0 + depth: null expand_all_items: false visibility: { } diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php index 3d056401a2b8..03bd477123c1 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php @@ -24,6 +24,16 @@ class AjaxTest extends WebDriverTestBase { */ protected $defaultTheme = 'stark'; + /** + * {@inheritdoc} + */ + protected function setUp(): void { + if ($this->name() === 'testAjaxFocus') { + $this->markTestSkipped("Skipped due to frequent random test failures. See https://www.drupal.org/project/drupal/issues/3396536"); + } + parent::setUp(); + } + public function testAjaxWithAdminRoute(): void { \Drupal::service('theme_installer')->install(['stable9', 'claro']); $theme_config = \Drupal::configFactory()->getEditable('system.theme'); @@ -301,7 +311,6 @@ JS; * Tests ajax focus handling. */ public function testAjaxFocus(): void { - $this->markTestSkipped("Skipped due to frequent random test failures. See https://www.drupal.org/project/drupal/issues/3396536"); $this->drupalGet('/ajax_forms_test_get_form'); $this->assertNotNull($select = $this->assertSession()->elementExists('css', '#edit-select')); diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffTest.php index 92ce91803294..53b2c5dcb7a5 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Field/TimestampFormatterWithTimeDiffTest.php @@ -39,6 +39,10 @@ class TimestampFormatterWithTimeDiffTest extends WebDriverTestBase { * {@inheritdoc} */ protected function setUp(): void { + if ($this->name() === 'testNoRefreshInterval') { + $this->markTestSkipped("Skipped due to frequent random test failures. See https://www.drupal.org/project/drupal/issues/3400150"); + } + parent::setUp(); FieldStorageConfig::create([ @@ -138,7 +142,6 @@ class TimestampFormatterWithTimeDiffTest extends WebDriverTestBase { * Tests the 'timestamp' formatter without refresh interval. */ public function testNoRefreshInterval(): void { - $this->markTestSkipped("Skipped due to frequent random test failures. See https://www.drupal.org/project/drupal/issues/3400150"); // Set the refresh interval to zero, meaning "no refresh". $display = EntityViewDisplay::load('entity_test.entity_test.default'); $component = $display->getComponent('time_field'); diff --git a/core/tests/Drupal/KernelTests/Core/File/FileDeleteGadgetChainTest.php b/core/tests/Drupal/KernelTests/Core/File/FileDeleteGadgetChainTest.php new file mode 100644 index 000000000000..2be0bc9406a5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/File/FileDeleteGadgetChainTest.php @@ -0,0 +1,39 @@ +<?php + +declare(strict_types=1); + +// cSpell:ignore phpggc + +namespace Drupal\KernelTests\Core\File; + +use Drupal\KernelTests\KernelTestBase; + +/** + * Tests protection against SA-CORE-2024-006 File Delete Gadget Chain. + * + * @group file + */ +class FileDeleteGadgetChainTest extends KernelTestBase { + + /** + * Tests unserializing a File Delete payload. + */ + public function testFileDeleteGadgetChain(): void { + file_put_contents('public://canary.txt', 'now you see me'); + // ./phpggc --public-properties Drupal/FD1 public://canary.txt + $payload = 'O:34:"Drupal\Core\Config\StorageComparer":1:{s:18:"targetCacheStorage";O:39:"Drupal\Component\PhpStorage\FileStorage":1:{s:9:"directory";s:19:"public://canary.txt";}}'; + + try { + unserialize($payload); + $this->fail('No exception was thrown'); + } + catch (\Throwable $e) { + $this->assertInstanceOf(\TypeError::class, $e); + $this->assertStringContainsString('Cannot assign Drupal\Component\PhpStorage\FileStorage to property Drupal\Core\Config\StorageComparer::$targetCacheStorage', $e->getMessage()); + } + + $this->assertTrue(file_exists('public://canary.txt')); + unlink('public://canary.txt'); + } + +} diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php index 381872916b4f..accd108bfec6 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php @@ -54,10 +54,6 @@ class ScaffoldUpgradeTest extends TestCase { * Tests upgrading the Composer Scaffold plugin. */ public function testScaffoldUpgrade(): void { - $composerVersionLine = exec('composer --version'); - if (str_contains($composerVersionLine, 'Composer version 2')) { - $this->markTestSkipped('We cannot run the scaffold upgrade test with Composer 2 until we have a stable version of drupal/core-composer-scaffold to start from that we can install with Composer 2.x.'); - } $this->fixturesDir = $this->fixtures->tmpDir($this->name()); $replacements = ['SYMLINK' => 'false', 'PROJECT_ROOT' => $this->fixtures->projectRoot()]; $this->fixtures->cloneFixtureProjects($this->fixturesDir, $replacements); @@ -73,8 +69,9 @@ class ScaffoldUpgradeTest extends TestCase { // Packagist is disabled in the fixture; we bring it back by removing the // line that disables it. $this->mustExec("composer config --unset repositories.packagist.org", $sut); - $stdout = $this->mustExec("composer require --no-ansi drupal/core-composer-scaffold:8.8.0 --no-plugins 2>&1", $sut); - $this->assertStringContainsString(" - Installing drupal/core-composer-scaffold (8.8.0):", $stdout); + $this->mustExec("composer config --unset repositories.composer-scaffold", $sut); + $stdout = $this->mustExec("composer require --no-ansi drupal/core-composer-scaffold:9.5.0 --no-plugins 2>&1", $sut); + $this->assertStringContainsString(" - Installing drupal/core-composer-scaffold (9.5.0):", $stdout); // We can't force the path repo to re-install over the stable version // without removing it, and removing it masks the bugs we are testing for. diff --git a/core/tests/PHPStan/composer.json b/core/tests/PHPStan/composer.json index ce3805887867..ab10409fcbff 100644 --- a/core/tests/PHPStan/composer.json +++ b/core/tests/PHPStan/composer.json @@ -3,7 +3,7 @@ "description": "Tests Drupal core's PHPStan rules", "require-dev": { "phpunit/phpunit": "^10", - "phpstan/phpstan": "2.1.11" + "phpstan/phpstan": "2.1.12" }, "license": "GPL-2.0-or-later", "autoload": { diff --git a/core/tests/fixtures/config_install/multilingual/block.block.stark_admin.yml b/core/tests/fixtures/config_install/multilingual/block.block.stark_admin.yml index 639a9dbcbbc5..735a64c4caeb 100644 --- a/core/tests/fixtures/config_install/multilingual/block.block.stark_admin.yml +++ b/core/tests/fixtures/config_install/multilingual/block.block.stark_admin.yml @@ -22,6 +22,6 @@ settings: label_display: visible provider: system level: 1 - depth: 0 + depth: null expand_all_items: false visibility: { } diff --git a/core/tests/fixtures/config_install/multilingual/block.block.stark_tools.yml b/core/tests/fixtures/config_install/multilingual/block.block.stark_tools.yml index 92fae3cf426e..06bdfa7e7901 100644 --- a/core/tests/fixtures/config_install/multilingual/block.block.stark_tools.yml +++ b/core/tests/fixtures/config_install/multilingual/block.block.stark_tools.yml @@ -22,6 +22,6 @@ settings: label_display: visible provider: system level: 1 - depth: 0 + depth: null expand_all_items: false visibility: { } diff --git a/core/tests/fixtures/config_install/testing_config_install/block.block.stark_admin.yml b/core/tests/fixtures/config_install/testing_config_install/block.block.stark_admin.yml index 999f8e5fd772..6c43960d459e 100644 --- a/core/tests/fixtures/config_install/testing_config_install/block.block.stark_admin.yml +++ b/core/tests/fixtures/config_install/testing_config_install/block.block.stark_admin.yml @@ -22,6 +22,6 @@ settings: label_display: visible provider: system level: 1 - depth: 0 + depth: null expand_all_items: false visibility: { } diff --git a/core/tests/fixtures/config_install/testing_config_install/block.block.stark_tools.yml b/core/tests/fixtures/config_install/testing_config_install/block.block.stark_tools.yml index a4c08abf357e..2d9f68c5d6ec 100644 --- a/core/tests/fixtures/config_install/testing_config_install/block.block.stark_tools.yml +++ b/core/tests/fixtures/config_install/testing_config_install/block.block.stark_tools.yml @@ -22,6 +22,6 @@ settings: label_display: visible provider: system level: 1 - depth: 0 + depth: null expand_all_items: false visibility: { } |