88use PHPUnit \Framework \TestCase ;
99
1010/**
11+ * @phpstan-type ErrorCase array{template: string, expected: string}
1112 * @phpstan-type SpecToken array{name: string, text: string}
1213 * @phpstan-type SpecArr array{it: string, number?: string, template: string, expected?: string, exception?: string|true}
1314 */
@@ -31,6 +32,217 @@ public function testParse(): void
3132 $ this ->assertSame (file_get_contents ('tests/test1.json ' ), $ actual );
3233 }
3334
35+ /**
36+ * @return list<array{string}>
37+ */
38+ public static function successProvider (): array
39+ {
40+ return [
41+ ['{{winner.[.test6]}} ' ],
42+ ['{{winner.[#te.st7]}} ' ],
43+ ['{{test8}} ' ],
44+ ['{{[testD]}} ' ],
45+ ['{{te.[est].endK}} ' ],
46+ ['{{te.[est]o.endN}} ' ],
47+ ['{{te.[e.st].endO}} ' ],
48+ ['{{te.[e.s[t].endP}} ' ],
49+ ['{{te.[e[s.t].endQ}} ' ],
50+ ['{{#with items}}OK!{{/with}} ' ],
51+ ];
52+ }
53+
54+ #[DataProvider("successProvider " )]
55+ public function testExpectedSuccess (string $ template ): void
56+ {
57+ $ parser = (new ParserFactory ())->create ();
58+
59+ try {
60+ $ parser ->parse ($ template );
61+ $ this ->expectNotToPerformAssertions ();
62+ } catch (\Exception $ e ) {
63+ $ this ->fail ("Unexpected exception: {$ e ->getMessage ()}" );
64+ }
65+ }
66+
67+ /**
68+ * @return list<ErrorCase>
69+ */
70+ public static function errorProvider (): array
71+ {
72+ return [
73+ [
74+ 'template ' => "some \nlong \ncontext \n{{{foo}} additional \nsentence " ,
75+ 'expected ' => "Parse error on line 4: \n...longcontext{{{foo}} additionalsenten "
76+ . "\n--------------------^ \nExpecting CLOSE_UNESCAPED, got CLOSE " ,
77+ ],
78+ [
79+ 'template ' => '{{testerr1}}} ' ,
80+ 'expected ' => "Parse error on line 1: \n{{testerr1}}} \n----------^ \nExpecting CLOSE, got CLOSE_UNESCAPED " ,
81+ ],
82+ [
83+ 'template ' => '{{{testerr2}} ' ,
84+ 'expected ' => "Parse error on line 1: \n{{{testerr2}} \n-----------^ \nExpecting CLOSE_UNESCAPED, got CLOSE " ,
85+ ],
86+ [
87+ 'template ' => '{{{#testerr3}}} ' ,
88+ 'expected ' => "Parse error on line 1: \n{{{#testerr3}}} \n---^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
89+ ],
90+ [
91+ 'template ' => '{{{!testerr4}}} ' ,
92+ 'expected ' => "Parse error on line 1: \n{{{!testerr4}}} \n---^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
93+ ],
94+ [
95+ 'template ' => '{{{^testerr5}}} ' ,
96+ 'expected ' => "Parse error on line 1: \n{{{^testerr5}}} \n---^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
97+ ],
98+ [
99+ 'template ' => '{{{/testerr6}}} ' ,
100+ 'expected ' => "Parse error on line 1: \n{{{/testerr6}}} \n---^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got SEP " ,
101+ ],
102+ [
103+ 'template ' => '{{win[ner.test1}} ' ,
104+ 'expected ' => "Parse error on line 1: \n{{win[ner.test1}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
105+ ],
106+ [
107+ 'template ' => '{{win]ner.test2}} ' ,
108+ 'expected ' => "Parse error on line 1: \n{{win]ner.test2}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
109+ ],
110+ [
111+ 'template ' => '{{wi[n]ner.test3}} ' ,
112+ 'expected ' => "Parse error on line 1: \n{{wi[n]ner.test3}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
113+ ],
114+ [
115+ 'template ' => '{{winner].[test4]}} ' ,
116+ 'expected ' => "Parse error on line 1: \n{{winner].[test4]}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
117+ ],
118+ [
119+ 'template ' => '{{winner[.test5]}} ' ,
120+ 'expected ' => "Parse error on line 1: \n{{winner[.test5]}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
121+ ],
122+ [
123+ 'template ' => '{{test9]}} ' ,
124+ 'expected ' => "Parse error on line 1: \n{{test9]}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
125+ ],
126+ [
127+ 'template ' => '{{testA[}} ' ,
128+ 'expected ' => "Parse error on line 1: \n{{testA[}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
129+ ],
130+ [
131+ 'template ' => '{{[testB}} ' ,
132+ 'expected ' => "Parse error on line 1: \n{{[testB}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
133+ ],
134+ [
135+ 'template ' => '{{]testC}} ' ,
136+ 'expected ' => "Parse error on line 1: \n{{]testC}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
137+ ],
138+ [
139+ 'template ' => '{{te]stE}} ' ,
140+ 'expected ' => "Parse error on line 1: \n{{te]stE}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
141+ ],
142+ [
143+ 'template ' => '{{tee[stF}} ' ,
144+ 'expected ' => "Parse error on line 1: \n{{tee[stF}} \n--^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
145+ ],
146+ [
147+ 'template ' => '{{te.e[stG}} ' ,
148+ 'expected ' => "Parse error on line 1: \n{{te.e[stG}} \n-----^ \nExpecting ID, got INVALID " ,
149+ ],
150+ [
151+ 'template ' => '{{te.e]stH}} ' ,
152+ 'expected ' => "Parse error on line 1: \n{{te.e]stH}} \n-----^ \nExpecting ID, got INVALID " ,
153+ ],
154+ [
155+ 'template ' => '{{te.e[st.endI}} ' ,
156+ 'expected ' => "Parse error on line 1: \n{{te.e[st.endI}} \n-----^ \nExpecting ID, got INVALID " ,
157+ ],
158+ [
159+ 'template ' => '{{te.e]st.endJ}} ' ,
160+ 'expected ' => "Parse error on line 1: \n{{te.e]st.endJ}} \n-----^ \nExpecting ID, got INVALID " ,
161+ ],
162+ [
163+ 'template ' => '{{te.t[est].endL}} ' ,
164+ 'expected ' => "Parse error on line 1: \n{{te.t[est].endL}} \n-----^ \nExpecting ID, got INVALID " ,
165+ ],
166+ [
167+ 'template ' => '{{te.t[est]o.endM}} ' ,
168+ 'expected ' => "Parse error on line 1: \n{{te.t[est]o.endM}} \n-----^ \nExpecting ID, got INVALID " ,
169+ ],
170+ [
171+ 'template ' => '<ul>{{#each item}}<li>{{name}}</li> ' ,
172+ 'expected ' => "Parse error on line 1: \n...m}}<li>{{name}}</li> \n-----------------------^ \nExpecting OPEN_ENDBLOCK, got EOF " ,
173+ ],
174+ [
175+ 'template ' => 'issue63: {{test_join}} Test! {{this}} {{/test_join}} ' ,
176+ 'expected ' => "Parse error on line 1: \n...in}} Test! {{this}} {{/test_join}} \n-----------------------^ \nExpecting EOF, got OPEN_ENDBLOCK " ,
177+ ],
178+ [
179+ 'template ' => '{{#if a}}TEST{{/with}} ' ,
180+ 'expected ' => "if doesn't match with - 1:3 " ,
181+ ],
182+ [
183+ 'template ' => '{{#foo}}error{{/bar}} ' ,
184+ 'expected ' => "foo doesn't match bar - 1:3 " ,
185+ ],
186+ [
187+ 'template ' => '{{{{foo}}}} {{ {{{{/bar}}}} ' ,
188+ 'expected ' => "foo doesn't match bar - 1:4 " ,
189+ ],
190+ [
191+ 'template ' => '{{a=b}} ' ,
192+ 'expected ' => "Parse error on line 1: \n{{a=b}} \n---^ \nExpecting CLOSE, got EQUALS " ,
193+ ],
194+ [
195+ 'template ' => '{{#with a}OK!{{/with}} ' ,
196+ 'expected ' => "Parse error on line 1: \n{{#with a}OK!{{/with}} \n---------^ \nExpecting CLOSE, got INVALID " ,
197+ ],
198+ [
199+ 'template ' => '{{#each a}OK!{{/each}} ' ,
200+ 'expected ' => "Parse error on line 1: \n{{#each a}OK!{{/each}} \n---------^ \nExpecting CLOSE, got INVALID " ,
201+ ],
202+ [
203+ 'template ' => '{{1 + 2}} ' ,
204+ 'expected ' => "Parse error on line 1: \n{{1 + 2}} \n----^ \nExpecting CLOSE, got INVALID " ,
205+ ],
206+ [
207+ 'template ' => '{{{{#foo}}} ' ,
208+ 'expected ' => "Parse error on line 1: \n{{{{#foo}}} \n----^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
209+ ],
210+ [
211+ 'template ' => '{{foo (foo (foo 1 2) 3))}} ' ,
212+ 'expected ' => "Parse error on line 1: \n...oo (foo (foo 1 2) 3))}} \n-----------------------^ \nExpecting CLOSE, got CLOSE_SEXPR " ,
213+ ],
214+ [
215+ 'template ' => '{{{{foo}}}} {{ {{{{#foo}}}} ' ,
216+ 'expected ' => "Lexical error on line 1. Unrecognized text. \n{{{{foo}}}} {{ {{{{#foo}}}} \n-------------------^ " ,
217+ ],
218+ [
219+ 'template ' => '{{else}} ' ,
220+ 'expected ' => "Parse error on line 1: \n{{else}} \n^ \nExpecting EOF, got INVERSE " ,
221+ ],
222+ [
223+ 'template ' => '{{#>foo}}bar ' ,
224+ 'expected ' => "Parse error on line 1: \n{{#>foo}}bar \n------------^ \nExpecting OPEN_ENDBLOCK, got EOF " ,
225+ ],
226+ [
227+ 'template ' => '{{ #2 }} ' ,
228+ 'expected ' => "Parse error on line 1: \n{{ #2 }} \n---^ \nExpecting BOOLEAN, DATA, ID, NULL, NUMBER, OPEN_SEXPR, STRING, UNDEFINED, got INVALID " ,
229+ ],
230+ ];
231+ }
232+
233+ #[DataProvider("errorProvider " )]
234+ public function testErrors (string $ template , string $ expected ): void
235+ {
236+ $ parser = (new ParserFactory ())->create ();
237+
238+ try {
239+ $ parser ->parse ($ template );
240+ $ this ->fail ("Expected to throw exception: {$ expected }" );
241+ } catch (\Exception $ e ) {
242+ $ this ->assertSame ($ expected , $ e ->getMessage ());
243+ }
244+ }
245+
34246 /**
35247 * @return \Generator<array{0: SpecArr}>
36248 */
0 commit comments