Skip to content

Commit bafb01b

Browse files
committed
update
1 parent 8061ade commit bafb01b

9 files changed

Lines changed: 1643 additions & 1566 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Template Engine Documentation
1+
# MintyPHP Template
22

33
## Overview
44

tests/ControlStructuresTest.php

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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

Comments
 (0)