Problem/Motivation

I'm seeing a problem with drupalPlaceBlock() in functional tests on 11.3.x. What's happening is Drupal is placing the block via the incorrect theme. This problem seems to only occur within a multisite setup in a theme test.

Steps to reproduce

Say I have the below 2 tests:
docroot/modules/custom/test_module/tests/src/Functional/ModuleTest.php
docroot/themes/custom/test_theme/tests/src/Functional/ThemeTest.php

In the first one, The stark theme is being used as the $defaultTheme. Say I use $this->drupalPlaceBlock(). No problems. Block is placed correctly using stark.

In the second one, say I'm setting $defaultTheme to test_theme right in my Functional test. When I use $this->drupalPlaceBlock(), there are also no problems. The block is placed using my custom theme.

Now for the problem... say I have a functional test via a multisite setup located here:
docroot/sites/custom_site/themes/custom_theme/tests/src/Functional/CustomThemeTest.php

and say I have code in my test that looks like this:

protected $defaultTheme = 'custom_theme';

public function testCustomTheme(): void {
  $this->drupalPlaceBlock('system_powered_by_block');
  $this->drupalGet('<front>');
  // Ensure block appears.
  $this->assertSession()->elementExists('css', '.block-system-powered-by-block');
}

The block is not there! What I discovered was that Drupal is placing that block via the stark theme, even though I'm defining the $defaultThemeas custom_theme in my test.

In my 2nd example above(in ThemeTest.php), I'm also doing that, but $this->drupalPlaceBlock() works fine and places the block via my custom theme. This seems to only happen in a multisite setup.

The problem appears to be related to code inside of BlockCreationTrait.php. More specifically, this line: 'theme' => $config->get('system.theme')->get('default'),

That line returns stark and not my custom theme.

The strange part is, if I did this in my test instead:

public function testCustomTheme(): void {
    $this->drupalGet('<front>');
    $this->drupalPlaceBlock('system_powered_by_block');
    $this->drupalGet('<front>');
    // Ensure block appears.
    $this->assertSession()->elementExists('css', '.block-system-powered-by-block');
  }

Then it works (doing a $this->drupalGet('<front>'); BEFORE placing the block). In that case, it now places the block using my custom theme. It seems like that drupalGet() call refreshes something and Drupal now sees the correct theme.

Proposed resolution

In my research, this was originally caused by https://www.drupal.org/project/drupal/issues/3544715 and more specifically, this commit: https://git.drupalcode.org/project/drupal/-/commit/7c9aacf51fc9b4486952e....

It seems like what's happening is that the ThemeInstaller is rebuilding the container mid-install, so the parent method then saves system.theme via a now-stale $container reference, leaving the global config factory's system.theme object as stark.

To get around this, I'm hooking into installDefaultThemeFromClassProperty() and then I'm adding this: \Drupal::configFactory()->reset('system.theme');.

Now $this->drupalPlaceBlock() is placing the block via the correct theme.

Not sure if this is the correct solution here, but $config->get('system.theme')->get('default') is definitely returning an incorrect theme in the scenario I walked through above. So something like $this->drupalPlaceBlock()can potentially place a block using the wrong theme.

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Comments

tlo405 created an issue. See original summary.

quietone’s picture

Version: 11.3.x-dev » main
Issue summary: View changes
Issue tags: -theme

Issues are fixed on main first, then backported as needed.

hinal05’s picture

Status: Active » Needs review

I think this might already be fixed. Commit 6a9373d79a2 (Aug 25, unrelated task #3614825) dropped the installDefaultThemeFromClassProperty($container) call this issue is about. default_theme now gets set via installParameters() before install, so there's no stale $container involved anymore.

I couldn't get the original repro to actually fail locally, even on the old code, so I can't say for sure this fixes it. Just that the code it points to is gone. Setting to Needs review so someone can check against the real steps.

Generated with the help of an LLM.

smustgrave’s picture

Status: Needs review » Needs work

May be worth adding a test proving this is fixed in main.