}} example names an option Divi 5.15.1 really registers (research 4e, table of the loop_* options). */ final class LoopGuideTest extends TestCase { /** Divi 5.15.1 option keys (without WooCommerce and ACF repeater options). */ private const DIVI_LOOP_OPTIONS = [ 'loop_post_title', 'loop_post_excerpt', 'loop_post_date', 'loop_post_modified_date', 'loop_post_author', 'loop_post_author_bio', 'loop_post_author_profile_picture', 'loop_post_comment_count', 'loop_post_featured_image', 'loop_post_id', 'loop_post_link', 'loop_post_terms', 'loop_term_name', 'loop_term_description', 'loop_term_count', 'loop_term_permalink', 'loop_term_taxonomy', 'loop_term_featured_image', 'loop_user_name', 'loop_user_username', 'loop_user_email', 'loop_user_avatar', 'loop_user_description', 'loop_user_url', 'loop_menu_text', 'loop_menu_link', 'loop_menu_menu_order', 'loop_menu_attr_title', 'loop_menu_classes', 'loop_menu_xfn', 'loop_menu_description', ]; private const PHRASES = ['braucht eine Divi-Schleife', 'passt nicht zur Schleife', 'kein Schleifen-Kontext']; private static function root(): string { return dirname(__DIR__, 3); } /** @return string Content of all guides. */ private static function guides(): string { $text = ''; foreach (glob(self::root() . '/docs/guides/*.md') ?: [] as $file) { $text .= (string) file_get_contents($file) . "\n"; } return $text; } public function testGuidesExplainTheLoopRefusals(): void { $guides = self::guides(); foreach (self::PHRASES as $phrase) { self::assertStringContainsString($phrase, $guides, 'guides lack "' . $phrase . '"'); } } public function testRefusalPhrasesAreTheOnesTheCodeSays(): void { // Only string literals count (no comments), matched exactly including case. $code = ''; $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(self::root() . '/src')); foreach ($files as $file) { if (! str_ends_with((string) $file, '.php')) { continue; } foreach (token_get_all((string) file_get_contents((string) $file)) as $token) { if (is_array($token) && in_array($token[0], [T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE], true)) { $code .= $token[1] . "\n"; } } } foreach (self::PHRASES as $phrase) { self::assertNotFalse(mb_strpos($code, $phrase), 'no source string says "' . $phrase . '"'); } } public function testLoopShortFormExamplesNameRealDiviOptions(): void { preg_match_all('/\{\{loop:([a-z0-9_]+)\}\}/', self::guides(), $examples); self::assertNotEmpty($examples[1], 'the guides show no {{loop:…}} example'); foreach (array_unique($examples[1]) as $name) { self::assertContains('loop_' . $name, self::DIVI_LOOP_OPTIONS, '{{loop:' . $name . '}} is no Divi option'); self::assertSame('loop_' . $name, Placeholders::LOOP[$name] ?? null, '{{loop:' . $name . '}} is not in Placeholders::LOOP'); } } }