|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace MintyPHP\Template\Tests; |
| 4 | + |
| 5 | +use MintyPHP\Template\Template; |
| 6 | +use PHPUnit\Framework\TestCase; |
| 7 | + |
| 8 | +class ControlStructuresTest extends TestCase |
| 9 | +{ |
| 10 | + private static Template $template; |
| 11 | + |
| 12 | + public static function setUpBeforeClass(): void |
| 13 | + { |
| 14 | + self::$template = new Template(); |
| 15 | + } |
| 16 | + |
| 17 | + public function testRenderIfWithNestedPath(): void |
| 18 | + { |
| 19 | + $this->assertEquals("hello m is 3", self::$template->render('hello {% if n.m|eq(3) %}m is 3{% endif %}', ['n' => ['m' => 3]], ['eq' => fn($a, $b) => $a == $b])); |
| 20 | + } |
| 21 | + |
| 22 | + public function testRenderIfElse(): void |
| 23 | + { |
| 24 | + $this->assertEquals("hello not n", self::$template->render('hello {% if n %}n{% else %}not n{% endif %}', ['n' => false])); |
| 25 | + } |
| 26 | + |
| 27 | + public function testRenderForLoopWithValues(): void |
| 28 | + { |
| 29 | + $this->assertEquals("test 1 2 3", self::$template->render('test{% for i in counts %} {{ i }}{% endfor %}', ['counts' => [1, 2, 3]])); |
| 30 | + } |
| 31 | + |
| 32 | + public function testRenderForLoopWithKeysAndValues(): void |
| 33 | + { |
| 34 | + $this->assertEquals("test a=1 b=2 c=3", self::$template->render('test{% for k, v in counts %} {{ k }}={{ v }}{% endfor %}', ['counts' => ['a' => 1, 'b' => 2, 'c' => 3]])); |
| 35 | + } |
| 36 | + |
| 37 | + public function testRenderNestedForLoops(): void |
| 38 | + { |
| 39 | + $this->assertEquals("test (-1,-1) (-1,1) (1,-1) (1,1)", self::$template->render('test{% for x in steps %}{% for y in steps %} ({{ x }},{{ y }}){% endfor %}{% endfor %}', ['steps' => [-1, 1]])); |
| 40 | + } |
| 41 | + |
| 42 | + public function testRenderForLoopWithIfElseIf(): void |
| 43 | + { |
| 44 | + $this->assertEquals("hello one two three", self::$template->render('hello{% for i in counts %} {% if i|eq(1) %}one{% elseif i|eq(2) %}two{% else %}three{% endif %}{% endfor %}', ['counts' => [1, 2, 3]], ['eq' => fn($a, $b) => $a == $b])); |
| 45 | + } |
| 46 | + |
| 47 | + // Multiline template tests inspired by Jinja |
| 48 | + public function testMultilineForLoopSimple(): void |
| 49 | + { |
| 50 | + $template = "<ul>\n{% for item in items %}\n <li>{{ item }}</li>\n{% endfor %}\n</ul>"; |
| 51 | + $expected = "<ul>\n <li>apple</li>\n <li>banana</li>\n <li>cherry</li>\n</ul>"; |
| 52 | + $this->assertEquals($expected, self::$template->render($template, ['items' => ['apple', 'banana', 'cherry']])); |
| 53 | + } |
| 54 | + |
| 55 | + public function testMultilineForLoopWithIndentation(): void |
| 56 | + { |
| 57 | + $template = "<div>\n <ul>\n {% for user in users %}\n <li>{{ user }}</li>\n {% endfor %}\n </ul>\n</div>"; |
| 58 | + $expected = "<div>\n <ul>\n <li>Alice</li>\n <li>Bob</li>\n </ul>\n</div>"; |
| 59 | + $this->assertEquals($expected, self::$template->render($template, ['users' => ['Alice', 'Bob']])); |
| 60 | + } |
| 61 | + |
| 62 | + public function testMultilineIfWithWhitespace(): void |
| 63 | + { |
| 64 | + $template = "<div>\n {% if active %}\n <span>Active</span>\n {% endif %}\n</div>"; |
| 65 | + $expected = "<div>\n <span>Active</span>\n</div>"; |
| 66 | + $this->assertEquals($expected, self::$template->render($template, ['active' => true])); |
| 67 | + } |
| 68 | + |
| 69 | + public function testMultilineIfElseWithWhitespace(): void |
| 70 | + { |
| 71 | + $template = "<div>\n {% if active %}\n <span>Active</span>\n {% else %}\n <span>Inactive</span>\n {% endif %}\n</div>"; |
| 72 | + $expected = "<div>\n <span>Inactive</span>\n</div>"; |
| 73 | + $this->assertEquals($expected, self::$template->render($template, ['active' => false])); |
| 74 | + } |
| 75 | + |
| 76 | + public function testMultilineNestedForLoops(): void |
| 77 | + { |
| 78 | + $template = "<table>\n{% for row in rows %}\n <tr>\n {% for cell in row %}\n <td>{{ cell }}</td>\n {% endfor %}\n </tr>\n{% endfor %}\n</table>"; |
| 79 | + $expected = "<table>\n <tr>\n <td>1</td>\n <td>2</td>\n </tr>\n <tr>\n <td>3</td>\n <td>4</td>\n </tr>\n</table>"; |
| 80 | + $this->assertEquals($expected, self::$template->render($template, ['rows' => [[1, 2], [3, 4]]])); |
| 81 | + } |
| 82 | + |
| 83 | + public function testMultilineComplexHtmlStructure(): void |
| 84 | + { |
| 85 | + $template = "<!DOCTYPE html>\n<html>\n<head>\n <title>{{ title }}</title>\n</head>\n<body>\n <ul id=\"navigation\">\n {% for item in navigation %}\n <li><a href=\"{{ item.href }}\">{{ item.caption }}</a></li>\n {% endfor %}\n </ul>\n <h1>{{ heading }}</h1>\n</body>\n</html>"; |
| 86 | + |
| 87 | + $data = [ |
| 88 | + 'title' => 'My Page', |
| 89 | + 'heading' => 'Welcome', |
| 90 | + 'navigation' => [ |
| 91 | + ['href' => '/home', 'caption' => 'Home'], |
| 92 | + ['href' => '/about', 'caption' => 'About'] |
| 93 | + ] |
| 94 | + ]; |
| 95 | + |
| 96 | + $expected = "<!DOCTYPE html>\n<html>\n<head>\n <title>My Page</title>\n</head>\n<body>\n <ul id=\"navigation\">\n <li><a href=\"/home\">Home</a></li>\n <li><a href=\"/about\">About</a></li>\n </ul>\n <h1>Welcome</h1>\n</body>\n</html>"; |
| 97 | + |
| 98 | + $this->assertEquals($expected, self::$template->render($template, $data)); |
| 99 | + } |
| 100 | + |
| 101 | + public function testWhitespacePreservationWithLeadingSpaces(): void |
| 102 | + { |
| 103 | + $template = " Leading spaces\n{{ text }}\n Trailing spaces "; |
| 104 | + $expected = " Leading spaces\nHello\n Trailing spaces "; |
| 105 | + $this->assertEquals($expected, self::$template->render($template, ['text' => 'Hello'])); |
| 106 | + } |
| 107 | + |
| 108 | + public function testWhitespacePreservationWithTabs(): void |
| 109 | + { |
| 110 | + $template = "\t\tTabbed content\n{{ text }}\n\t\tMore tabs"; |
| 111 | + $expected = "\t\tTabbed content\nWorld\n\t\tMore tabs"; |
| 112 | + $this->assertEquals($expected, self::$template->render($template, ['text' => 'World'])); |
| 113 | + } |
| 114 | + |
| 115 | + public function testWhitespacePreservationEmptyLines(): void |
| 116 | + { |
| 117 | + $template = "Line 1\n\n{{ text }}\n\nLine 4"; |
| 118 | + $expected = "Line 1\n\nTest\n\nLine 4"; |
| 119 | + $this->assertEquals($expected, self::$template->render($template, ['text' => 'Test'])); |
| 120 | + } |
| 121 | + |
| 122 | + public function testMultilineForLoopWithEmptyList(): void |
| 123 | + { |
| 124 | + $template = "<ul>\n{% for item in items %}\n <li>{{ item }}</li>\n{% endfor %}\n</ul>"; |
| 125 | + $expected = "<ul>\n</ul>"; |
| 126 | + $this->assertEquals($expected, self::$template->render($template, ['items' => []])); |
| 127 | + } |
| 128 | + |
| 129 | + public function testMultilineIfWithFalseCondition(): void |
| 130 | + { |
| 131 | + $template = "<div>\n Content before\n {% if show %}\n This should not appear\n {% endif %}\n Content after\n</div>"; |
| 132 | + $expected = "<div>\n Content before\n Content after\n</div>"; |
| 133 | + $this->assertEquals($expected, self::$template->render($template, ['show' => false])); |
| 134 | + } |
| 135 | + |
| 136 | + public function testMultilineTextPreservation(): void |
| 137 | + { |
| 138 | + $template = "First line\nSecond line\nThird line with {{ var }}\nFourth line"; |
| 139 | + $expected = "First line\nSecond line\nThird line with value\nFourth line"; |
| 140 | + $this->assertEquals($expected, self::$template->render($template, ['var' => 'value'])); |
| 141 | + } |
| 142 | + |
| 143 | + public function testMultilineWithMixedContentTypes(): void |
| 144 | + { |
| 145 | + $template = "<p>\n Text content\n {{ text }}\n {% if show %}\n <strong>{{ emphasis }}</strong>\n {% endif %}\n More text\n</p>"; |
| 146 | + $expected = "<p>\n Text content\n Hello\n <strong>Important</strong>\n More text\n</p>"; |
| 147 | + $this->assertEquals($expected, self::$template->render($template, ['text' => 'Hello', 'show' => true, 'emphasis' => 'Important'])); |
| 148 | + } |
| 149 | + |
| 150 | + public function testMultilineHtmlListWithData(): void |
| 151 | + { |
| 152 | + $template = "<h1>Members</h1>\n<ul>\n{% for user in users %}\n <li>{{ user.username }}</li>\n{% endfor %}\n</ul>"; |
| 153 | + $expected = "<h1>Members</h1>\n<ul>\n <li>alice</li>\n <li>bob</li>\n <li>charlie</li>\n</ul>"; |
| 154 | + $data = [ |
| 155 | + 'users' => [ |
| 156 | + ['username' => 'alice'], |
| 157 | + ['username' => 'bob'], |
| 158 | + ['username' => 'charlie'] |
| 159 | + ] |
| 160 | + ]; |
| 161 | + $this->assertEquals($expected, self::$template->render($template, $data)); |
| 162 | + } |
| 163 | + |
| 164 | + public function testMultilineNestedIfStatements(): void |
| 165 | + { |
| 166 | + $template = "<div>\n{% if outer %}\n <div class=\"outer\">\n {% if inner %}\n <div class=\"inner\">Content</div>\n {% endif %}\n </div>\n{% endif %}\n</div>"; |
| 167 | + $expected = "<div>\n <div class=\"outer\">\n <div class=\"inner\">Content</div>\n </div>\n</div>"; |
| 168 | + $this->assertEquals($expected, self::$template->render($template, ['outer' => true, 'inner' => true])); |
| 169 | + } |
| 170 | + |
| 171 | + public function testMultilineWhitespaceOnlyBetweenTags(): void |
| 172 | + { |
| 173 | + $template = "<div> \n {{ text }} \n </div>"; |
| 174 | + $expected = "<div> \n Value \n </div>"; |
| 175 | + $this->assertEquals($expected, self::$template->render($template, ['text' => 'Value'])); |
| 176 | + } |
| 177 | + |
| 178 | + public function testMultilineCommentLikeStructure(): void |
| 179 | + { |
| 180 | + // Test Jinja-style {# #} comment syntax - comments should be completely removed |
| 181 | + $template = "<div>\n {# This is a comment #}\n {{ content }}\n {# Another comment #}\n</div>"; |
| 182 | + $expected = "<div>\n Data\n</div>"; |
| 183 | + $this->assertEquals($expected, self::$template->render($template, ['content' => 'Data'])); |
| 184 | + } |
| 185 | + |
| 186 | + public function testMultilineForLoopWithComplexData(): void |
| 187 | + { |
| 188 | + $template = "<dl>\n{% for item in items %}\n <dt>{{ item.key }}</dt>\n <dd>{{ item.value }}</dd>\n{% endfor %}\n</dl>"; |
| 189 | + $expected = "<dl>\n <dt>Name</dt>\n <dd>John</dd>\n <dt>Age</dt>\n <dd>30</dd>\n</dl>"; |
| 190 | + $data = [ |
| 191 | + 'items' => [ |
| 192 | + ['key' => 'Name', 'value' => 'John'], |
| 193 | + ['key' => 'Age', 'value' => '30'] |
| 194 | + ] |
| 195 | + ]; |
| 196 | + $this->assertEquals($expected, self::$template->render($template, $data)); |
| 197 | + } |
| 198 | + |
| 199 | + public function testMultilineTemplateWithNoWhitespace(): void |
| 200 | + { |
| 201 | + $template = "<ul>{% for i in items %}<li>{{ i }}</li>{% endfor %}</ul>"; |
| 202 | + $expected = "<ul><li>A</li><li>B</li></ul>"; |
| 203 | + $this->assertEquals($expected, self::$template->render($template, ['items' => ['A', 'B']])); |
| 204 | + } |
| 205 | + |
| 206 | + public function testMultilineIndentationVariations(): void |
| 207 | + { |
| 208 | + $template = "<div>\n Two spaces\n Four spaces\n\tOne tab\n{{ text }}\n</div>"; |
| 209 | + $expected = "<div>\n Two spaces\n Four spaces\n\tOne tab\nValue\n</div>"; |
| 210 | + $this->assertEquals($expected, self::$template->render($template, ['text' => 'Value'])); |
| 211 | + } |
| 212 | +} |
0 commit comments