Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

devtools_test.gno

4.08 Kb · 114 lines
  1package devtools
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/moul/fifo/v0"
  7	"gno.land/r/moul/config/v1"
  8	"gno.land/p/nt/uassert/v0"
  9)
 10
 11// reset puts the realm back to a just-deployed state. Realm globals live for
 12// the whole test binary and gno runs every Example AFTER every Test, so without
 13// this the pinned output would depend on which tests ran before it.
 14func reset() { notes = fifo.New(maxNotes) }
 15
 16func TestAdd(cur realm, t *testing.T) {
 17	reset()
 18
 19	Add(cross(cur), "hello")
 20	uassert.Equal(t, 1, notes.Size())
 21	uassert.Equal(t, "hello", notes.Get(0).(string))
 22
 23	uassert.AbortsWithMessage(t, cur, "empty note", func() { Add(cross(cur), "   ") })
 24	uassert.AbortsContains(t, cur, "note too long", func() {
 25		long := ""
 26		for i := 0; i < 201; i++ {
 27			long += "x"
 28		}
 29		Add(cross(cur), long)
 30	})
 31}
 32
 33// TestAddEscapes is the guard that matters most in a realm anyone can write to:
 34// a note is a string a caller typed, and it lands in a markdown list. Without
 35// ui.Inline the first line below would close the list item and render as a
 36// heading, and the second would become a link.
 37func TestAddEscapes(cur realm, t *testing.T) {
 38	reset()
 39
 40	Add(cross(cur), "# not a heading")
 41	Add(cross(cur), "[not](https://evil.example.com)")
 42
 43	for _, got := range []string{notes.Get(0).(string), notes.Get(1).(string)} {
 44		uassert.False(t, got == "# not a heading", "markdown survived unescaped")
 45		uassert.False(t, got == "[not](https://evil.example.com)", "a link survived unescaped")
 46	}
 47	uassert.True(t, len(notes.Get(0).(string)) > len("# not a heading"),
 48		"escaping should have added characters")
 49}
 50
 51// TestNotesAreBounded pins the reason fifo is here rather than a slice: storage
 52// is paid for and never refunded, so a list anyone can append to needs a
 53// ceiling in the type, not in a comment.
 54func TestNotesAreBounded(cur realm, t *testing.T) {
 55	reset()
 56
 57	for i := 0; i < maxNotes+5; i++ {
 58		Add(cross(cur), "note "+string(rune('a'+i)))
 59	}
 60	uassert.Equal(t, maxNotes, notes.Size(), "the list stops growing at maxNotes")
 61	uassert.Equal(t, maxNotes, notes.MaxSize())
 62}
 63
 64// TestAddHonoursThePause is the whole point of importing r/moul/config: this
 65// realm can be stopped from outside, by a transaction against another realm,
 66// with no redeploy here. It cannot set that from this package (config has its
 67// own authority), so it asserts the wiring: Add goes through the pause check
 68// and passes while nothing is set.
 69func TestAddHonoursThePause(cur realm, t *testing.T) {
 70	reset()
 71	uassert.False(t, config.IsPausedFor(realmPath), "nothing configured means running")
 72	Add(cross(cur), "allowed while running")
 73	uassert.Equal(t, 1, notes.Size())
 74}
 75
 76func TestReset(cur realm, t *testing.T) {
 77	reset()
 78	Add(cross(cur), "one")
 79	Reset(cross(cur))
 80	uassert.Equal(t, 0, notes.Size())
 81}
 82
 83// ExampleRender pins the root view. It is free of the height and the chain id
 84// on purpose: those live under :chain, so this one is stable.
 85func ExampleRender() {
 86	reset()
 87	print(Render(""))
 88	// Output:
 89	// # allinone: devtools
 90	//
 91	// Eight packages, used together rather than one at a time. [source](https://mygnoscan.moul.p2p.team/realm/r/moul/x/allinone/devtools/v0)
 92	//
 93	// ## Notes
 94	//
 95	// *no notes yet*
 96	//
 97	// - [add a note](/r/moul/x/allinone/devtools/v0$help&func=Add&body=hello)
 98	// - [clear them](/r/moul/x/allinone/devtools/v0$help&func=Reset)
 99	// - [live chain values](/r/moul/x/allinone/devtools/v0:chain)
100	// - [toggle the debug panel](/r/moul/x/allinone/devtools/v0:?debug=1)
101	//
102	// ## What each package does here
103	//
104	// | package | what it contributed |
105	// | --- | --- |
106	// | `r/moul/config` | the notice block above, the pause guard on `Add`, and which explorer to link |
107	// | `p/moul/mygnoscan` | the explorer routes, so a link survives a route change |
108	// | `p/moul/debug` | the `?debug=1` panel, empty otherwise |
109	// | `p/moul/realmpath` | splitting the render path into a view and a query |
110	// | `p/moul/kit/ui` | escaping a caller's note, and this table |
111	// | `p/moul/md` | every heading, link and list on the page |
112	// | `p/moul/txlink` | the call links above, relative to this realm |
113	// | `p/moul/fifo` | the note list, bounded so storage cannot grow forever |
114}