//! General template tests for Pugz engine const helper = @import("helper.zig"); const expectOutput = helper.expectOutput; // ───────────────────────────────────────────────────────────────────────────── // Test Case 1: Simple interpolation // ───────────────────────────────────────────────────────────────────────────── test "Simple interpolation" { try expectOutput( "p #{name}'s Pug source code!", .{ .name = "ankit patial" }, "

ankit patial's Pug source code!

", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 2: Attributes with inline text // ───────────────────────────────────────────────────────────────────────────── test "Link with href attribute" { try expectOutput( "a(href='//google.com') Google", .{}, "Google", ); } test "Link with class and href (space separated)" { try expectOutput( "a(class='button' href='//google.com') Google", .{}, "Google", ); } test "Link with class and href (comma separated)" { try expectOutput( "a(class='button', href='//google.com') Google", .{}, "Google", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 3: Boolean attributes (multiline) // ───────────────────────────────────────────────────────────────────────────── test "Checkbox with boolean checked attribute" { try expectOutput( \\input( \\ type='checkbox' \\ name='agreement' \\ checked \\) , .{}, "", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 4: Backtick template literal with multiline JSON // ───────────────────────────────────────────────────────────────────────────── test "Input with multiline JSON data attribute" { try expectOutput( \\input(data-json=` \\ { \\ "very-long": "piece of ", \\ "data": true \\ } \\`) , .{}, \\ , ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 5: Escaped vs unescaped attribute values // ───────────────────────────────────────────────────────────────────────────── test "Escaped attribute value" { try expectOutput( "div(escaped=\"\")", .{}, "
", ); } test "Unescaped attribute value" { try expectOutput( "div(unescaped!=\"\")", .{}, "
\">
", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 6: Boolean attributes with true/false values // ───────────────────────────────────────────────────────────────────────────── test "Checkbox with checked (no value)" { try expectOutput( "input(type='checkbox' checked)", .{}, "", ); } test "Checkbox with checked=true" { try expectOutput( "input(type='checkbox' checked=true)", .{}, "", ); } test "Checkbox with checked=false (omitted)" { try expectOutput( "input(type='checkbox' checked=false)", .{}, "", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 7: Object literal as style attribute // ───────────────────────────────────────────────────────────────────────────── test "Style object literal" { try expectOutput( "a(style={color: 'red', background: 'green'})", .{}, "", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 8: Array literals for class attribute // ───────────────────────────────────────────────────────────────────────────── test "Class array literal" { try expectOutput("a(class=['foo', 'bar', 'baz'])", .{}, ""); } test "Class array merged with shorthand and array" { try expectOutput( "a.bang(class=['foo', 'bar', 'baz'] class=['bing'])", .{}, "", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 9: Shorthand class syntax // ───────────────────────────────────────────────────────────────────────────── test "Shorthand class on anchor" { try expectOutput("a.button", .{}, ""); } test "Implicit div with class" { try expectOutput(".content", .{}, "
"); } test "Shorthand ID on anchor" { try expectOutput("a#main-link", .{}, ""); } test "Implicit div with ID" { try expectOutput("#content", .{}, "
"); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 10: &attributes spread operator // ───────────────────────────────────────────────────────────────────────────── test "Attributes spread with &attributes" { try expectOutput( "div#foo(data-bar=\"foo\")&attributes({'data-foo': 'bar'})", .{}, "
", ); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 11: case/when/default // ───────────────────────────────────────────────────────────────────────────── test "Case statement with friends=1" { try expectOutput( \\case friends \\ when 0 \\ p you have no friends \\ when 1 \\ p you have a friend \\ default \\ p you have #{friends} friends , .{ .friends = @as(i64, 1) }, "

you have a friend

"); } test "Case statement with friends=10" { try expectOutput( \\case friends \\ when 0 \\ p you have no friends \\ when 1 \\ p you have a friend \\ default \\ p you have #{friends} friends , .{ .friends = @as(i64, 10) }, "

you have 10 friends

"); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 12: Conditionals (if/else if/else) // ───────────────────────────────────────────────────────────────────────────── test "If condition true" { try expectOutput( \\if showMessage \\ p Hello! , .{ .showMessage = true }, "

Hello!

"); } test "If condition false (no data)" { try expectOutput( \\if showMessage \\ p Hello! , .{}, ""); } test "If condition false with else" { try expectOutput( \\if showMessage \\ p Hello! \\else \\ p Goodbye! , .{ .showMessage = false }, "

Goodbye!

"); } test "Unless condition (negated if)" { try expectOutput( \\unless isHidden \\ p Visible content , .{ .isHidden = false }, "

Visible content

"); } // ───────────────────────────────────────────────────────────────────────────── // Test Case 13: Nested conditionals with dot notation // ───────────────────────────────────────────────────────────────────────────── test "Condition with nested user.description" { try expectOutput( \\#user \\ if user.description \\ h2.green Description \\ p.description= user.description \\ else if authorised \\ h2.blue Description \\ p.description No description (authorised) \\ else \\ h2.red Description \\ p.description User has no description , .{ .user = .{ .description = "foo bar baz" }, .authorised = false }, \\
\\

Description

\\

foo bar baz

\\
); } test "Condition with nested user.description and autorized" { try expectOutput( \\#user \\ if user.description \\ h2.green Description \\ p.description= user.description \\ else if authorised \\ h2.blue Description \\ p.description No description (authorised) \\ else \\ h2.red Description \\ p.description User has no description , .{ .authorised = true }, \\
\\

Description

\\

No description (authorised)

\\
); } test "Condition with nested user.description and no data" { try expectOutput( \\#user \\ if user.description \\ h2.green Description \\ p.description= user.description \\ else if authorised \\ h2.blue Description \\ p.description No description (authorised) \\ else \\ h2.red Description \\ p.description User has no description , .{}, \\
\\

Description

\\

User has no description

\\
); } // ───────────────────────────────────────────────────────────────────────────── // Tag Interpolation Tests // ───────────────────────────────────────────────────────────────────────────── test "Simple tag interpolation" { try expectOutput( "p This is #[em emphasized] text.", .{}, "

