tokenize()); $nodes = $parser->parse(); $this->assertCount(1, $nodes); $this->assertInstanceOf(TextNode::class, $nodes[0]); $this->assertEquals("Hello World", $nodes[0]->content); } public function testParsesInterpolation(): void { $lexer = new Lexer("{{ var }}"); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertCount(1, $nodes); $this->assertInstanceOf(VariableNode::class, $nodes[0]); $this->assertEquals("var", $nodes[0]->path); $this->assertFalse($nodes[0]->isRaw); } public function testParsesInterpolationWithFilters(): void { $template = "{{ price | currency('USD') | lower }}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(VariableNode::class, $nodes[0]); $this->assertEquals("price", $nodes[0]->path); $this->assertCount(2, $nodes[0]->filters); $this->assertEquals("currency", $nodes[0]->filters[0]['name']); $this->assertEquals(["'USD'"], $nodes[0]->filters[0]['args']); $this->assertEquals("lower", $nodes[0]->filters[1]['name']); } public function testParsesForeach(): void { $template = "{( foreach item in items )} {{ item }} {( endforeach )}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertCount(1, $nodes); $this->assertInstanceOf(ForeachNode::class, $nodes[0]); $this->assertEquals("items", $nodes[0]->collection); $this->assertEquals("item", $nodes[0]->valueVar); $this->assertNull($nodes[0]->keyVar); $this->assertCount(3, $nodes[0]->body); // Space, Variable, Space } public function testParsesForeachWithKey(): void { $template = "{( foreach k, v in items )}{( endforeach )}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(ForeachNode::class, $nodes[0]); $this->assertEquals("k", $nodes[0]->keyVar); $this->assertEquals("v", $nodes[0]->valueVar); } public function testParsesForeachWithPositionalTags(): void { $template = "{( foreach item in items )}{( first )}FIRST{( endfirst )}{( inner )}INNER{( endinner )}{( last )}LAST{( endlast )}{( endforeach )}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $node = $nodes[0]; $this->assertInstanceOf(ForeachNode::class, $node); $this->assertCount(1, $node->first); $this->assertInstanceOf(TextNode::class, $node->first[0]); $this->assertEquals("FIRST", $node->first[0]->content); $this->assertCount(1, $node->inner); $this->assertEquals("INNER", $node->inner[0]->content); $this->assertCount(1, $node->last); $this->assertEquals("LAST", $node->last[0]->content); } public function testParsesExtends(): void { $template = "{[ extends 'layout.main' ]}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(ExtendsNode::class, $nodes[0]); $this->assertEquals("layout.main", $nodes[0]->path); } public function testParsesBlock(): void { $template = "{[ block 'content' ]}Block Content{[ endblock ]}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(BlockNode::class, $nodes[0]); $this->assertEquals("content", $nodes[0]->name); $this->assertCount(1, $nodes[0]->body); $this->assertEquals("Block Content", $nodes[0]->body[0]->content); } public function testParsesInclude(): void { $template = "{[ include 'partials.nav' ]}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(IncludeNode::class, $nodes[0]); $this->assertEquals("partials.nav", $nodes[0]->path); $this->assertNull($nodes[0]->context); } public function testParsesIncludeWithContext(): void { $template = "{[ include 'partials.user' with user_data ]}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(IncludeNode::class, $nodes[0]); $this->assertEquals("user_data", $nodes[0]->context); } public function testParsesParent(): void { $template = "{[ parent ]}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(ParentNode::class, $nodes[0]); } public function testParsesUses(): void { $template = "{( uses filters:string )}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(FilterLoadNode::class, $nodes[0]); $this->assertTrue($nodes[0]->isInternal); $this->assertEquals("filters:string", $nodes[0]->path); } public function testParsesLoadFilter(): void { $template = "{( load_filter('custom.my_filter') )}"; $lexer = new Lexer($template); $parser = new Parser($lexer->tokenize()); $nodes = $parser->parse(); $this->assertInstanceOf(FilterLoadNode::class, $nodes[0]); $this->assertFalse($nodes[0]->isInternal); $this->assertEquals("custom.my_filter", $nodes[0]->path); } }