- 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>
168 lines
8.2 KiB
PHP
168 lines
8.2 KiB
PHP
<?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));
|
||
}
|
||
}
|