Running a real Claude Code session against Fathom exposed three things that only show up on real data: - A run measured 16 input and 1,301 output tokens against 280,054 cache reads. Summing them reports "281k" and makes every cached run look huge; omitting them reports "1.3k" and hides where the context came from. Both are now tracked and shown separately. - Folding required a matching tool_use_id, but Claude Code stamps that on the tool and tool.execution spans and omits it on tool.blocked_on_user, so a permission-wait row survived under every tool. The test is now containment plus a dotted name extension, folding 29 spans into 17 rows. - Span durations were absolutely positioned past the end of their bar, so a full-width bar pushed its label off-screen; percentage geometry resolves against the padding box, so padding reserved nothing. Durations moved to their own grid column, which also aligns them. Adds config tests, an annotated fathom.ini.example, and a test that runs node --check over the embedded JS -- a mangled edit shipped a blank page once, and nothing else validates JS that has no build step.
56 lines
1.5 KiB
Go
56 lines
1.5 KiB
Go
package web
|
|
|
|
import (
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestEmbeddedJSParses catches syntax errors in the embedded UI at build time.
|
|
//
|
|
// The UI is plain JS with no build step, which is the point — but it also means
|
|
// nothing validates it before it reaches a browser, where a syntax error is a
|
|
// blank page and a console message nobody is watching. A broken edit shipped
|
|
// exactly that way once; this makes it a test failure instead.
|
|
//
|
|
// Skips cleanly when no JS runtime is installed, since one is not otherwise
|
|
// required to build or run Fathom.
|
|
func TestEmbeddedJSParses(t *testing.T) {
|
|
node := findNode()
|
|
if node == "" {
|
|
t.Skip("no node/deno/bun on PATH; skipping UI syntax check")
|
|
}
|
|
files, err := filepath.Glob("static/*.js")
|
|
if err != nil || len(files) == 0 {
|
|
t.Fatalf("no embedded JS found: %v", err)
|
|
}
|
|
for _, f := range files {
|
|
out, err := exec.Command(node, "--check", f).CombinedOutput()
|
|
if err != nil {
|
|
t.Errorf("%s does not parse:\n%s", f, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func findNode() string {
|
|
// nvm installs node outside the PATH a test process inherits, so check the
|
|
// usual versioned locations too.
|
|
if p, err := exec.LookPath("node"); err == nil {
|
|
return p
|
|
}
|
|
matches, _ := filepath.Glob(homeDir() + "/.nvm/versions/node/*/bin/node")
|
|
if len(matches) > 0 {
|
|
return matches[len(matches)-1]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func homeDir() string {
|
|
out, err := exec.Command("sh", "-c", "echo $HOME").Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|