diff --git a/src/renderer_test.zig b/src/renderer_test.zig
index 977ac46..74a1b38 100644
--- a/src/renderer_test.zig
+++ b/src/renderer_test.zig
@@ -5,16 +5,20 @@ const Renderer = @import("renderer.zig").Renderer;
const RenderError = @import("renderer.zig").RenderError;
const Context = @import("context.zig").Context;
-const Value = @import("context.zig").Value; // ajuste o caminho se estiver diferente
+const Value = @import("context.zig").Value;
+const TemplateCache = @import("cache.zig").TemplateCache;
test "renderer: literal + variável simples" {
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
- try ctx.set("nome", Value{ .string = "Mariana" });
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
- const renderer = Renderer.init(&ctx);
+ const renderer = Renderer.init(&ctx, &cache);
+
+ try ctx.set("nome", Value{ .string = "Mariana" });
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
@@ -31,14 +35,16 @@ test "renderer: literal + variável simples" {
test "renderer: filtros + autoescape" {
const alloc = testing.allocator;
var ctx = Context.init(alloc);
-
defer ctx.deinit();
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
+
try ctx.set("html", Value{ .string = "" });
try ctx.set("texto", Value{ .string = "maiusculo e slug" });
- const renderer = Renderer.init(&ctx);
-
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
@@ -62,10 +68,12 @@ test "renderer: filtros + autoescape" {
test "literal simples" {
const alloc = testing.allocator;
var ctx = Context.init(alloc);
-
defer ctx.deinit();
- const renderer = Renderer.init(&ctx);
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
@@ -80,14 +88,15 @@ test "literal simples" {
test "variável com filtro encadeado e autoescape" {
const alloc = testing.allocator;
var ctx = Context.init(alloc);
-
defer ctx.deinit();
- const renderer = Renderer.init(&ctx);
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
try ctx.set("texto", Value{ .string = "Exemplo de Texto" });
-
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
@@ -95,20 +104,21 @@ test "variável com filtro encadeado e autoescape" {
try renderer.renderString(template, buf.writer(alloc));
- try testing.expectEqualStrings("Resultado: EXEMPLO DE TEXTO", buf.items); // assume lower then upper
+ try testing.expectEqualStrings("Resultado: EXEMPLO DE TEXTO", buf.items); // assume lower then upper
}
test "autoescape com safe" {
const alloc = testing.allocator;
var ctx = Context.init(alloc);
-
defer ctx.deinit();
- const renderer = Renderer.init(&ctx);
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
try ctx.set("html", Value{ .string = "
conteúdo
" });
-
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
@@ -118,3 +128,316 @@ test "autoescape com safe" {
try testing.expectEqualStrings("Escape: <div>conteúdo</div> | Safe: conteúdo
", buf.items);
}
+
+test "renderer - if and for" {
+ const alloc = testing.allocator;
+ var ctx = Context.init(alloc);
+ defer ctx.deinit();
+
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
+
+ try ctx.set("ativo", Value{ .bool = true });
+ try ctx.set("nomes", Value{ .list = &[_]Value{
+ Value{ .string = "Ana" },
+ Value{ .string = "Bia" },
+ Value{ .string = "Cris" },
+ } });
+
+ const template =
+ \\{% if ativo %}Sim!{% endif %}
+ \\{% if not ativo %}Não{% endif %}
+ \\Lista:
+ \\{% for nome in nomes %}
+ \\- {{ nome }}
+ \\{% endfor %}
+ ;
+
+ var buf = std.ArrayList(u8){};
+ defer buf.deinit(alloc);
+
+ try renderer.renderString(template, buf.writer(alloc));
+
+ try testing.expect(std.mem.indexOf(u8, buf.items, "Sim!") != null);
+ try testing.expect(std.mem.indexOf(u8, buf.items, "Não") == null);
+ try testing.expect(std.mem.indexOf(u8, buf.items, "- Ana") != null);
+ try testing.expect(std.mem.indexOf(u8, buf.items, "- Bia") != null);
+ try testing.expect(std.mem.indexOf(u8, buf.items, "- Cris") != null);
+}
+
+test "renderer - block and extends" {
+ const alloc = testing.allocator;
+ var ctx = Context.init(alloc);
+ defer ctx.deinit();
+
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
+
+ const base =
+ \\
+ \\{% block title %}Título Padrão{% endblock %}
+ \\
+ \\{% block content %}Conteúdo padrão{% endblock %}
+ \\
+ \\
+ ;
+
+ const child =
+ \\{% extends "base.html" %}
+ \\{% block title %}Meu Título{% endblock %}
+ \\{% block content %}
+ \\Olá {{ nome }}!
+ \\{% endblock %}
+ ;
+
+ const expected =
+ \\
+ \\Meu Título
+ \\
+ \\
+ \\Olá Lucas!
+ \\
+ \\
+ \\
+ ;
+
+ // Simula arquivos (ou usa renderString com base se quiser simplificar)
+ try std.fs.cwd().writeFile(.{ .sub_path = "base.html", .data = base });
+ try std.fs.cwd().writeFile(.{ .sub_path = "child.html", .data = child });
+ defer std.fs.cwd().deleteFile("base.html") catch {};
+ defer std.fs.cwd().deleteFile("child.html") catch {};
+
+ try ctx.set("nome", Value{ .string = "Lucas" });
+
+ var buf = std.ArrayList(u8){};
+ defer buf.deinit(alloc);
+
+ try renderer.render("child.html", buf.writer(alloc));
+
+ const output = buf.items;
+
+ std.debug.print("OUTPUT:\n{s}\n", .{output});
+
+ try testing.expect(std.mem.indexOf(u8, output, "") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Meu Título") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Olá Lucas!") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Conteúdo padrão") == null);
+ try testing.expectEqualStrings(expected, output);
+}
+
+test "renderer - block and extends with super" {
+ const alloc = testing.allocator;
+ var ctx = Context.init(alloc);
+ defer ctx.deinit();
+
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
+
+ const base =
+ \\
+ \\{% block title %}Título Padrão{% endblock %}
+ \\
+ \\{% block content %}Conteúdo padrão{% endblock %}
+ \\
+ \\
+ ;
+
+ const child =
+ \\{% extends "base.html" %}
+ \\{% block title %}Meu Título{% endblock %}
+ \\{% block content %}
+ \\{{ block.super }}
+ \\Olá {{ nome }}!
+ \\{% endblock %}
+ ;
+
+ const expected =
+ \\
+ \\Meu Título
+ \\
+ \\
+ \\Conteúdo padrão
+ \\Olá Lucas!
+ \\
+ \\
+ \\
+ ;
+
+ // Simula arquivos (ou usa renderString com base se quiser simplificar)
+ try std.fs.cwd().writeFile(.{ .sub_path = "base.html", .data = base });
+ try std.fs.cwd().writeFile(.{ .sub_path = "child.html", .data = child });
+ defer std.fs.cwd().deleteFile("base.html") catch {};
+ defer std.fs.cwd().deleteFile("child.html") catch {};
+
+ try ctx.set("nome", Value{ .string = "Lucas" });
+
+ var buf = std.ArrayList(u8){};
+ defer buf.deinit(alloc);
+
+ try renderer.render("child.html", buf.writer(alloc));
+
+ const output = buf.items;
+
+ std.debug.print("{s}\n", .{output});
+
+ try testing.expect(std.mem.indexOf(u8, output, "") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Meu Título") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Olá Lucas!") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Conteúdo padrão") != null);
+ try testing.expectEqualStrings(expected, output);
+}
+
+test "renderer - include" {
+ const alloc = testing.allocator;
+
+ const header =
+ \\
+ ;
+
+ const main =
+ \\{% include "header.html" %}
+ \\
+ \\ Conteúdo principal
+ \\ Olá {{ nome }}!
+ \\
+ ;
+
+ const expected =
+ \\
+ \\
+ \\ Conteúdo principal
+ \\ Olá Lucas!
+ \\
+ ;
+
+ try std.fs.cwd().writeFile(.{ .sub_path = "header.html", .data = header });
+ try std.fs.cwd().writeFile(.{ .sub_path = "main.html", .data = main });
+ defer std.fs.cwd().deleteFile("header.html") catch {};
+ defer std.fs.cwd().deleteFile("main.html") catch {};
+
+ var ctx = Context.init(alloc);
+ defer ctx.deinit();
+
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
+
+ try ctx.set("nome", Value{ .string = "Lucas" });
+
+ var buf = std.ArrayList(u8){};
+ defer buf.deinit(alloc);
+
+ try renderer.render("main.html", buf.writer(alloc));
+
+ const output = buf.items;
+
+ try testing.expect(std.mem.indexOf(u8, output, "Bem-vindo
") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Olá Lucas!") != null);
+ try testing.expectEqualStrings(expected, output);
+}
+
+test "renderer - comment" {
+ const alloc = testing.allocator;
+
+ const template =
+ \\Normal: Olá {{ nome }}
+ \\{% comment %}
+ \\ Isso é um comentário
+ \\ que não deve aparecer
+ \\ nem processar variáveis {{ nome }}
+ \\{% endcomment %}
+ \\Fim: {{ nome }}
+ ;
+ const expected =
+ \\Normal: Olá Lucas
+ \\
+ \\Fim: Lucas
+ ;
+
+ var ctx = Context.init(alloc);
+ defer ctx.deinit();
+
+ var cache = TemplateCache.init(alloc);
+ defer cache.deinit();
+
+ const renderer = Renderer.init(&ctx, &cache);
+
+ try ctx.set("nome", Value{ .string = "Lucas" });
+
+ var buf = std.ArrayList(u8){};
+ defer buf.deinit(alloc);
+
+ try renderer.renderString(template, buf.writer(alloc));
+
+ const output = buf.items;
+
+ try testing.expect(std.mem.indexOf(u8, output, "Olá Lucas") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Fim: Lucas") != null);
+ try testing.expect(std.mem.indexOf(u8, output, "Isso é um comentário") == null);
+ try testing.expect(std.mem.indexOf(u8, output, "que não deve aparecer") == null);
+ try testing.expect(std.mem.indexOf(u8, output, "nem processar variáveis") == null);
+ try testing.expectEqualStrings(expected, output);
+}
+
+// FIX: comment inside block
+//
+// test "renderer - full template with extends, super, include, comment" {
+// const alloc = testing.allocator;
+//
+// const header = "";
+// const base =
+// \\{% include "header.html" %}
+// \\
+// \\ {% block content %}
+// \\ Conteúdo padrão
+// \\ {% endblock %}
+// \\
+// ;
+//
+// const child =
+// \\{% extends "base.html" %}
+// \\{% block content %}
+// \\ {{ block.super }}
+// \\ Conteúdo do filho
+// \\ {% comment %} Isso não aparece {% endcomment %}
+// \\{% endblock %}
+// ;
+//
+// try std.fs.cwd().writeFile(.{ .sub_path = "header.html", .data = header });
+// try std.fs.cwd().writeFile(.{ .sub_path = "base.html", .data = base });
+// try std.fs.cwd().writeFile(.{ .sub_path = "child.html", .data = child });
+// defer std.fs.cwd().deleteFile("header.html") catch {};
+// defer std.fs.cwd().deleteFile("base.html") catch {};
+// defer std.fs.cwd().deleteFile("child.html") catch {};
+//
+// var ctx = Context.init(alloc);
+// defer ctx.deinit();
+//
+// var cache = TemplateCache.init(alloc);
+// defer cache.deinit();
+//
+// const renderer = Renderer.init(&ctx, &cache);
+//
+// var buf = std.ArrayList(u8){};
+// defer buf.deinit(alloc);
+//
+// try renderer.render("child.html", buf.writer(alloc));
+//
+// const output = buf.items;
+//
+// try testing.expect(std.mem.indexOf(u8, output, "") != null);
+// try testing.expect(std.mem.indexOf(u8, output, "Conteúdo padrão") != null);
+// try testing.expect(std.mem.indexOf(u8, output, "Conteúdo do filho") != null);
+// try testing.expect(std.mem.indexOf(u8, output, "Isso não aparece") == null);
+// }