fix(conventions): isolate banned imports and clarify tests

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-03-26 11:15:24 +00:00
parent b3f622988d
commit 8a3f28aff3
13 changed files with 62 additions and 26 deletions

View file

@ -1,7 +1,7 @@
package html
import (
"fmt"
"strconv"
"testing"
i18n "dappco.re/go/core/i18n"
@ -100,7 +100,7 @@ func BenchmarkImprint_Small(b *testing.B) {
func BenchmarkImprint_Large(b *testing.B) {
items := make([]string, 20)
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").
H(El("h1", Text("Building project"))).
@ -207,7 +207,7 @@ func BenchmarkLayout_Nested(b *testing.B) {
func BenchmarkLayout_ManySlotChildren(b *testing.B) {
nodes := make([]Node, 50)
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").
H(Raw("header")).
@ -242,7 +242,7 @@ func benchEach(b *testing.B, n int) {
items[i] = i
}
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()

View file

@ -1,3 +1,5 @@
//go:build !js
// 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.
//

View file

@ -1,3 +1,5 @@
//go:build !js
package main
import (
@ -9,7 +11,7 @@ import (
"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"}`)
var output bytes.Buffer
@ -23,7 +25,7 @@ func TestRun_Good(t *testing.T) {
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`)
var output bytes.Buffer
@ -32,7 +34,7 @@ func TestRun_Bad_InvalidJSON(t *testing.T) {
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"}`)
var output bytes.Buffer
@ -41,7 +43,7 @@ func TestRun_Bad_InvalidTag(t *testing.T) {
assert.Contains(t, err.Error(), "hyphen")
}
func TestRun_Good_Empty(t *testing.T) {
func TestRun_EmptySlots(t *testing.T) {
input := strings.NewReader(`{}`)
var output bytes.Buffer

View file

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestBuildComponentJS_Good(t *testing.T) {
func TestBuildComponentJS_ValidJSON(t *testing.T) {
slotsJSON := `{"H":"nav-bar","C":"main-content"}`
js, err := buildComponentJS(slotsJSON)
require.NoError(t, err)
@ -18,7 +18,7 @@ func TestBuildComponentJS_Good(t *testing.T) {
assert.Contains(t, js, "customElements.define")
}
func TestBuildComponentJS_Bad_InvalidJSON(t *testing.T) {
func TestBuildComponentJS_InvalidJSON(t *testing.T) {
_, err := buildComponentJS("not json")
assert.Error(t, err)
}

View file

@ -21,7 +21,7 @@ const (
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() {
t.Skip("skipping WASM build test in short mode")
}

View file

@ -1,3 +1,5 @@
//go:build !js
package codegen
import "testing"

View file

@ -1,7 +1,8 @@
//go:build !js
package codegen
import (
"fmt"
"strings"
"text/template"
@ -51,7 +52,7 @@ func GenerateClass(tag, slot string) (string, error) {
// GenerateRegistration produces the customElements.define() call.
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.

View file

@ -1,3 +1,5 @@
//go:build !js
package codegen
import (
@ -8,7 +10,7 @@ import (
"github.com/stretchr/testify/require"
)
func TestGenerateClass_Good(t *testing.T) {
func TestGenerateClass_ValidTag(t *testing.T) {
js, err := GenerateClass("photo-grid", "C")
require.NoError(t, err)
assert.Contains(t, js, "class PhotoGrid extends HTMLElement")
@ -17,19 +19,19 @@ func TestGenerateClass_Good(t *testing.T) {
assert.Contains(t, js, "photo-grid")
}
func TestGenerateClass_Bad_InvalidTag(t *testing.T) {
func TestGenerateClass_InvalidTag(t *testing.T) {
_, err := GenerateClass("invalid", "C")
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")
assert.Contains(t, js, "customElements.define")
assert.Contains(t, js, `"photo-grid"`)
assert.Contains(t, js, "PhotoGrid")
}
func TestTagToClassName_Good(t *testing.T) {
func TestTagToClassName_KebabCase(t *testing.T) {
tests := []struct{ tag, want string }{
{"photo-grid", "PhotoGrid"},
{"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{
"H": "nav-bar",
"C": "main-content",
"F": "nav-bar",
}
js, err := GenerateBundle(slots)
require.NoError(t, err)
assert.Contains(t, js, "NavBar")
assert.Contains(t, js, "MainContent")
assert.Equal(t, 2, strings.Count(js, "extends HTMLElement"))
assert.Equal(t, 2, strings.Count(js, "customElements.define"))
}

13
codegen/doc.go Normal file
View 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
View 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

View file

@ -66,7 +66,7 @@ go test ./cmd/codegen/
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
@ -278,7 +278,7 @@ func TestIntegration_RenderThenReverse(t *testing.T) {
### Codegen Tests with Testify
```go
func TestGenerateClass_Good(t *testing.T) {
func TestGenerateClass_ValidTag(t *testing.T) {
js, err := GenerateClass("photo-grid", "C")
require.NoError(t, err)
assert.Contains(t, js, "class PhotoGrid extends HTMLElement")

View file

@ -78,7 +78,7 @@ The fix was applied in three distinct steps:
### 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).
- Raw size < 3,145,728 bytes (3 MB).

View file

@ -1,7 +1,7 @@
package html
import (
"fmt"
"strconv"
"strings"
"testing"
@ -196,7 +196,7 @@ func TestLayout_DeepNesting_10Levels(t *testing.T) {
for i := 1; i < 10; i++ {
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)
}
@ -251,7 +251,7 @@ func TestEach_LargeIteration_1000(t *testing.T) {
}
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)
@ -275,7 +275,7 @@ func TestEach_LargeIteration_5000(t *testing.T) {
}
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)
@ -292,7 +292,7 @@ func TestEach_NestedEach(t *testing.T) {
node := Each(rows, func(row int) 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))
}))
})