This is emphasized text.

", ); } test "Tag interpolation with strong" { try expectOutput( "p This is #[strong important] text.", .{}, "

This is important text.

", ); } test "Tag interpolation with link" { try expectOutput( "p Click #[a(href='/') here] to continue.", .{}, "

Click here to continue.

", ); } test "Tag interpolation with class" { try expectOutput( "p This is #[span.highlight highlighted] text.", .{}, "

This is highlighted text.

", ); } test "Tag interpolation with id" { try expectOutput( "p See #[span#note this note] for details.", .{}, "

See this note for details.

", ); } test "Tag interpolation with class and id" { try expectOutput( "p Check #[span#info.tooltip the tooltip] here.", .{}, "

Check the tooltip here.

", ); } test "Multiple tag interpolations" { try expectOutput( "p This has #[em emphasis] and #[strong strength].", .{}, "

This has emphasis and strength.

", ); } test "Tag interpolation with multiple classes" { try expectOutput( "p Text with #[span.red.bold styled content] here.", .{}, "

Text with styled content here.

", ); } // ───────────────────────────────────────────────────────────────────────────── // Iteration Tests // ───────────────────────────────────────────────────────────────────────────── test "each loop with array" { try expectOutput( \\ul \\ each item in items \\ li= item , .{ .items = &[_][]const u8{ "apple", "banana", "cherry" } }, \\ ); } test "for loop as alias for each" { try expectOutput( \\ul \\ for item in items \\ li= item , .{ .items = &[_][]const u8{ "one", "two", "three" } }, \\ ); } test "each loop with index" { try expectOutput( \\ul \\ each item, idx in items \\ li #{idx}: #{item} , .{ .items = &[_][]const u8{ "a", "b", "c" } }, \\ ); } test "each loop with else block" { try expectOutput( \\ul \\ each item in items \\ li= item \\ else \\ li No items found , .{ .items = &[_][]const u8{} }, \\ ); } // ───────────────────────────────────────────────────────────────────────────── // Mixin Tests // ───────────────────────────────────────────────────────────────────────────── test "Basic mixin declaration and call" { try expectOutput( \\mixin list \\ ul \\ li foo \\ li bar \\+list , .{}, \\ ); } test "Mixin with arguments" { try expectOutput( \\mixin pet(name) \\ li.pet= name \\ul \\ +pet('cat') \\ +pet('dog') , .{}, \\ ); } test "Mixin with default argument" { try expectOutput( \\mixin greet(name='World') \\ p Hello, #{name}! \\+greet \\+greet('Zig') , .{}, \\

