PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/kit/ui/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / kit / ui
Files
4 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/kit/ui/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • ui.gnogno
  • ui_test.gnogno
ui_test.gnogno
1package ui23import (4	"strings"5	"testing"67	"gno.land/p/nt/testutils/v0"8	"gno.land/p/nt/uassert/v0"9)1011func TestShortN(t *testing.T) {12	cases := []struct {13		name       string14		in         string15		head, tail int16		want       string17	}{18		{"empty", "", 8, 4, ""},19		{"shorter than the rule", "abc", 8, 4, "abc"},20		{"exactly at the threshold", "0123456789abc", 8, 4, "0123456789abc"},21		{"one over the threshold", "0123456789abcd", 8, 4, "01234567…abcd"},22		{"gno address", "g1manfred47kzduec920z88wfr64ylksmdcedlf5", 8, 4, "g1manfre…dlf5"},23		{"head only", "0123456789abcd", 6, 0, "012345…"},24		{"tail only", "0123456789abcd", 0, 4, "…abcd"},25		{"negative clamps to zero", "0123456789abcd", -3, -3, "…"},26		{"multibyte is not split", "éééééééééééééé", 2, 2, "éé…éé"},27	}28	for _, tc := range cases {29		got := ShortN(tc.in, tc.head, tc.tail)30		uassert.Equal(t, tc.want, got, tc.name)31	}32}3334// ShortN must never make a string longer: that is the property the four35// divergent shortAddr implementations disagreed on.36func TestShortNeverGrows(t *testing.T) {37	inputs := []string{"", "a", "abcdefghijkl", "0123456789abc", "0123456789abcd", strings.Repeat("x", 100)}38	for _, in := range inputs {39		got := Short(in)40		uassert.True(t, len([]rune(got)) <= len([]rune(in)), "Short("+in+") grew the input")41	}42}4344func TestAddr(t *testing.T) {45	a := testutils.TestAddress("alice")46	full := a.String()4748	uassert.Equal(t, "`"+full+"`", AddrFull(a))49	uassert.Equal(t, Short(full), AddrText(a))50	uassert.Equal(t, "`"+Short(full)+"`", Addr(a))5152	// The shortened form keeps the recognisable head and the distinguishing tail.53	uassert.True(t, strings.HasPrefix(AddrText(a), full[:Head]), "head is kept")54	uassert.True(t, strings.HasSuffix(AddrText(a), full[len(full)-Tail:]), "tail is kept")55	uassert.True(t, strings.Contains(AddrText(a), Ellipsis), "ellipsis is present")56}5758// Two different addresses must not collide once shortened, or the display is59// worse than useless.60func TestAddrOf(t *testing.T) {61	a := testutils.TestAddress("alice")62	uassert.Equal(t, Addr(a), AddrOf(a.String()))63}6465func TestAddrDistinguishes(t *testing.T) {66	seen := map[string]bool{}67	for _, name := range []string{"alice", "bob", "carol", "dave", "eve"} {68		s := AddrText(testutils.TestAddress(name))69		uassert.False(t, seen[s], "shortened address collision on "+name)70		seen[s] = true71	}72}7374func TestInlineEscapes(t *testing.T) {75	// The seven pairs the hand-rolled escapeInline copies covered.76	for _, meta := range []string{"*", "_", "[", "]", "`", "\\"} {77		got := Inline("a" + meta + "b")78		uassert.True(t, strings.Contains(got, "\\"+meta), "Inline did not escape "+meta)79	}80	// And the one they all missed: a newline lets user text escape its line.81	uassert.False(t, strings.Contains(Inline("a\nb"), "\n"), "Inline must fold newlines")82}8384func TestCellEscapesPipe(t *testing.T) {85	got := Cell("a|b")86	uassert.False(t, strings.Contains(got, " |"), "a bare pipe would open a new column")87	uassert.True(t, strings.Contains(got, "\\|"), "pipe must be escaped")88}8990func TestEmpty(t *testing.T) {91	uassert.Equal(t, "*No games yet.*\n", Empty("No games yet."))92}9394func TestPodium(t *testing.T) {95	cases := []struct {96		rank int97		want string98	}{99		{0, "\U0001F947"},100		{1, "\U0001F948"},101		{2, "\U0001F949"},102		{3, ""},103		{99, ""},104		{-1, ""},105	}106	for _, tc := range cases {107		uassert.Equal(t, tc.want, Podium(tc.rank))108	}109}110111func TestTable(t *testing.T) {112	tbl := NewTable("#", "who", "score")113	uassert.Equal(t, 0, tbl.Len())114	uassert.Equal(t, "", tbl.String())115	uassert.Equal(t, "*nobody yet.*\n", tbl.OrEmpty("nobody yet."))116117	tbl.Row("1", "`g1abc…wxyz`", "42").Row("2", "`g1def…uvwx`", "7")118	uassert.Equal(t, 2, tbl.Len())119120	want := "| # | who | score |\n" +121		"| --- | --- | --- |\n" +122		"| 1 | `g1abc…wxyz` | 42 |\n" +123		"| 2 | `g1def…uvwx` | 7 |\n"124	uassert.Equal(t, want, tbl.String())125	uassert.Equal(t, want, tbl.OrEmpty("nobody yet."))126}127128// A short row is padded rather than producing a table the renderer misreads.129func TestTableShortRowIsPadded(t *testing.T) {130	tbl := NewTable("a", "b", "c")131	tbl.Row("1")132	uassert.Equal(t, "| a | b | c |\n| --- | --- | --- |\n| 1 |  |  |\n", tbl.String())133}134135// A cell escaped with Cell must survive Table untouched: stacking Cell on an136// escaper that also rewrites "|" produced "a\\&#124;b" in the guestbook port.137func TestTableDoesNotDoubleEscape(t *testing.T) {138	tbl := NewTable("msg")139	tbl.Row(Cell("a|b"))140	got := tbl.String()141	uassert.True(t, strings.Contains(got, `a\|b`), "expected the GFM pipe escape, got: "+got)142	uassert.False(t, strings.Contains(got, "&#124;"), "cell was escaped twice: "+got)143}144145func TestJoin(t *testing.T) {146	uassert.Equal(t, "a\n\nb", Join("\n\n", "a", "", "b"))147	uassert.Equal(t, "", Join("\n\n", "", ""))148	uassert.Equal(t, "solo", Join("\n\n", "solo"))149}150151// Action must produce a markdown link whose target carries the function name152// and the key/value arguments, and must escape the title (which helplink153// itself does not).154func TestAction(t *testing.T) {155	got := Action("Play cell 4", "Move", "gameID", "7", "cell", "4")156	uassert.True(t, strings.HasPrefix(got, "[Play cell 4]("), "expected a markdown link, got: "+got)157	uassert.True(t, strings.Contains(got, "Move"), "link must name the function")158	uassert.True(t, strings.Contains(got, "gameID=7"), "link must carry the args")159	uassert.True(t, strings.Contains(got, "cell=4"), "link must carry the args")160161	esc := Action("a*b", "Fn")162	uassert.True(t, strings.HasPrefix(esc, "[a\\*b]("), "title must be escaped, got: "+esc)163}164165func TestActionIn(t *testing.T) {166	got := ActionIn("gno.land/r/moul/gns/v0", "Say hi", "Hello", "name", "moul")167	uassert.True(t, strings.Contains(got, "/r/moul/gns/v0"), "must target the named realm, got: "+got)168	uassert.True(t, strings.Contains(got, "Hello"), "must name the function")169}170171func TestExcerpt(t *testing.T) {172	cases := []struct {173		name  string174		in    string175		width int176		want  string177	}{178		{"empty", "", 10, ""},179		{"shorter than the width", "hello", 10, "hello"},180		{"one over is kept whole, as ShortN does", "0123456789a", 10, "0123456789a"},181		{"two over is cut", "0123456789ab", 10, "0123456789…"},182		{"zero width", "hello", 0, "…"},183		{"negative width clamps to zero", "hello", -3, "…"},184		// The cut counts runes. A byte slice at 10 would split the 11th 'é'185		// and put invalid UTF-8 on the page.186		{"multibyte is not split", strings.Repeat("é", 20), 10, strings.Repeat("é", 10) + "…"},187		// The escaping half: what is kept is escaped, the ellipsis is not.188		{"escapes what it keeps", "[link](url) and more", 11, "\\[link\\]\\(url\\)…"},189		{"newline cannot escape the line", "one\ntwo", 10, "one two"},190	}191	for _, tc := range cases {192		uassert.Equal(t, tc.want, Excerpt(tc.in, tc.width), tc.name)193	}194}195196// Excerpt must never leave a trailing lone backslash: that is the bug that197// comes from escaping first and cutting second, and it escapes whatever the198// caller appends after the excerpt.199func TestExcerptNeverStrandsABackslash(t *testing.T) {200	// Each of these ends, at some width, on a character the escaper backslashes.201	for _, in := range []string{202		"....................", "********************", "[[[[[[[[[[[[[[[[[[[[",203		"!!!!!!!!!!!!!!!!!!!!", "<<<<<<<<<<<<<<<<<<<<", "--------------------",204	} {205		for width := 0; width <= 15; width++ {206			got := Excerpt(in, width)207			body := strings.TrimSuffix(got, Ellipsis)208			trailing := 0209			for i := len(body) - 1; i >= 0 && body[i] == '\\'; i-- {210				trailing++211			}212			uassert.Equal(t, 0, trailing%2, "odd run of trailing backslashes in Excerpt("+in+")")213		}214	}215}216

Functions

not supported for pure packages by the node (vm/qfuncs)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.