117 lines
3.6 KiB
Go
117 lines
3.6 KiB
Go
package webbundler
|
|
|
|
import "testing"
|
|
|
|
// parseOne parses a single JSX element starting at the first '<'.
|
|
func parseOne(t *testing.T, src string) jsxNode {
|
|
t.Helper()
|
|
i := 0
|
|
for i < len(src) && src[i] != '<' {
|
|
i++
|
|
}
|
|
node, next, err := parseJSX(src, i)
|
|
if err != nil {
|
|
t.Fatalf("parseJSX(%q): %v", src, err)
|
|
}
|
|
if next != len(src) {
|
|
t.Fatalf("parseJSX(%q): consumed to %d, want %d (trailing %q)", src, next, len(src), src[next:])
|
|
}
|
|
return node
|
|
}
|
|
|
|
func TestParseStaticElement(t *testing.T) {
|
|
n := parseOne(t, `<div class="x">hi</div>`)
|
|
if n.kind != jsxElement || n.tag != "div" {
|
|
t.Fatalf("kind/tag = %d/%q", n.kind, n.tag)
|
|
}
|
|
if len(n.attrs) != 1 || n.attrs[0].kind != attrStatic || n.attrs[0].name != "class" || n.attrs[0].value != "x" {
|
|
t.Fatalf("attrs = %+v", n.attrs)
|
|
}
|
|
if len(n.children) != 1 || n.children[0].kind != jsxText || n.children[0].text != "hi" {
|
|
t.Fatalf("children = %+v", n.children)
|
|
}
|
|
}
|
|
|
|
func TestParseComponent(t *testing.T) {
|
|
n := parseOne(t, `<Foo bar={1}>child</Foo>`)
|
|
if n.kind != jsxComponent || n.tag != "Foo" {
|
|
t.Fatalf("kind/tag = %d/%q", n.kind, n.tag)
|
|
}
|
|
if len(n.attrs) != 1 || n.attrs[0].kind != attrExpr || n.attrs[0].name != "bar" || n.attrs[0].expr != "1" {
|
|
t.Fatalf("attrs = %+v", n.attrs)
|
|
}
|
|
}
|
|
|
|
func TestParseNested(t *testing.T) {
|
|
n := parseOne(t, `<div><span>a</span><b>{x()}</b></div>`)
|
|
if len(n.children) != 2 {
|
|
t.Fatalf("want 2 element children, got %d: %+v", len(n.children), n.children)
|
|
}
|
|
if n.children[0].tag != "span" || n.children[1].tag != "b" {
|
|
t.Fatalf("child tags = %q, %q", n.children[0].tag, n.children[1].tag)
|
|
}
|
|
b := n.children[1]
|
|
if len(b.children) != 1 || b.children[0].kind != jsxExpr || b.children[0].expr != "x()" {
|
|
t.Fatalf("b children = %+v", b.children)
|
|
}
|
|
}
|
|
|
|
func TestParseFragment(t *testing.T) {
|
|
n := parseOne(t, `<>{a()}{b()}</>`)
|
|
if n.kind != jsxFragment || len(n.children) != 2 {
|
|
t.Fatalf("fragment: kind=%d children=%d", n.kind, len(n.children))
|
|
}
|
|
if n.children[0].expr != "a()" || n.children[1].expr != "b()" {
|
|
t.Fatalf("fragment children = %+v", n.children)
|
|
}
|
|
}
|
|
|
|
func TestParseSelfCloseAndBool(t *testing.T) {
|
|
n := parseOne(t, `<input disabled type="text" />`)
|
|
if n.tag != "input" || len(n.children) != 0 {
|
|
t.Fatalf("input: tag=%q children=%d", n.tag, len(n.children))
|
|
}
|
|
if len(n.attrs) != 2 || !n.attrs[0].boolt || n.attrs[0].name != "disabled" {
|
|
t.Fatalf("attrs = %+v", n.attrs)
|
|
}
|
|
if n.attrs[1].name != "type" || n.attrs[1].value != "text" {
|
|
t.Fatalf("type attr = %+v", n.attrs[1])
|
|
}
|
|
}
|
|
|
|
func TestParseSpread(t *testing.T) {
|
|
n := parseOne(t, `<div {...p} class="x">hi</div>`)
|
|
if len(n.attrs) != 2 || n.attrs[0].kind != attrSpread || n.attrs[0].expr != "p" {
|
|
t.Fatalf("attrs = %+v", n.attrs)
|
|
}
|
|
}
|
|
|
|
// The hardest case: an expression child containing nested JSX AND braces inside
|
|
// strings/templates. The parser must capture the whole expression opaquely.
|
|
func TestParseExprWithNestedJSX(t *testing.T) {
|
|
n := parseOne(t, "<ul>{items.map((x) => <li>{x}</li>)}</ul>")
|
|
if len(n.children) != 1 || n.children[0].kind != jsxExpr {
|
|
t.Fatalf("children = %+v", n.children)
|
|
}
|
|
if n.children[0].expr != "items.map((x) => <li>{x}</li>)" {
|
|
t.Fatalf("expr = %q", n.children[0].expr)
|
|
}
|
|
}
|
|
|
|
func TestCaptureBracesLexer(t *testing.T) {
|
|
cases := []struct{ in, want string }{
|
|
{`{a + "}" + b}`, `a + "}" + b`},
|
|
{"{`a${nested}b`}", "`a${nested}b`"},
|
|
{`{ {k: 1} }`, ` {k: 1} `},
|
|
{`{f(/[}]/)}`, `f(/[}]/)`},
|
|
{`{x} rest`, `x`},
|
|
}
|
|
for _, c := range cases {
|
|
got, next := captureBraces(c.in, 0)
|
|
if got != c.want {
|
|
t.Errorf("captureBraces(%q) = %q, want %q", c.in, got, c.want)
|
|
}
|
|
_ = next
|
|
}
|
|
}
|