package parser
import (
"testing"
"github.com/a-h/parse"
"github.com/google/go-cmp/cmp"
)
func TestIfExpression(t *testing.T) {
tests := []struct {
name string
input string
expected IfExpression
}{
{
name: "if: simple expression",
input: `if p.Test {
{ "span content" }
}
`,
expected: IfExpression{
Expression: Expression{
Value: `p.Test`,
Range: Range{
From: Position{
Index: 3,
Line: 0,
Col: 3,
},
To: Position{
Index: 9,
Line: 0,
Col: 9,
},
},
},
Then: []Node{
Element{
Name: "span",
NameRange: Range{
From: Position{Index: 13, Line: 1, Col: 1},
To: Position{Index: 17, Line: 1, Col: 5},
},
Children: []Node{
Whitespace{Value: "\n "},
StringExpression{
Expression: Expression{
Value: `"span content"`,
Range: Range{
From: Position{
Index: 23,
Line: 2,
Col: 4,
},
To: Position{
Index: 37,
Line: 2,
Col: 18,
},
},
},
TrailingSpace: SpaceVertical,
},
},
IndentChildren: true,
TrailingSpace: SpaceVertical,
},
},
},
},
{
name: "if: else",
input: `if p.A {
{ "A" }
} else {
{ "B" }
}`,
expected: IfExpression{
Expression: Expression{
Value: `p.A`,
Range: Range{
From: Position{
Index: 3,
Line: 0,
Col: 3,
},
To: Position{
Index: 6,
Line: 0,
Col: 6,
},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"A"`,
Range: Range{
From: Position{
Index: 12,
Line: 1,
Col: 3,
},
To: Position{
Index: 15,
Line: 1,
Col: 6,
},
},
},
TrailingSpace: SpaceVertical,
},
},
Else: []Node{
StringExpression{
Expression: Expression{
Value: `"B"`,
Range: Range{
From: Position{
Index: 30,
Line: 3,
Col: 3,
},
To: Position{
Index: 33,
Line: 3,
Col: 6,
},
},
},
TrailingSpace: SpaceVertical,
},
},
},
},
{
name: "if: expressions can have a space after the opening brace",
input: `if p.Test {
text
}
`,
expected: IfExpression{
Expression: Expression{
Value: `p.Test`,
Range: Range{
From: Position{
Index: 3,
Line: 0,
Col: 3,
},
To: Position{
Index: 9,
Line: 0,
Col: 9,
},
},
},
Then: []Node{
Whitespace{Value: " "},
Text{
Value: "text",
Range: Range{
From: Position{Index: 15, Line: 1, Col: 2},
To: Position{Index: 19, Line: 1, Col: 6},
},
TrailingSpace: SpaceVertical,
},
},
},
},
{
name: "if: simple expression, without spaces",
input: `if p.Test {
{ "span content" }
}
`,
expected: IfExpression{
Expression: Expression{
Value: `p.Test`,
Range: Range{
From: Position{
Index: 3,
Line: 0,
Col: 3,
},
To: Position{
Index: 9,
Line: 0,
Col: 9,
},
},
},
Then: []Node{
Element{
Name: "span",
NameRange: Range{
From: Position{Index: 13, Line: 1, Col: 1},
To: Position{Index: 17, Line: 1, Col: 5},
},
Children: []Node{
Whitespace{Value: "\n "},
StringExpression{
Expression: Expression{
Value: `"span content"`,
Range: Range{
From: Position{
Index: 23,
Line: 2,
Col: 4,
},
To: Position{
Index: 37,
Line: 2,
Col: 18,
},
},
},
TrailingSpace: SpaceVertical,
},
},
IndentChildren: true,
TrailingSpace: SpaceVertical,
},
},
},
},
{
name: "if: else, without spaces",
input: `if p.A{
{ "A" }
} else {
{ "B" }
}`,
expected: IfExpression{
Expression: Expression{
Value: `p.A`,
Range: Range{
From: Position{
Index: 3,
Line: 0,
Col: 3,
},
To: Position{
Index: 6,
Line: 0,
Col: 6,
},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"A"`,
Range: Range{
From: Position{
Index: 11,
Line: 1,
Col: 3,
},
To: Position{
Index: 14,
Line: 1,
Col: 6,
},
},
},
TrailingSpace: SpaceVertical,
},
},
Else: []Node{
StringExpression{
Expression: Expression{
Value: `"B"`,
Range: Range{
From: Position{
Index: 29,
Line: 3,
Col: 3,
},
To: Position{
Index: 32,
Line: 3,
Col: 6,
},
},
},
TrailingSpace: SpaceVertical,
},
},
},
},
{
name: "if: nested",
input: `if p.A {
if p.B {
{ "B" }
}
}`,
expected: IfExpression{
Expression: Expression{
Value: `p.A`,
Range: Range{
From: Position{
Index: 3,
Line: 0,
Col: 3,
},
To: Position{
Index: 6,
Line: 0,
Col: 6,
},
},
},
Then: []Node{
Whitespace{Value: "\t\t\t\t\t"},
IfExpression{
Expression: Expression{
Value: `p.B`,
Range: Range{
From: Position{
Index: 17,
Line: 1,
Col: 8,
},
To: Position{
Index: 20,
Line: 1,
Col: 11,
},
},
},
Then: []Node{
Whitespace{Value: "\t\t\t\t\t\t"},
Element{
Name: "div",
NameRange: Range{
From: Position{Index: 30, Line: 2, Col: 7},
To: Position{Index: 33, Line: 2, Col: 10},
},
Children: []Node{
StringExpression{
Expression: Expression{
Value: `"B"`,
Range: Range{
From: Position{
Index: 36,
Line: 2,
Col: 13,
},
To: Position{
Index: 39,
Line: 2,
Col: 16,
},
},
},
},
},
TrailingSpace: SpaceVertical,
},
},
},
Whitespace{Value: "\n\t\t\t\t"},
},
},
},
{
name: "if: else if",
input: `if p.A {
{ "A" }
} else if p.B {
{ "B" }
}`,
expected: IfExpression{
Expression: Expression{
Value: `p.A`,
Range: Range{
From: Position{Index: 3, Line: 0, Col: 3},
To: Position{Index: 6, Line: 0, Col: 6},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"A"`,
Range: Range{
From: Position{Index: 12, Line: 1, Col: 3},
To: Position{Index: 15, Line: 1, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
ElseIfs: []ElseIfExpression{
{
Expression: Expression{
Value: `p.B`,
Range: Range{
From: Position{Index: 28, Line: 2, Col: 10},
To: Position{Index: 31, Line: 2, Col: 13},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"B"`,
Range: Range{
From: Position{Index: 37, Line: 3, Col: 3},
To: Position{Index: 40, Line: 3, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
},
},
},
},
{
name: "if: else if, else if",
input: `if p.A {
{ "A" }
} else if p.B {
{ "B" }
} else if p.C {
{ "C" }
}`,
expected: IfExpression{
Expression: Expression{
Value: `p.A`,
Range: Range{
From: Position{Index: 3, Line: 0, Col: 3},
To: Position{Index: 6, Line: 0, Col: 6},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"A"`,
Range: Range{
From: Position{Index: 12, Line: 1, Col: 3},
To: Position{Index: 15, Line: 1, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
ElseIfs: []ElseIfExpression{
{
Expression: Expression{
Value: `p.B`,
Range: Range{
From: Position{Index: 28, Line: 2, Col: 10},
To: Position{Index: 31, Line: 2, Col: 13},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"B"`,
Range: Range{
From: Position{Index: 37, Line: 3, Col: 3},
To: Position{Index: 40, Line: 3, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
},
{
Expression: Expression{
Value: `p.C`,
Range: Range{
From: Position{Index: 53, Line: 4, Col: 10},
To: Position{Index: 56, Line: 4, Col: 13},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"C"`,
Range: Range{
From: Position{Index: 62, Line: 5, Col: 3},
To: Position{Index: 65, Line: 5, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
},
},
},
},
{
name: "if: else if, else if, else",
input: `if p.A {
{ "A" }
} else if p.B {
{ "B" }
} else if p.C {
{ "C" }
} else {
{ "D" }
}`,
expected: IfExpression{
Expression: Expression{
Value: `p.A`,
Range: Range{
From: Position{Index: 3, Line: 0, Col: 3},
To: Position{Index: 6, Line: 0, Col: 6},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"A"`,
Range: Range{
From: Position{Index: 12, Line: 1, Col: 3},
To: Position{Index: 15, Line: 1, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
ElseIfs: []ElseIfExpression{
{
Expression: Expression{
Value: `p.B`,
Range: Range{
From: Position{Index: 28, Line: 2, Col: 10},
To: Position{Index: 31, Line: 2, Col: 13},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"B"`,
Range: Range{
From: Position{Index: 37, Line: 3, Col: 3},
To: Position{Index: 40, Line: 3, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
},
{
Expression: Expression{
Value: `p.C`,
Range: Range{
From: Position{Index: 53, Line: 4, Col: 10},
To: Position{Index: 56, Line: 4, Col: 13},
},
},
Then: []Node{
Whitespace{Value: "\t"},
StringExpression{
Expression: Expression{
Value: `"C"`,
Range: Range{
From: Position{Index: 62, Line: 5, Col: 3},
To: Position{Index: 65, Line: 5, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
},
},
Else: []Node{
StringExpression{
Expression: Expression{
Value: `"D"`,
Range: Range{
From: Position{Index: 80, Line: 7, Col: 3},
To: Position{Index: 83, Line: 7, Col: 6},
},
},
TrailingSpace: SpaceVertical,
},
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
input := parse.NewInput(tt.input)
actual, ok, err := ifExpression.Parse(input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !ok {
t.Fatalf("unexpected failure for input %q", tt.input)
}
if diff := cmp.Diff(tt.expected, actual); diff != "" {
t.Error(diff)
}
})
}
}
func TestIncompleteIf(t *testing.T) {
t.Run("no opening brace", func(t *testing.T) {
input := parse.NewInput(`if a tree falls in the woods`)
_, _, err := ifExpression.Parse(input)
if err == nil {
t.Fatal("expected an error, got nil")
}
pe, isParseError := err.(parse.ParseError)
if !isParseError {
t.Fatalf("expected a parse error, got %T", err)
}
if pe.Msg != "if: unterminated (missing closing '{\\n') - https://templ.guide/syntax-and-usage/statements#incomplete-statements" {
t.Fatalf("unexpected error: %v", err)
}
if pe.Pos.Line != 0 {
t.Fatalf("unexpected line: %d", pe.Pos.Line)
}
})
t.Run("capitalised If", func(t *testing.T) {
input := parse.NewInput(`If a tree falls in the woods`)
_, ok, err := ifExpression.Parse(input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if ok {
t.Fatal("expected a non match")
}
})
}