diff --git a/core/lib/Drupal/Core/Recipe/Recipe.php b/core/lib/Drupal/Core/Recipe/Recipe.php index 1baa8b9dec200628c150b5a0e326a5b6d2e5cef9..4da07d2743e575bb53e07c73475a269c6b8d5f9b 100644 --- a/core/lib/Drupal/Core/Recipe/Recipe.php +++ b/core/lib/Drupal/Core/Recipe/Recipe.php @@ -40,7 +40,8 @@ public function __construct( public readonly RecipeConfigurator $recipes, public readonly InstallConfigurator $install, public readonly ConfigConfigurator $config, - public readonly Finder $content + public readonly Finder $content, + public readonly string $path, ) { } @@ -61,7 +62,7 @@ public static function createFromDirectory(string $path): static { $install = new InstallConfigurator($recipe_data['install'], \Drupal::service('extension.list.module'), \Drupal::service('extension.list.theme')); $config = new ConfigConfigurator($recipe_data['config'], $path, \Drupal::service('config.storage')); $content = new Finder($path . '/content'); - return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $recipes, $install, $config, $content); + return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $recipes, $install, $config, $content, $path); } /** @@ -255,7 +256,7 @@ private static function validateRecipeExists(string $name, ExecutionContextInter * @return \Drupal\Core\Recipe\RecipeDiscovery */ private static function getRecipeDiscovery(string $recipeDirectory): RecipeDiscovery { - return new RecipeDiscovery([$recipeDirectory]); + return new RecipeDiscovery($recipeDirectory); } /** diff --git a/core/lib/Drupal/Core/Recipe/RecipeDiscovery.php b/core/lib/Drupal/Core/Recipe/RecipeDiscovery.php index cb6de7a781043fe6466d559e916d45fefcfedd2c..a871dea14de53a850097c650690500cea2fd5f5d 100644 --- a/core/lib/Drupal/Core/Recipe/RecipeDiscovery.php +++ b/core/lib/Drupal/Core/Recipe/RecipeDiscovery.php @@ -4,8 +4,6 @@ namespace Drupal\Core\Recipe; -use Drupal\Component\Assertion\Inspector; - /** * @internal * This API is experimental. @@ -15,17 +13,15 @@ final class RecipeDiscovery { /** * Constructs a recipe discovery object. * - * @param array $paths - * An array of paths where to search for recipes. The path will be searched - * folders containing a recipe.yml. There will be no traversal further into - * the directory structure. + * @param string $path + * The path will be searched folders containing a recipe.yml. There will be + * no traversal further into the directory structure. */ - public function __construct(protected readonly array $paths) { - assert(Inspector::assertAllStrings($paths), 'Paths must be strings.'); + public function __construct(protected string $path) { } /** - * Constructs a RecipeDiscovery object. + * Gets a recipe object. * * @param string $name * The machine name of the recipe to find. @@ -37,17 +33,26 @@ public function __construct(protected readonly array $paths) { * Thrown when the recipe cannot be found. */ public function getRecipe(string $name): Recipe { - $paths = [ - ...$this->paths, - DRUPAL_ROOT . '/recipes', - DRUPAL_ROOT . '/core/recipes', - ]; - foreach ($paths as $path) { - if (file_exists($path . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'recipe.yml')) { - return Recipe::createFromDirectory($path . DIRECTORY_SEPARATOR . $name); - } + // In order to allow recipes to include core provided recipes, $name can be + // a Drupal root relative path to a recipe folder. For example, a recipe can + // include the core provided 'article_tags' recipe by listing the recipe as + // 'core/recipes/article_tags'. It is strongly recommended not to rely on + // relative paths for including recipes. Required recipes should be put in + // the same parent directory as the recipe being applied. Note, only linux + // style directory separators are supported. PHP on windows can resolve the + // mix of directory separators. + if (str_contains($name, '/')) { + $path = \Drupal::root() . "/$name/recipe.yml"; + } + else { + $path = $this->path . "/$name/recipe.yml"; + } + + if (file_exists($path)) { + return Recipe::createFromDirectory(dirname($path)); } - throw new UnknownRecipeException($name, $paths, sprintf("Can not find the %s recipe, search paths: %s", $name, implode(', ', $paths))); + $search_path = dirname($path, 2); + throw new UnknownRecipeException($name, $search_path, sprintf("Can not find the %s recipe, search path: %s", $name, $search_path)); } } diff --git a/core/lib/Drupal/Core/Recipe/RecipeRunner.php b/core/lib/Drupal/Core/Recipe/RecipeRunner.php index 0e08d2df16bfa39aeb5367a24ff48a8dc72f4a44..35f2cec470674ee1eb30b9af40ffa2fc165b7a78 100644 --- a/core/lib/Drupal/Core/Recipe/RecipeRunner.php +++ b/core/lib/Drupal/Core/Recipe/RecipeRunner.php @@ -151,7 +151,7 @@ public static function toBatchOperations(Recipe $recipe): array { * @param \Drupal\Core\Recipe\Recipe $recipe * The recipe to convert to batch operations. * @param string[] $recipes - * The recipes that have already been converted to batch operations. + * The paths of the recipes that have already been converted to batch operations. * @param string[] $modules * The modules that will already be installed due to previous recipes in the * batch. @@ -165,11 +165,11 @@ public static function toBatchOperations(Recipe $recipe): array { * pass to the callable. */ protected static function toBatchOperationsRecipe(Recipe $recipe, array $recipes, array &$modules, array &$themes): array { - if (in_array($recipe->name, $recipes, TRUE)) { + if (in_array($recipe->path, $recipes, TRUE)) { return []; } $steps = []; - $recipes[] = $recipe->name; + $recipes[] = $recipe->path; foreach ($recipe->recipes->recipes as $sub_recipe) { $steps = array_merge($steps, static::toBatchOperationsRecipe($sub_recipe, $recipes, $modules, $themes)); diff --git a/core/lib/Drupal/Core/Recipe/UnknownRecipeException.php b/core/lib/Drupal/Core/Recipe/UnknownRecipeException.php index 8bfd61ea03d45434e090507def426d64578cf938..b0f63c002824d9c2eea400e0cdeca7643c1b5789 100644 --- a/core/lib/Drupal/Core/Recipe/UnknownRecipeException.php +++ b/core/lib/Drupal/Core/Recipe/UnknownRecipeException.php @@ -15,8 +15,8 @@ final class UnknownRecipeException extends \RuntimeException { /** * @param string $recipe * The recipe's name. - * @param array $searchPaths - * The paths searched for the recipe. + * @param string $searchPath + * The path searched for the recipe. * @param string $message * (optional) The exception message. * @param int $code @@ -24,7 +24,7 @@ final class UnknownRecipeException extends \RuntimeException { * @param \Throwable|null $previous * (optional) The previous exception. */ - public function __construct(public readonly string $recipe, public readonly array $searchPaths, string $message = "", int $code = 0, ?\Throwable $previous = NULL) { + public function __construct(public readonly string $recipe, public readonly string $searchPath, string $message = "", int $code = 0, ?\Throwable $previous = NULL) { parent::__construct($message, $code, $previous); } diff --git a/core/modules/content_moderation/tests/src/Kernel/ConfigAction/AddModerationConfigActionTest.php b/core/modules/content_moderation/tests/src/Kernel/ConfigAction/AddModerationConfigActionTest.php index 374ea3e1f0d81e37850667f620c9586d9a7738ad..4d5afd395e2ab2bf133a13125d69c362f5987fb1 100644 --- a/core/modules/content_moderation/tests/src/Kernel/ConfigAction/AddModerationConfigActionTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/ConfigAction/AddModerationConfigActionTest.php @@ -95,7 +95,7 @@ private function createRecipe(string $config_name): Recipe { $recipe = <<<YAML name: 'Add entity types and bundles to workflow' recipes: - - editorial_workflow + - core/recipes/editorial_workflow config: actions: $config_name: diff --git a/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeCommandTest.php b/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeCommandTest.php index 229d4acfdacf5925c109a33bca4a7b0533f9ba07..39149729e78e2e08b6e2e7feb8031fc2a20c3087 100644 --- a/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeCommandTest.php +++ b/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeCommandTest.php @@ -37,7 +37,7 @@ public function testRecipeCommand(): void { $process = $this->applyRecipe('core/tests/fixtures/recipes/install_node_with_config'); $this->assertSame(0, $process->getExitCode()); - $this->assertStringContainsString("6/6 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]\nApplied Install node with config recipe.", $process->getErrorOutput()); + $this->assertStringContainsString("Applied Install node with config recipe.", $process->getErrorOutput()); $this->assertStringContainsString('Install node with config applied successfully', $process->getOutput()); $this->assertTrue(\Drupal::moduleHandler()->moduleExists('node'), 'The node module is installed'); $this->assertCheckpointsExist(["Backup before the 'Install node with config' recipe."]); @@ -45,7 +45,7 @@ public function testRecipeCommand(): void { // Ensure recipes can be applied without affecting pre-existing checkpoints. $process = $this->applyRecipe('core/tests/fixtures/recipes/install_two_modules'); $this->assertSame(0, $process->getExitCode()); - $this->assertStringContainsString("1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]\nApplied Install two modules recipe.", $process->getErrorOutput()); + $this->assertStringContainsString("Applied Install two modules recipe.", $process->getErrorOutput()); $this->assertStringContainsString('Install two modules applied successfully', $process->getOutput()); $this->assertTrue(\Drupal::moduleHandler()->moduleExists('node'), 'The node module is installed'); $this->assertCheckpointsExist([ @@ -68,7 +68,7 @@ public function testRecipeCommand(): void { \Drupal::service('config.storage.checkpoint')->checkpoint('Test log message'); $process = $this->applyRecipe('core/tests/fixtures/recipes/no_extensions'); $this->assertSame(0, $process->getExitCode()); - $this->assertStringContainsString("1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]\nApplied No extensions recipe.", $process->getErrorOutput()); + $this->assertStringContainsString("Applied No extensions recipe.", $process->getErrorOutput()); $this->assertCheckpointsExist([ "Backup before the 'Install node with config' recipe.", "Backup before the 'Install two modules' recipe.", diff --git a/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeTestTrait.php b/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeTestTrait.php index 3ee24b85dcaa0a6d19a9822e26596922c3d51a06..601dced29634dc77c3e9e5d1fbf87d913655759d 100644 --- a/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeTestTrait.php +++ b/core/tests/Drupal/FunctionalTests/Core/Recipe/RecipeTestTrait.php @@ -21,16 +21,24 @@ trait RecipeTestTrait { * @param string|array<mixed> $data * The contents of recipe.yml. If passed as an array, will be encoded to * YAML. + * @param string|null $machine_name + * The machine name for the recipe. Will be used as the directory name. * * @return \Drupal\Core\Recipe\Recipe * The recipe object. */ - protected function createRecipe(string|array $data): Recipe { + protected function createRecipe(string|array $data, ?string $machine_name = NULL): Recipe { if (is_array($data)) { $data = Yaml::encode($data); } - $dir = uniqid('public://'); - mkdir($dir); + $recipes_dir = $this->siteDirectory . '/recipes'; + if ($machine_name === NULL) { + $dir = uniqid($recipes_dir . '/'); + } + else { + $dir = $recipes_dir . '/' . $machine_name; + } + mkdir($dir, recursive: TRUE); file_put_contents($dir . '/recipe.yml', $data); return Recipe::createFromDirectory($dir); diff --git a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeConfiguratorTest.php b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeConfiguratorTest.php index b640fd58ec5269d8427026bb5f57e181cdb5cff8..640cc182de87cc155d326f16cbe1594b04df26c9 100644 --- a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeConfiguratorTest.php +++ b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeConfiguratorTest.php @@ -4,6 +4,7 @@ namespace Drupal\KernelTests\Core\Recipe; +use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\RecipeConfigurator; use Drupal\Core\Recipe\RecipeDiscovery; use Drupal\KernelTests\KernelTestBase; @@ -15,7 +16,7 @@ class RecipeConfiguratorTest extends KernelTestBase { public function testRecipeConfigurator(): void { - $discovery = new RecipeDiscovery(['core/tests/fixtures/recipes']); + $discovery = new RecipeDiscovery('core/tests/fixtures/recipes'); $recipe_configurator = new RecipeConfigurator( ['install_two_modules', 'install_node_with_config', 'recipe_include'], $discovery @@ -24,7 +25,9 @@ public function testRecipeConfigurator(): void { $reflection = new \ReflectionMethod('\Drupal\Core\Recipe\RecipeConfigurator', 'listAllRecipes'); // Test methods. - $recipes_names = array_column((array) $reflection->invoke($recipe_configurator), 'name'); + /** @var \Drupal\Core\Recipe\Recipe[] $recipes */ + $recipes = (array) $reflection->invoke($recipe_configurator); + $recipes_names = array_map(fn(Recipe $recipe) => $recipe->name, $recipes); $recipe_extensions = $recipe_configurator->listAllExtensions(); $expected_recipes_names = [ 'Install two modules', @@ -45,7 +48,6 @@ public function testRecipeConfigurator(): void { $this->assertEquals($expected_recipe_extensions, $recipe_extensions); $this->assertEquals(1, array_count_values($recipes_names)['Install node with config']); $this->assertEquals(1, array_count_values($recipe_extensions)['field']); - } } diff --git a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeDiscoveryTest.php b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeDiscoveryTest.php index 7eab347ca7a9a4baa70e0448db014b2b87393703..430aa8f34b8598f77b1b8c35c162009cf103e828 100644 --- a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeDiscoveryTest.php +++ b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeDiscoveryTest.php @@ -27,7 +27,7 @@ public function providerRecipeDiscovery(): array { * @dataProvider providerRecipeDiscovery */ public function testRecipeDiscovery(string $recipe, string $name): void { - $discovery = new RecipeDiscovery(['core/tests/fixtures/recipes']); + $discovery = new RecipeDiscovery('core/tests/fixtures/recipes'); $recipe = $discovery->getRecipe($recipe); $this->assertSame($name, $recipe->name); } @@ -45,20 +45,15 @@ public function providerRecipeDiscoveryException(): array { * @dataProvider providerRecipeDiscoveryException */ public function testRecipeDiscoveryException(string $recipe): void { - $discovery = new RecipeDiscovery(['core/tests/fixtures/recipes']); + $discovery = new RecipeDiscovery('core/tests/fixtures/recipes'); try { $discovery->getRecipe($recipe); $this->fail('Expected exception not thrown'); } catch (UnknownRecipeException $e) { - $root = $this->getDrupalRoot(); $this->assertSame($recipe, $e->recipe); - $this->assertSame([ - 'core/tests/fixtures/recipes', - $root . '/recipes', - $root . '/core/recipes', - ], $e->searchPaths); - $this->assertSame('Can not find the ' . $recipe . ' recipe, search paths: ' . implode(', ', $e->searchPaths), $e->getMessage()); + $this->assertSame('core/tests/fixtures/recipes', $e->searchPath); + $this->assertSame('Can not find the ' . $recipe . ' recipe, search path: ' . $e->searchPath, $e->getMessage()); } } diff --git a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeEventsTest.php b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeEventsTest.php index 002299076cf46cc32ef1e6f6877dbb666c3799ae..20d12bae4c3852bd59931435dded77d87f2de930 100644 --- a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeEventsTest.php +++ b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeEventsTest.php @@ -49,7 +49,7 @@ public function register(ContainerBuilder $container): void { } public function testRecipeAppliedEvent(): void { - $recipe = Recipe::createFromDirectory($this->getDrupalRoot() . '/core/tests/fixtures/recipes/recipe_include'); + $recipe = Recipe::createFromDirectory('core/tests/fixtures/recipes/recipe_include'); RecipeRunner::processRecipe($recipe); $this->assertSame(['Install node with config', 'Recipe include'], $this->recipesApplied); diff --git a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php index 09de4be4bb6a1a5668eac9bebe97568f37dc75d1..0af066b626cde69a59b94efc548b1bfbbf6f4e49 100644 --- a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php +++ b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php @@ -5,6 +5,7 @@ namespace Drupal\KernelTests\Core\Recipe; use Drupal\Component\Plugin\Exception\PluginNotFoundException; +use Drupal\config_test\Entity\ConfigTest; use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\RecipePreExistingConfigException; use Drupal\Core\Recipe\RecipeRunner; @@ -75,7 +76,7 @@ public function testModuleConfigurationOverride(): void { $this->assertTrue($this->container->get('config.storage')->exists('node.settings'), 'The node.settings configuration has been created'); $this->assertTrue($this->container->get('config.storage')->exists('node.settings'), 'The node.settings configuration has been created'); $this->assertTrue($this->config('node.settings')->get('use_admin_theme'), 'The node.settings:use_admin_theme is set to TRUE'); - $this->assertSame('Test content type', NodeType::load('test')->label()); + $this->assertSame('Test content type', NodeType::load('test')?->label()); $node_type_data = $this->config('node.type.test')->get(); $this->assertGreaterThan(0, strlen($node_type_data['uuid']), 'The node type configuration has been assigned a UUID.'); // cSpell:disable-next-line @@ -158,8 +159,8 @@ public function testRecipeInclude(): void { // Test the state after to applying the recipe. $this->assertTrue($this->container->get('module_handler')->moduleExists('dblog'), 'Dblog module installed'); - $this->assertSame('Test content type', NodeType::load('test')->label()); - $this->assertSame('Another test content type', NodeType::load('another_test')->label()); + $this->assertSame('Test content type', NodeType::load('test')?->label()); + $this->assertSame('Another test content type', NodeType::load('another_test')?->label()); } public function testConfigActions() :void { @@ -171,8 +172,8 @@ public function testConfigActions() :void { // Test the state after to applying the recipe. $storage = \Drupal::entityTypeManager()->getStorage('config_test'); - /** @var \Drupal\config_test\Entity\ConfigTest $config_test_entity */ $config_test_entity = $storage->load('recipe'); + $this->assertInstanceOf(ConfigTest::class, $config_test_entity); $this->assertSame('Created by recipe', $config_test_entity->label()); $this->assertSame('Set by recipe', $config_test_entity->getProtectedProperty()); $this->assertSame('not bar', $this->config('config_test.system')->get('foo')); @@ -183,8 +184,8 @@ public function testConfigActionsPreExistingConfig() :void { $this->installConfig(['config_test']); $this->assertSame('bar', $this->config('config_test.system')->get('foo')); $storage = \Drupal::entityTypeManager()->getStorage('config_test'); - /** @var \Drupal\config_test\Entity\ConfigTest $config_test_entity */ $config_test_entity = $storage->create(['id' => 'recipe', 'label' => 'Created by test']); + $this->assertInstanceOf(ConfigTest::class, $config_test_entity); $config_test_entity->setProtectedProperty('Set by test'); $config_test_entity->save(); @@ -192,8 +193,8 @@ public function testConfigActionsPreExistingConfig() :void { RecipeRunner::processRecipe($recipe); // Test the state after to applying the recipe. - /** @var \Drupal\config_test\Entity\ConfigTest $config_test_entity */ $config_test_entity = $storage->load('recipe'); + $this->assertInstanceOf(ConfigTest::class, $config_test_entity); $this->assertSame('Created by test', $config_test_entity->label()); $this->assertSame('Set by recipe', $config_test_entity->getProtectedProperty()); $this->assertSame('not bar', $this->config('config_test.system')->get('foo')); @@ -218,4 +219,36 @@ public function testInvalidConfigAction() :void { RecipeRunner::processRecipe($recipe); } + public function testRecipesAreDisambiguatedByPath(): void { + $recipe_data = <<<YAML +name: 'Recipe include' +recipes: + - core/tests/fixtures/recipes/recipe_include +install: + - config_test +YAML; + + $recipe = $this->createRecipe($recipe_data, 'recipe_include'); + RecipeRunner::processRecipe($recipe); + + // Test the state after to applying the recipe. + $this->assertTrue($this->container->get('module_handler')->moduleExists('dblog'), 'Dblog module installed'); + $this->assertTrue($this->container->get('module_handler')->moduleExists('config_test'), 'Config test module installed'); + $this->assertSame('Test content type', NodeType::load('test')?->label()); + $this->assertSame('Another test content type', NodeType::load('another_test')?->label()); + + $operations = RecipeRunner::toBatchOperations($recipe); + $this->assertSame('triggerEvent', $operations[7][0][1]); + $this->assertSame('Install node with config', $operations[7][1][0]->name); + $this->assertStringEndsWith('core/tests/fixtures/recipes/install_node_with_config', $operations[7][1][0]->path); + + $this->assertSame('triggerEvent', $operations[10][0][1]); + $this->assertSame('Recipe include', $operations[10][1][0]->name); + $this->assertStringEndsWith('core/tests/fixtures/recipes/recipe_include', $operations[10][1][0]->path); + + $this->assertSame('triggerEvent', $operations[12][0][1]); + $this->assertSame('Recipe include', $operations[12][1][0]->name); + $this->assertSame($this->siteDirectory . '/recipes/recipe_include', $operations[12][1][0]->path); + } + } diff --git a/phpstan-level9-baseline.neon b/phpstan-level9-baseline.neon index 82f59183f73598b4ccbca47be5c987bcfea0dacf..b598bcfeae2a6f06418ff23cbb8bc1b5a7a98175 100644 --- a/phpstan-level9-baseline.neon +++ b/phpstan-level9-baseline.neon @@ -291,7 +291,7 @@ parameters: path: core/lib/Drupal/Core/Recipe/RecipeConfigurator.php - - message: "#^Method Drupal\\\\Core\\\\Recipe\\\\RecipeDiscovery\\:\\:__construct\\(\\) has parameter \\$paths with no value type specified in iterable type array\\.$#" + message: "#^\\\\Drupal calls should be avoided in classes, use dependency injection instead$#" count: 1 path: core/lib/Drupal/Core/Recipe/RecipeDiscovery.php @@ -390,11 +390,6 @@ parameters: count: 1 path: core/lib/Drupal/Core/Recipe/RecipeRunner.php - - - message: "#^Method Drupal\\\\Core\\\\Recipe\\\\UnknownRecipeException\\:\\:__construct\\(\\) has parameter \\$searchPaths with no value type specified in iterable type array\\.$#" - count: 1 - path: core/lib/Drupal/Core/Recipe/UnknownRecipeException.php - - message: "#^Method Drupal\\\\ckeditor5\\\\Plugin\\\\ConfigAction\\\\AddItemToToolbar\\:\\:create\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" count: 1 @@ -491,7 +486,17 @@ parameters: path: core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php - - message: "#^Cannot call method label\\(\\) on Drupal\\\\node\\\\Entity\\\\NodeType\\|null\\.$#" + message: "#^Cannot access offset 1 on callable\\(\\)\\: mixed\\.$#" + count: 3 + path: core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php + + - + message: "#^Cannot access property \\$name on mixed\\.$#" + count: 3 + path: core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php + + - + message: "#^Cannot access property \\$path on mixed\\.$#" count: 3 path: core/tests/Drupal/KernelTests/Core/Recipe/RecipeRunnerTest.php