Hello, World!

\\

Hello, Zig!

); } test "Mixin with block content" { try expectOutput( \\mixin article(title) \\ .article \\ h1= title \\ block \\+article('Hello') \\ p This is content \\ p More content , .{}, \\
\\

Hello

\\

This is content

\\

More content

\\
); } test "Mixin with block and no content passed" { try expectOutput( \\mixin box \\ .box \\ block \\+box , .{}, \\
\\
); } test "Mixin with attributes" { try expectOutput( \\mixin link(href, name) \\ a(href=href)&attributes(attributes)= name \\+link('/foo', 'foo')(class="btn") , .{}, \\foo ); } test "Mixin with rest arguments" { try expectOutput( \\mixin list(id, ...items) \\ ul(id=id) \\ each item in items \\ li= item \\+list('my-list', 'one', 'two', 'three') , .{}, \\ ); } test "Mixin with rest arguments empty" { try expectOutput( \\mixin list(id, ...items) \\ ul(id=id) \\ each item in items \\ li= item \\+list('my-list') , .{}, \\ ); } // ───────────────────────────────────────────────────────────────────────────── // Plain Text Tests // ───────────────────────────────────────────────────────────────────────────── test "Inline text in tag" { try expectOutput( \\p This is plain old text content. , .{}, \\

This is plain old text content.

); } test "Piped text basic" { try expectOutput( \\p \\ | The pipe always goes at the beginning of its own line, \\ | not counting indentation. , .{}, \\

\\ The pipe always goes at the beginning of its own line, \\ not counting indentation. \\

); } // test "Piped text with inline tags" { // try expectOutput( // \\| You put the em // \\em pha // \\| sis on the wrong syl // \\em la // \\| ble. // , .{}, // \\You put the em // \\phasis on the wrong syl // \\lable. // ); // } test "Block text with dot" { try expectOutput( \\script. \\ if (usingPug) \\ console.log('you are awesome') , .{}, \\ ); } test "Block text with dot and attributes" { try expectOutput( \\style(type='text/css'). \\ body { \\ color: red; \\ } , .{}, \\ ); } test "Literal HTML passthrough" { try expectOutput( \\ \\p Hello from Pug \\ , .{}, \\ \\

Hello from Pug

\\ ); } test "Literal HTML mixed with Pug" { try expectOutput( \\div \\ Literal HTML \\ p Pug paragraph , .{}, \\
\\Literal HTML \\

Pug paragraph

\\
); } // ───────────────────────────────────────────────────────────────────────────── // Tag Tests // ───────────────────────────────────────────────────────────────────────────── test "Nested tags with indentation" { try expectOutput( \\ul \\ li Item A \\ li Item B \\ li Item C , .{}, \\ ); } test "Self-closing void elements" { try expectOutput( \\img \\br \\input , .{}, \\ \\
\\ ); } test "Block expansion with colon" { try expectOutput( \\a: img , .{}, \\ \\ \\ ); } test "Block expansion nested" { try expectOutput( \\ul \\ li: a(href='/') Home \\ li: a(href='/about') About , .{}, \\ ); } test "Explicit self-closing tag" { try expectOutput( \\foo/ , .{}, \\ ); } test "Explicit self-closing tag with attributes" { try expectOutput( \\foo(bar='baz')/ , .{}, \\ ); }