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)); } }