Files
janniclas.loosenandClaude Opus 5.5 a31d4340f9 fix: TB dry runs hide uneditable live templates, off-switch despite draft (rulings Q20, Q21)
- ThemeBuilderAccess::editableLiveTemplates() shared by tb-list and the tb-assign/tb-activate dry runs;
  uneditable live templates only as a count (hidden_overlaps, hidden_live)
- tb-activate enabled:false works while the Theme Builder has a draft, with a warning
- possible_overlaps skips identical conditions; reattach records the previous enabled state;
  exclude_from must be a list; tb-activate live from the read-back

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 20:55:53 +02:00

168 lines
8.2 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\ThemeBuilder;
use LIT\MCP\Integrations\Divi\ThemeBuilder\Template;
use LIT\MCP\Tests\Support\ThemeBuilderFixtures;
use PHPUnit\Framework\TestCase;
final class TemplateTest extends TestCase
{
public function testReadsDivisTemplateArray(): void
{
$template = ThemeBuilderFixtures::template();
self::assertSame(1066, $template->id);
self::assertFalse($template->enabled);
self::assertSame(['id' => 1062, 'enabled' => true], $template->layouts['body']);
self::assertSame(['singular:post_type:page:id:1053'], $template->useOn);
self::assertSame(['page'], $template->postTypes());
self::assertFalse($template->isLive());
}
/** Ruling Q8/B8: Divi renders every listed, enabled template whatever its post status – fail closed. */
public function testLiveMeansAttachedAndEnabledWhateverThePostStatus(): void
{
self::assertTrue(ThemeBuilderFixtures::template(1066, ['enabled' => true])->isLive());
self::assertFalse(ThemeBuilderFixtures::template(1066, ['enabled' => true, 'attached' => false])->isLive());
self::assertFalse(ThemeBuilderFixtures::template(1066, ['attached' => true])->isLive());
self::assertTrue(ThemeBuilderFixtures::template(1066, ['enabled' => true, 'status' => 'trash'])->isLive());
self::assertTrue(ThemeBuilderFixtures::template(1066, ['enabled' => true, 'status' => 'draft'])->isLive());
self::assertTrue(ThemeBuilderFixtures::template(1066, ['enabled' => true])->showsLayout('body', 1062));
$hiddenBody = ThemeBuilderFixtures::template(1066, [
'enabled' => true,
'layouts' => [
'header' => ['id' => 1061, 'enabled' => true],
'body' => ['id' => 1062, 'enabled' => false],
'footer' => ['id' => 1063, 'enabled' => true],
],
]);
self::assertFalse($hiddenBody->showsLayout('body', 1062));
self::assertFalse(ThemeBuilderFixtures::template()->showsLayout('body', 1062), 'disabled template');
}
/** Stored as Divi stores it (stage3b/tb-template--person.json): layout IDs and flags as strings. */
public function testReadsDivisStorageTypes(): void
{
$template = Template::fromDivi([
'id' => '2394',
'title' => '[Fixture 3b] Person',
'default' => false,
'enabled' => false,
'layouts' => [
'header' => ['id' => '1061', 'enabled' => true, 'override' => true, 'global' => false],
'body' => ['id' => '2391', 'enabled' => true],
'footer' => ['id' => '0', 'enabled' => true],
],
'use_on' => ['singular:post_type:lit_fixture_person:all'],
'exclude_from' => [],
], 'publish', true, '2026-09-27 12:47:02');
self::assertSame(2394, $template->id);
self::assertSame(['id' => 1061, 'enabled' => true], $template->layouts['header']);
self::assertSame(['id' => 0, 'enabled' => true], $template->layouts['footer']);
self::assertSame(['lit_fixture_person'], $template->postTypes());
self::assertSame('2026-09-27 12:47:02', $template->toArray()['marked_unused']);
}
public function testRevChangesWithEveryField(): void
{
$rev = ThemeBuilderFixtures::template()->rev();
self::assertSame($rev, ThemeBuilderFixtures::template()->rev());
self::assertNotSame($rev, ThemeBuilderFixtures::template(1066, ['enabled' => true])->rev());
self::assertNotSame($rev, ThemeBuilderFixtures::template(1066, ['use_on' => ['homepage']])->rev());
self::assertNotSame($rev, ThemeBuilderFixtures::template(1066, ['attached' => false])->rev());
}
public function testOverlapsNameOtherLiveTemplatesWithSharedConditions(): void
{
$other = ThemeBuilderFixtures::template(1070, [
'enabled' => true,
'title' => 'Seiten',
'use_on' => ['singular:post_type:page:all', 'homepage'],
]);
$disabled = ThemeBuilderFixtures::template(1071, ['use_on' => ['homepage']]);
self::assertSame(
[['id' => 1070, 'title' => 'Seiten', 'conditions' => ['homepage']]],
Template::overlaps([$other, $disabled], 1066, ['homepage', 'search'])
);
self::assertSame([], Template::overlaps([$other], 1070, ['homepage']));
}
/** Ruling Q8/B9: conditions of the same family may overlap (Divi then picks one template). */
public function testPossibleOverlapsNameOtherLiveTemplatesOfTheSameFamily(): void
{
$pages = ThemeBuilderFixtures::template(1070, [
'enabled' => true,
'title' => 'Seiten',
'use_on' => ['singular:post_type:page:all', 'search'],
]);
$archives = ThemeBuilderFixtures::template(1071, ['enabled' => true, 'title' => 'Archive', 'use_on' => ['archive:all']]);
$inTerm = ThemeBuilderFixtures::template(1072, [
'enabled' => true,
'title' => 'Beiträge einer Kategorie',
'use_on' => ['singular:taxonomy:category:term:id:5'],
]);
$disabled = ThemeBuilderFixtures::template(1073, ['use_on' => ['singular:post_type:page:all']]);
$live = [$pages, $archives, $inTerm, $disabled];
self::assertSame(
[
['id' => 1070, 'title' => 'Seiten', 'conditions' => ['singular:post_type:page:all']],
['id' => 1072, 'title' => 'Beiträge einer Kategorie', 'conditions' => ['singular:taxonomy:category:term:id:5']],
],
Template::possibleOverlaps($live, 1066, ['singular:post_type:page:id:1053'])
);
self::assertSame(
[['id' => 1071, 'title' => 'Archive', 'conditions' => ['archive:all']]],
Template::possibleOverlaps($live, 1066, ['archive:taxonomy:category:term:id:5'])
);
self::assertSame([], Template::possibleOverlaps($live, 1066, ['search']), 'identical ones are overlaps()');
self::assertSame([], Template::possibleOverlaps($live, 1066, ['404']));
self::assertSame(
['id' => 1070, 'title' => 'Seiten', 'conditions' => ['singular:post_type:page:all']],
Template::possibleOverlaps($live, 1066, ['homepage'])[0],
'a static front page is a page'
);
}
/** Fix round 1: a condition the template shares exactly is in overlaps() only, never twice. */
public function testPossibleOverlapsSkipIdenticalConditions(): void
{
$other = ThemeBuilderFixtures::template(1070, [
'enabled' => true,
'title' => 'Seiten',
'use_on' => ['homepage', 'singular:post_type:page:all'],
]);
$useOn = ['homepage', 'singular:post_type:page:id:1053'];
self::assertSame(['homepage'], Template::overlaps([$other], 1066, $useOn)[0]['conditions']);
self::assertSame(['singular:post_type:page:all'], Template::possibleOverlaps([$other], 1066, $useOn)[0]['conditions']);
}
/** Ruling Q21: overlapping templates the user may not edit only as a number. */
public function testOverlapDetailsCountHiddenTemplatesOnly(): void
{
$mine = ThemeBuilderFixtures::template(1070, ['enabled' => true, 'title' => 'Meine', 'use_on' => ['search']]);
$hiddenExact = ThemeBuilderFixtures::template(1071, ['enabled' => true, 'use_on' => ['search', 'homepage']]);
$hiddenPossible = ThemeBuilderFixtures::template(1072, ['enabled' => true, 'use_on' => ['singular:post_type:page:all']]);
$hiddenOther = ThemeBuilderFixtures::template(1073, ['enabled' => true, 'use_on' => ['404']]);
$details = Template::overlapDetails(
['templates' => [$mine], 'hidden' => [$hiddenExact, $hiddenPossible, $hiddenOther]],
1066,
['search', 'homepage']
);
self::assertSame([['id' => 1070, 'title' => 'Meine', 'conditions' => ['search']]], $details['overlaps']);
self::assertSame([], $details['possible_overlaps']);
self::assertSame(2, $details['hidden_overlaps']);
self::assertStringContainsString('ohne Berechtigung', Template::hiddenOverlapsNote(2));
self::assertSame('', Template::hiddenOverlapsNote(0));
}
}