301 lines
6.8 KiB
Go
301 lines
6.8 KiB
Go
package lang
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestParsePageDefinitions(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want AST
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "basic page with minimal fields",
|
|
input: `page Dashboard at "/dashboard" layout main`,
|
|
want: AST{
|
|
Definitions: []Definition{
|
|
{
|
|
Page: &Page{
|
|
Name: "Dashboard",
|
|
Path: "/dashboard",
|
|
Layout: "main",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "page with all optional fields",
|
|
input: `page UserProfile at "/profile" layout main title "User Profile" desc "Manage user profile settings" auth`,
|
|
want: AST{
|
|
Definitions: []Definition{
|
|
{
|
|
Page: &Page{
|
|
Name: "UserProfile",
|
|
Path: "/profile",
|
|
Layout: "main",
|
|
Title: stringPtr("User Profile"),
|
|
Description: stringPtr("Manage user profile settings"),
|
|
Auth: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "page with meta tags",
|
|
input: `page HomePage at "/" layout main {
|
|
meta description "Welcome to our application"
|
|
meta keywords "app, dashboard, management"
|
|
}`,
|
|
want: AST{
|
|
Definitions: []Definition{
|
|
{
|
|
Page: &Page{
|
|
Name: "HomePage",
|
|
Path: "/",
|
|
Layout: "main",
|
|
Meta: []MetaTag{
|
|
{Name: "description", Content: "Welcome to our application"},
|
|
{Name: "keywords", Content: "app, dashboard, management"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "page with nested sections",
|
|
input: `page Settings at "/settings" layout main {
|
|
section tabs type tab {
|
|
section profile label "Profile" active {
|
|
component form for User {
|
|
field name type text
|
|
}
|
|
}
|
|
|
|
section security label "Security" {
|
|
component form for User {
|
|
field password type password
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
want: AST{
|
|
Definitions: []Definition{
|
|
{
|
|
Page: &Page{
|
|
Name: "Settings",
|
|
Path: "/settings",
|
|
Layout: "main",
|
|
Sections: []Section{
|
|
{
|
|
Name: "tabs",
|
|
Type: stringPtr("tab"),
|
|
Elements: []SectionElement{
|
|
{
|
|
Section: &Section{
|
|
Name: "profile",
|
|
Label: stringPtr("Profile"),
|
|
Active: true,
|
|
Elements: []SectionElement{
|
|
{
|
|
Component: &Component{
|
|
Type: "form",
|
|
Entity: stringPtr("User"),
|
|
Elements: []ComponentElement{
|
|
{
|
|
Field: &ComponentField{
|
|
Name: "name",
|
|
Type: "text",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Section: &Section{
|
|
Name: "security",
|
|
Label: stringPtr("Security"),
|
|
Elements: []SectionElement{
|
|
{
|
|
Component: &Component{
|
|
Type: "form",
|
|
Entity: stringPtr("User"),
|
|
Elements: []ComponentElement{
|
|
{
|
|
Field: &ComponentField{
|
|
Name: "password",
|
|
Type: "password",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "page with modal and panel sections",
|
|
input: `page ProductList at "/products" layout main {
|
|
section main type container {
|
|
component table for Product
|
|
}
|
|
|
|
section editModal type modal trigger "edit-product" {
|
|
component form for Product {
|
|
field name type text required
|
|
button save label "Save Changes" style "primary"
|
|
}
|
|
}
|
|
|
|
section filters type panel position "left" {
|
|
component form {
|
|
field category type select
|
|
field price_range type range
|
|
}
|
|
}
|
|
}`,
|
|
want: AST{
|
|
Definitions: []Definition{
|
|
{
|
|
Page: &Page{
|
|
Name: "ProductList",
|
|
Path: "/products",
|
|
Layout: "main",
|
|
Sections: []Section{
|
|
{
|
|
Name: "main",
|
|
Type: stringPtr("container"),
|
|
Elements: []SectionElement{
|
|
{
|
|
Component: &Component{
|
|
Type: "table",
|
|
Entity: stringPtr("Product"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "editModal",
|
|
Type: stringPtr("modal"),
|
|
Trigger: stringPtr("edit-product"),
|
|
Elements: []SectionElement{
|
|
{
|
|
Component: &Component{
|
|
Type: "form",
|
|
Entity: stringPtr("Product"),
|
|
Elements: []ComponentElement{
|
|
{
|
|
Field: &ComponentField{
|
|
Name: "name",
|
|
Type: "text",
|
|
Attributes: []ComponentFieldAttribute{
|
|
{Required: true},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Button: &ComponentButton{
|
|
Name: "save",
|
|
Label: "Save Changes",
|
|
Attributes: []ComponentButtonAttr{
|
|
{Style: &ComponentButtonStyle{Value: "primary"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "filters",
|
|
Type: stringPtr("panel"),
|
|
Position: stringPtr("left"),
|
|
Elements: []SectionElement{
|
|
{
|
|
Component: &Component{
|
|
Type: "form",
|
|
Elements: []ComponentElement{
|
|
{
|
|
Field: &ComponentField{
|
|
Name: "category",
|
|
Type: "select",
|
|
},
|
|
},
|
|
{
|
|
Field: &ComponentField{
|
|
Name: "price_range",
|
|
Type: "range",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
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 TestParsePageErrors(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
}{
|
|
{
|
|
name: "missing layout",
|
|
input: `page Dashboard at "/dashboard"`,
|
|
},
|
|
{
|
|
name: "missing path",
|
|
input: `page Dashboard layout main`,
|
|
},
|
|
{
|
|
name: "invalid path format",
|
|
input: `page Dashboard at dashboard layout main`,
|
|
},
|
|
}
|
|
|
|
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")
|
|
}
|
|
})
|
|
}
|
|
}
|