package lang import ( "testing" ) func TestParseComponentDefinitions(t *testing.T) { tests := []struct { name string input string want AST wantErr bool }{ { name: "basic component with entity", input: `page Test at "/test" layout main { component table for User }`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "Test", Path: "/test", Layout: "main", Elements: []PageElement{ { Component: &Component{ Type: "table", Entity: stringPtr("User"), }, }, }, }, }, }, }, }, { name: "form component with fields", input: `page Test at "/test" layout main { component form for User { field name type text label "Full Name" placeholder "Enter your name" required field email type email label "Email Address" required field bio type textarea rows 5 placeholder "Tell us about yourself" field avatar type file accept "image/*" field role type select options ["admin", "user", "guest"] default "user" } }`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "Test", Path: "/test", Layout: "main", Elements: []PageElement{ { Component: &Component{ Type: "form", Entity: stringPtr("User"), Elements: []ComponentElement{ { Field: &ComponentField{ Name: "name", Type: "text", Attributes: []ComponentFieldAttribute{ {Label: stringPtr("Full Name")}, {Placeholder: stringPtr("Enter your name")}, {Required: true}, }, }, }, { Field: &ComponentField{ Name: "email", Type: "email", Attributes: []ComponentFieldAttribute{ {Label: stringPtr("Email Address")}, {Required: true}, }, }, }, { Field: &ComponentField{ Name: "bio", Type: "textarea", Attributes: []ComponentFieldAttribute{ {Rows: intPtr(5)}, {Placeholder: stringPtr("Tell us about yourself")}, }, }, }, { Field: &ComponentField{ Name: "avatar", Type: "file", Attributes: []ComponentFieldAttribute{ {Accept: stringPtr("image/*")}, }, }, }, { Field: &ComponentField{ Name: "role", Type: "select", Attributes: []ComponentFieldAttribute{ {Options: []string{"admin", "user", "guest"}}, {Default: stringPtr("user")}, }, }, }, }, }, }, }, }, }, }, }, }, { name: "component with field attributes and validation", input: `page Test at "/test" layout main { component form for Product { field name type text required validate min_length "3" field price type number format "currency" validate min "0" field category type autocomplete relates to Category field tags type multiselect source "tags/popular" field description type richtext field featured type checkbox default "false" field thumbnail type image thumbnail } }`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "Test", Path: "/test", Layout: "main", Elements: []PageElement{ { Component: &Component{ Type: "form", Entity: stringPtr("Product"), Elements: []ComponentElement{ { Field: &ComponentField{ Name: "name", Type: "text", Attributes: []ComponentFieldAttribute{ {Required: true}, {Validation: &ComponentValidation{Type: "min_length", Value: stringPtr("3")}}, }, }, }, { Field: &ComponentField{ Name: "price", Type: "number", Attributes: []ComponentFieldAttribute{ {Format: stringPtr("currency")}, {Validation: &ComponentValidation{Type: "min", Value: stringPtr("0")}}, }, }, }, { Field: &ComponentField{ Name: "category", Type: "autocomplete", Attributes: []ComponentFieldAttribute{ {Relates: &FieldRelation{Type: "Category"}}, }, }, }, { Field: &ComponentField{ Name: "tags", Type: "multiselect", Attributes: []ComponentFieldAttribute{ {Source: stringPtr("tags/popular")}, }, }, }, { Field: &ComponentField{ Name: "description", Type: "richtext", }, }, { Field: &ComponentField{ Name: "featured", Type: "checkbox", Attributes: []ComponentFieldAttribute{ {Default: stringPtr("false")}, }, }, }, { Field: &ComponentField{ Name: "thumbnail", Type: "image", Attributes: []ComponentFieldAttribute{ {Thumbnail: true}, }, }, }, }, }, }, }, }, }, }, }, }, { name: "component with buttons", input: `page Test at "/test" layout main { component form for User { field name type text button save label "Save User" style "primary" icon "save" button cancel label "Cancel" style "secondary" button delete label "Delete" style "danger" confirm "Are you sure?" disabled when is_protected } }`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "Test", Path: "/test", Layout: "main", Elements: []PageElement{ { Component: &Component{ Type: "form", Entity: stringPtr("User"), Elements: []ComponentElement{ { Field: &ComponentField{ Name: "name", Type: "text", }, }, { Button: &ComponentButton{ Name: "save", Label: "Save User", Attributes: []ComponentButtonAttr{ {Style: &ComponentButtonStyle{Value: "primary"}}, {Icon: &ComponentButtonIcon{Value: "save"}}, }, }, }, { Button: &ComponentButton{ Name: "cancel", Label: "Cancel", Attributes: []ComponentButtonAttr{ {Style: &ComponentButtonStyle{Value: "secondary"}}, }, }, }, { Button: &ComponentButton{ Name: "delete", Label: "Delete", Attributes: []ComponentButtonAttr{ {Style: &ComponentButtonStyle{Value: "danger"}}, {Confirm: &ComponentButtonConfirm{Value: "Are you sure?"}}, {Disabled: &ComponentButtonDisabled{Value: "is_protected"}}, }, }, }, }, }, }, }, }, }, }, }, }, { name: "component with conditional fields", input: `page Test at "/test" layout main { component form for User { field account_type type select options ["personal", "business"] when account_type equals "business" { field company_name type text required field tax_id type text button verify_business label "Verify Business" } when account_type equals "personal" { field date_of_birth type date } } }`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "Test", Path: "/test", Layout: "main", Elements: []PageElement{ { Component: &Component{ Type: "form", Entity: stringPtr("User"), Elements: []ComponentElement{ { Field: &ComponentField{ Name: "account_type", Type: "select", Attributes: []ComponentFieldAttribute{ {Options: []string{"personal", "business"}}, }, }, }, { When: &WhenCondition{ Field: "account_type", Operator: "equals", Value: "business", Fields: []ComponentField{ { Name: "company_name", Type: "text", Attributes: []ComponentFieldAttribute{ {Required: true}, }, }, { Name: "tax_id", Type: "text", }, }, Buttons: []ComponentButton{ { Name: "verify_business", Label: "Verify Business", }, }, }, }, { When: &WhenCondition{ Field: "account_type", Operator: "equals", Value: "personal", Fields: []ComponentField{ { Name: "date_of_birth", Type: "date", }, }, }, }, }, }, }, }, }, }, }, }, }, { name: "component with nested sections", input: `page Test at "/test" layout main { component dashboard { section stats type container class "stats-grid" { component metric { field total_users type display value "1,234" field revenue type display format "currency" value "45,678" } } section charts type container { component chart for Analytics { data from "analytics/monthly" } } } }`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "Test", Path: "/test", Layout: "main", Elements: []PageElement{ { Component: &Component{ Type: "dashboard", Elements: []ComponentElement{ { Section: &Section{ Name: "stats", Type: stringPtr("container"), Class: stringPtr("stats-grid"), Elements: []SectionElement{ { Component: &Component{ Type: "metric", Elements: []ComponentElement{ { Field: &ComponentField{ Name: "total_users", Type: "display", Attributes: []ComponentFieldAttribute{ {Value: stringPtr("1,234")}, }, }, }, { Field: &ComponentField{ Name: "revenue", Type: "display", Attributes: []ComponentFieldAttribute{ {Format: stringPtr("currency")}, {Value: stringPtr("45,678")}, }, }, }, }, }, }, }, }, }, { Section: &Section{ Name: "charts", Type: stringPtr("container"), Elements: []SectionElement{ { Component: &Component{ Type: "chart", Entity: stringPtr("Analytics"), Elements: []ComponentElement{ { Attribute: &ComponentAttr{ DataSource: stringPtr("analytics/monthly"), }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseInput(tt.input) if (err != nil) != tt.wantErr { t.Errorf("ParseInput() error = %v, wantErr %v", err, tt.wantErr) return } if !astEqual(got, tt.want) { t.Errorf("ParseInput() got = %+v, want %+v", got, tt.want) } }) } } func TestParseComponentFieldTypes(t *testing.T) { fieldTypes := []string{ "text", "email", "password", "number", "date", "datetime", "time", "textarea", "richtext", "select", "multiselect", "checkbox", "radio", "file", "image", "autocomplete", "range", "color", "url", "tel", "hidden", "display", "json", "code", } for _, fieldType := range fieldTypes { t.Run("field_type_"+fieldType, func(t *testing.T) { input := `page Test at "/test" layout main { component form { field test_field type ` + fieldType + ` } }` got, err := ParseInput(input) if err != nil { t.Errorf("ParseInput() failed for field type %s: %v", fieldType, err) return } if len(got.Definitions) != 1 || got.Definitions[0].Page == nil { t.Errorf("ParseInput() failed to parse page for field type %s", fieldType) return } page := got.Definitions[0].Page if len(page.Elements) != 1 { t.Errorf("ParseInput() failed to parse component for field type %s", fieldType) return } element := page.Elements[0] if element.Component == nil || len(element.Component.Elements) != 1 { t.Errorf("ParseInput() failed to parse component for field type %s", fieldType) return } fieldElement := element.Component.Elements[0] if fieldElement.Field == nil || fieldElement.Field.Type != fieldType { t.Errorf("ParseInput() field type mismatch: got %v, want %s", fieldElement.Field, fieldType) } }) } } func TestParseComponentErrors(t *testing.T) { tests := []struct { name string input string }{ { name: "missing component type", input: `page Test at "/test" layout main { component }`, }, { name: "invalid field syntax", input: `page Test at "/test" layout main { component form { field name } }`, }, { name: "invalid button syntax", input: `page Test at "/test" layout main { component form { button } }`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := ParseInput(tt.input) if err == nil { t.Errorf("ParseInput() expected error for invalid syntax, got nil") } }) } }