fix(conventions): isolate banned imports and clarify tests
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
b3f622988d
commit
8a3f28aff3
13 changed files with 62 additions and 26 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
package html
|
package html
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
i18n "dappco.re/go/core/i18n"
|
i18n "dappco.re/go/core/i18n"
|
||||||
|
|
@ -100,7 +100,7 @@ func BenchmarkImprint_Small(b *testing.B) {
|
||||||
func BenchmarkImprint_Large(b *testing.B) {
|
func BenchmarkImprint_Large(b *testing.B) {
|
||||||
items := make([]string, 20)
|
items := make([]string, 20)
|
||||||
for i := range items {
|
for i := range items {
|
||||||
items[i] = fmt.Sprintf("Item %d was created successfully", i)
|
items[i] = "Item " + strconv.Itoa(i) + " was created successfully"
|
||||||
}
|
}
|
||||||
page := NewLayout("HLCRF").
|
page := NewLayout("HLCRF").
|
||||||
H(El("h1", Text("Building project"))).
|
H(El("h1", Text("Building project"))).
|
||||||
|
|
@ -207,7 +207,7 @@ func BenchmarkLayout_Nested(b *testing.B) {
|
||||||
func BenchmarkLayout_ManySlotChildren(b *testing.B) {
|
func BenchmarkLayout_ManySlotChildren(b *testing.B) {
|
||||||
nodes := make([]Node, 50)
|
nodes := make([]Node, 50)
|
||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
nodes[i] = El("p", Raw(fmt.Sprintf("paragraph %d", i)))
|
nodes[i] = El("p", Raw("paragraph "+strconv.Itoa(i)))
|
||||||
}
|
}
|
||||||
layout := NewLayout("HLCRF").
|
layout := NewLayout("HLCRF").
|
||||||
H(Raw("header")).
|
H(Raw("header")).
|
||||||
|
|
@ -242,7 +242,7 @@ func benchEach(b *testing.B, n int) {
|
||||||
items[i] = i
|
items[i] = i
|
||||||
}
|
}
|
||||||
node := Each(items, func(i int) Node {
|
node := Each(items, func(i int) Node {
|
||||||
return El("li", Raw(fmt.Sprintf("item-%d", i)))
|
return El("li", Raw("item-"+strconv.Itoa(i)))
|
||||||
})
|
})
|
||||||
ctx := NewContext()
|
ctx := NewContext()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !js
|
||||||
|
|
||||||
// Package main provides a build-time CLI for generating Web Component JS bundles.
|
// Package main provides a build-time CLI for generating Web Component JS bundles.
|
||||||
// Reads a JSON slot map from stdin, writes the generated JS to stdout.
|
// Reads a JSON slot map from stdin, writes the generated JS to stdout.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !js
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -9,7 +11,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRun_Good(t *testing.T) {
|
func TestRun_WritesBundle(t *testing.T) {
|
||||||
input := strings.NewReader(`{"H":"nav-bar","C":"main-content"}`)
|
input := strings.NewReader(`{"H":"nav-bar","C":"main-content"}`)
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
|
||||||
|
|
@ -23,7 +25,7 @@ func TestRun_Good(t *testing.T) {
|
||||||
assert.Equal(t, 2, strings.Count(js, "extends HTMLElement"))
|
assert.Equal(t, 2, strings.Count(js, "extends HTMLElement"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRun_Bad_InvalidJSON(t *testing.T) {
|
func TestRun_InvalidJSON(t *testing.T) {
|
||||||
input := strings.NewReader(`not json`)
|
input := strings.NewReader(`not json`)
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
|
||||||
|
|
@ -32,7 +34,7 @@ func TestRun_Bad_InvalidJSON(t *testing.T) {
|
||||||
assert.Contains(t, err.Error(), "invalid JSON")
|
assert.Contains(t, err.Error(), "invalid JSON")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRun_Bad_InvalidTag(t *testing.T) {
|
func TestRun_InvalidTag(t *testing.T) {
|
||||||
input := strings.NewReader(`{"H":"notag"}`)
|
input := strings.NewReader(`{"H":"notag"}`)
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
|
||||||
|
|
@ -41,7 +43,7 @@ func TestRun_Bad_InvalidTag(t *testing.T) {
|
||||||
assert.Contains(t, err.Error(), "hyphen")
|
assert.Contains(t, err.Error(), "hyphen")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRun_Good_Empty(t *testing.T) {
|
func TestRun_EmptySlots(t *testing.T) {
|
||||||
input := strings.NewReader(`{}`)
|
input := strings.NewReader(`{}`)
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBuildComponentJS_Good(t *testing.T) {
|
func TestBuildComponentJS_ValidJSON(t *testing.T) {
|
||||||
slotsJSON := `{"H":"nav-bar","C":"main-content"}`
|
slotsJSON := `{"H":"nav-bar","C":"main-content"}`
|
||||||
js, err := buildComponentJS(slotsJSON)
|
js, err := buildComponentJS(slotsJSON)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -18,7 +18,7 @@ func TestBuildComponentJS_Good(t *testing.T) {
|
||||||
assert.Contains(t, js, "customElements.define")
|
assert.Contains(t, js, "customElements.define")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildComponentJS_Bad_InvalidJSON(t *testing.T) {
|
func TestBuildComponentJS_InvalidJSON(t *testing.T) {
|
||||||
_, err := buildComponentJS("not json")
|
_, err := buildComponentJS("not json")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ const (
|
||||||
wasmRawLimit = 3_670_016 // 3.5 MB raw size limit
|
wasmRawLimit = 3_670_016 // 3.5 MB raw size limit
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWASMBinarySize_Good(t *testing.T) {
|
func TestWASMBinarySize_WithinBudget(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping WASM build test in short mode")
|
t.Skip("skipping WASM build test in short mode")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !js
|
||||||
|
|
||||||
package codegen
|
package codegen
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
//go:build !js
|
||||||
|
|
||||||
package codegen
|
package codegen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
|
@ -51,7 +52,7 @@ func GenerateClass(tag, slot string) (string, error) {
|
||||||
|
|
||||||
// GenerateRegistration produces the customElements.define() call.
|
// GenerateRegistration produces the customElements.define() call.
|
||||||
func GenerateRegistration(tag, className string) string {
|
func GenerateRegistration(tag, className string) string {
|
||||||
return fmt.Sprintf(`customElements.define("%s", %s);`, tag, className)
|
return `customElements.define("` + tag + `", ` + className + `);`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagToClassName converts a kebab-case tag to PascalCase class name.
|
// TagToClassName converts a kebab-case tag to PascalCase class name.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !js
|
||||||
|
|
||||||
package codegen
|
package codegen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -8,7 +10,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateClass_Good(t *testing.T) {
|
func TestGenerateClass_ValidTag(t *testing.T) {
|
||||||
js, err := GenerateClass("photo-grid", "C")
|
js, err := GenerateClass("photo-grid", "C")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, js, "class PhotoGrid extends HTMLElement")
|
assert.Contains(t, js, "class PhotoGrid extends HTMLElement")
|
||||||
|
|
@ -17,19 +19,19 @@ func TestGenerateClass_Good(t *testing.T) {
|
||||||
assert.Contains(t, js, "photo-grid")
|
assert.Contains(t, js, "photo-grid")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateClass_Bad_InvalidTag(t *testing.T) {
|
func TestGenerateClass_InvalidTag(t *testing.T) {
|
||||||
_, err := GenerateClass("invalid", "C")
|
_, err := GenerateClass("invalid", "C")
|
||||||
assert.Error(t, err, "custom element names must contain a hyphen")
|
assert.Error(t, err, "custom element names must contain a hyphen")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateRegistration_Good(t *testing.T) {
|
func TestGenerateRegistration_DefinesCustomElement(t *testing.T) {
|
||||||
js := GenerateRegistration("photo-grid", "PhotoGrid")
|
js := GenerateRegistration("photo-grid", "PhotoGrid")
|
||||||
assert.Contains(t, js, "customElements.define")
|
assert.Contains(t, js, "customElements.define")
|
||||||
assert.Contains(t, js, `"photo-grid"`)
|
assert.Contains(t, js, `"photo-grid"`)
|
||||||
assert.Contains(t, js, "PhotoGrid")
|
assert.Contains(t, js, "PhotoGrid")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTagToClassName_Good(t *testing.T) {
|
func TestTagToClassName_KebabCase(t *testing.T) {
|
||||||
tests := []struct{ tag, want string }{
|
tests := []struct{ tag, want string }{
|
||||||
{"photo-grid", "PhotoGrid"},
|
{"photo-grid", "PhotoGrid"},
|
||||||
{"nav-breadcrumb", "NavBreadcrumb"},
|
{"nav-breadcrumb", "NavBreadcrumb"},
|
||||||
|
|
@ -41,14 +43,16 @@ func TestTagToClassName_Good(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateBundle_Good(t *testing.T) {
|
func TestGenerateBundle_DeduplicatesRegistrations(t *testing.T) {
|
||||||
slots := map[string]string{
|
slots := map[string]string{
|
||||||
"H": "nav-bar",
|
"H": "nav-bar",
|
||||||
"C": "main-content",
|
"C": "main-content",
|
||||||
|
"F": "nav-bar",
|
||||||
}
|
}
|
||||||
js, err := GenerateBundle(slots)
|
js, err := GenerateBundle(slots)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, js, "NavBar")
|
assert.Contains(t, js, "NavBar")
|
||||||
assert.Contains(t, js, "MainContent")
|
assert.Contains(t, js, "MainContent")
|
||||||
assert.Equal(t, 2, strings.Count(js, "extends HTMLElement"))
|
assert.Equal(t, 2, strings.Count(js, "extends HTMLElement"))
|
||||||
|
assert.Equal(t, 2, strings.Count(js, "customElements.define"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
codegen/doc.go
Normal file
13
codegen/doc.go
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
//go:build !js
|
||||||
|
|
||||||
|
// SPDX-Licence-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
// Package codegen generates Web Component bundles for go-html slot maps.
|
||||||
|
//
|
||||||
|
// Use it at build time, or through the cmd/codegen CLI:
|
||||||
|
//
|
||||||
|
// bundle, err := GenerateBundle(map[string]string{
|
||||||
|
// "H": "site-header",
|
||||||
|
// "C": "app-main",
|
||||||
|
// })
|
||||||
|
package codegen
|
||||||
12
doc.go
Normal file
12
doc.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// SPDX-Licence-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
// Package html renders semantic HTML from composable node trees.
|
||||||
|
//
|
||||||
|
// A typical page combines Layout, El, Text, and Render:
|
||||||
|
//
|
||||||
|
// page := NewLayout("HCF").
|
||||||
|
// H(El("h1", Text("page.title"))).
|
||||||
|
// C(El("main", Text("page.body"))).
|
||||||
|
// F(El("small", Text("page.footer")))
|
||||||
|
// out := Render(page, NewContext())
|
||||||
|
package html
|
||||||
|
|
@ -66,7 +66,7 @@ go test ./cmd/codegen/
|
||||||
go test ./cmd/wasm/
|
go test ./cmd/wasm/
|
||||||
```
|
```
|
||||||
|
|
||||||
The WASM size gate test (`TestWASMBinarySize_Good`) builds the WASM binary as a subprocess. It is slow and is skipped under `-short`. It is also guarded with `//go:build !js` so it cannot run within the WASM environment itself.
|
The WASM size gate test (`TestWASMBinarySize_WithinBudget`) builds the WASM binary as a subprocess. It is slow and is skipped under `-short`. It is also guarded with `//go:build !js` so it cannot run within the WASM environment itself.
|
||||||
|
|
||||||
### Test Dependencies
|
### Test Dependencies
|
||||||
|
|
||||||
|
|
@ -278,7 +278,7 @@ func TestIntegration_RenderThenReverse(t *testing.T) {
|
||||||
### Codegen Tests with Testify
|
### Codegen Tests with Testify
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func TestGenerateClass_Good(t *testing.T) {
|
func TestGenerateClass_ValidTag(t *testing.T) {
|
||||||
js, err := GenerateClass("photo-grid", "C")
|
js, err := GenerateClass("photo-grid", "C")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Contains(t, js, "class PhotoGrid extends HTMLElement")
|
assert.Contains(t, js, "class PhotoGrid extends HTMLElement")
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ The fix was applied in three distinct steps:
|
||||||
|
|
||||||
### Size gate test (`aae5d21`)
|
### Size gate test (`aae5d21`)
|
||||||
|
|
||||||
`cmd/wasm/size_test.go` was added to prevent regression. `TestWASMBinarySize_Good` builds the WASM binary in a temp directory, gzip-compresses it, and asserts:
|
`cmd/wasm/size_test.go` was added to prevent regression. `TestWASMBinarySize_WithinBudget` builds the WASM binary in a temp directory, gzip-compresses it, and asserts:
|
||||||
|
|
||||||
- Gzip size < 1,048,576 bytes (1 MB).
|
- Gzip size < 1,048,576 bytes (1 MB).
|
||||||
- Raw size < 3,145,728 bytes (3 MB).
|
- Raw size < 3,145,728 bytes (3 MB).
|
||||||
|
|
|
||||||
10
edge_test.go
10
edge_test.go
|
|
@ -1,7 +1,7 @@
|
||||||
package html
|
package html
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -196,7 +196,7 @@ func TestLayout_DeepNesting_10Levels(t *testing.T) {
|
||||||
for i := 1; i < 10; i++ {
|
for i := 1; i < 10; i++ {
|
||||||
expectedBlock += "-C-0"
|
expectedBlock += "-C-0"
|
||||||
}
|
}
|
||||||
if !strings.Contains(got, fmt.Sprintf(`data-block="%s"`, expectedBlock)) {
|
if !strings.Contains(got, `data-block="`+expectedBlock+`"`) {
|
||||||
t.Errorf("10 levels deep: missing expected block ID %q in:\n%s", expectedBlock, got)
|
t.Errorf("10 levels deep: missing expected block ID %q in:\n%s", expectedBlock, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ func TestEach_LargeIteration_1000(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
node := Each(items, func(i int) Node {
|
node := Each(items, func(i int) Node {
|
||||||
return El("li", Raw(fmt.Sprintf("%d", i)))
|
return El("li", Raw(strconv.Itoa(i)))
|
||||||
})
|
})
|
||||||
|
|
||||||
got := node.Render(ctx)
|
got := node.Render(ctx)
|
||||||
|
|
@ -275,7 +275,7 @@ func TestEach_LargeIteration_5000(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
node := Each(items, func(i int) Node {
|
node := Each(items, func(i int) Node {
|
||||||
return El("span", Raw(fmt.Sprintf("%d", i)))
|
return El("span", Raw(strconv.Itoa(i)))
|
||||||
})
|
})
|
||||||
|
|
||||||
got := node.Render(ctx)
|
got := node.Render(ctx)
|
||||||
|
|
@ -292,7 +292,7 @@ func TestEach_NestedEach(t *testing.T) {
|
||||||
|
|
||||||
node := Each(rows, func(row int) Node {
|
node := Each(rows, func(row int) Node {
|
||||||
return El("tr", Each(cols, func(col string) Node {
|
return El("tr", Each(cols, func(col string) Node {
|
||||||
return El("td", Raw(fmt.Sprintf("%d-%s", row, col)))
|
return El("td", Raw(strconv.Itoa(row)+"-"+col))
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue