e2e/diffweb_test.go

v1.0.0
gitbay/e2e/diffweb_test.go history · blame · raw

104 lines · 3643 bytes

  1package e2e
  2
  3import (
  4	"os"
  5	"path/filepath"
  6	"strings"
  7	"testing"
  8)
  9
 10// TestDiffRendering covers the shared diff view: per-file folds with stats,
 11// line-number gutters, syntax highlighting, and binary files declared
 12// rather than dumped.
 13func TestDiffRendering(t *testing.T) {
 14	inst := startInstance(t)
 15	aliceKey := inst.newKey(t, "alice")
 16	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
 17	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 {
 18		t.Fatalf("repo create: %s", errOut)
 19	}
 20
 21	work := t.TempDir()
 22	env := inst.gitEnv(aliceKey)
 23	mustGit(t, work, env, "clone", inst.sshURL("alice/app"), "w")
 24	dir := filepath.Join(work, "w")
 25	write := func(name, body string) {
 26		os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644)
 27	}
 28	write("main.go", "package main\n\nfunc greet() string {\n\treturn \"hello\"\n}\n")
 29	write("notes.txt", "old title\n")
 30	mustGit(t, dir, env, "checkout", "-q", "-b", "main")
 31	mustGit(t, dir, env, "add", ".")
 32	mustGit(t, dir, env, "commit", "-q", "-m", "base")
 33	mustGit(t, dir, env, "push", "-q", "origin", "main")
 34
 35	// One commit touching three files in three different ways.
 36	write("main.go", "package main\n\nfunc greet() string {\n\t// now with feeling\n\treturn \"HELLO\"\n}\n")
 37	os.Rename(filepath.Join(dir, "notes.txt"), filepath.Join(dir, "README.md"))
 38	os.WriteFile(filepath.Join(dir, "logo.png"), []byte("\x89PNG\r\n\x1a\n\x00\x00binary"), 0o644)
 39	mustGit(t, dir, env, "add", "-A")
 40	mustGit(t, dir, env, "commit", "-q", "-m", "rework")
 41	mustGit(t, dir, env, "push", "-q", "origin", "main")
 42
 43	out, _, _ := inst.ssh(t, aliceKey, "", "repo", "log", "alice/app", "--json")
 44	sha := jsonField(out, "sha")
 45	if sha == "" {
 46		t.Fatalf("no sha in log: %s", out)
 47	}
 48
 49	status, body := inst.get(t, "/alice/app/commit/"+sha)
 50	if status != 200 {
 51		t.Fatalf("commit page: %d", status)
 52	}
 53
 54	// A fold per file, each naming its path.
 55	for _, path := range []string{"main.go", "logo.png"} {
 56		if !strings.Contains(body, ">"+path+"<") {
 57			t.Errorf("no section for %s", path)
 58		}
 59	}
 60	if strings.Count(body, `<details class="difffold"`) != 3 {
 61		t.Errorf("want 3 file sections, got %d", strings.Count(body, `<details class="difffold"`))
 62	}
 63	// The rename is shown as one, not as an add plus a delete.
 64	if !strings.Contains(body, "notes.txt") || !strings.Contains(body, "renamed") {
 65		t.Error("rename not shown as a rename")
 66	}
 67	// Binary content is declared, never dumped into the page.
 68	if !strings.Contains(body, "Binary file not shown") {
 69		t.Error("binary file not declared")
 70	}
 71	if strings.Contains(body, "\x89PNG") {
 72		t.Error("binary content leaked into the diff")
 73	}
 74	// Line-number gutters and per-file stats.
 75	if !strings.Contains(body, `<td class="ln">`) {
 76		t.Error("no line-number gutter")
 77	}
 78	if !strings.Contains(body, `<span class="add">+`) || !strings.Contains(body, `<span class="del">−`) {
 79		t.Error("no per-file stat")
 80	}
 81	// Go is a type chroma knows, so the added line carries token markup.
 82	if !strings.Contains(body, "class=\"k\"") && !strings.Contains(body, "class=\"kd\"") {
 83		t.Error("diff content is not syntax highlighted")
 84	}
 85	// The +/- markers are CSS, so a copied selection is real source.
 86	if strings.Contains(body, `<td class="code">+`) {
 87		t.Error("diff markers are in the markup, not the stylesheet")
 88	}
 89}
 90
 91// jsonField pulls the first "name":"value" string out of a JSON blob.
 92func jsonField(blob, name string) string {
 93	key := `"` + name + `":"`
 94	i := strings.Index(blob, key)
 95	if i < 0 {
 96		return ""
 97	}
 98	rest := blob[i+len(key):]
 99	j := strings.IndexByte(rest, '"')
100	if j < 0 {
101		return ""
102	}
103	return rest[:j]
104}