ExApps that mount it share one Files-style tree; an ExApp that never mounts it keeps its own page. Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
4.2 KiB
Go
157 lines
4.2 KiB
Go
package gonexapp_test
|
|
|
|
import (
|
|
"html"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.neitzel.de/konrad/go-nc-exapp"
|
|
)
|
|
|
|
func sampleNavigation() gonexapp.AppNavigation {
|
|
items := []gonexapp.Item{
|
|
{
|
|
ID: "domains", Label: "Domains",
|
|
Children: []gonexapp.Item{{ID: "example.com", Label: "example.com"}},
|
|
},
|
|
{ID: "keys", Label: "Keys"},
|
|
}
|
|
return gonexapp.AppNavigation{
|
|
Items: func(*http.Request) []gonexapp.Item { return items },
|
|
Page: func(_ *http.Request, id string) (string, bool) {
|
|
switch id {
|
|
case "domains":
|
|
return "<p>domains-page</p>", true
|
|
case "keys":
|
|
return "<p>keys-page</p>", true
|
|
case "example.com":
|
|
return "<p>zone-page</p>", true
|
|
default:
|
|
return "", false
|
|
}
|
|
},
|
|
Header: func(*http.Request) string { return `<p id="visit">folder-line</p>` },
|
|
DefaultID: "domains",
|
|
MissingMessage: "that item is not in this Visit",
|
|
}
|
|
}
|
|
|
|
func navBody(t *testing.T, rawURL string) string {
|
|
t.Helper()
|
|
rec := httptest.NewRecorder()
|
|
sampleNavigation().Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, rawURL, nil))
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("GET %s: got %d", rawURL, rec.Code)
|
|
}
|
|
return rec.Body.String()
|
|
}
|
|
|
|
func navAddress(t *testing.T, body string) url.Values {
|
|
t.Helper()
|
|
const key = `data-address="`
|
|
_, after, ok := strings.Cut(body, key)
|
|
if !ok {
|
|
t.Fatalf("no data-address in %s", body)
|
|
}
|
|
rest := after
|
|
end := strings.Index(rest, `"`)
|
|
if end < 0 {
|
|
t.Fatalf("unclosed data-address")
|
|
}
|
|
raw, err := url.QueryUnescape(html.UnescapeString(rest[:end]))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
q, err := url.ParseQuery(strings.TrimPrefix(raw, "?"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return q
|
|
}
|
|
|
|
func TestAppNavigationShowsSelectedPage(t *testing.T) {
|
|
body := navBody(t, "/?item=keys")
|
|
if !strings.Contains(body, "<p>keys-page</p>") {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
if !strings.Contains(body, `data-selected="keys"`) {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
if navAddress(t, body).Get("item") != "keys" {
|
|
t.Fatalf("address: %s", navAddress(t, body).Encode())
|
|
}
|
|
}
|
|
|
|
func TestAppNavigationDefaultsWhenItemMissingFromAddress(t *testing.T) {
|
|
body := navBody(t, "/")
|
|
if !strings.Contains(body, "<p>domains-page</p>") {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
if navAddress(t, body).Get("item") != "domains" {
|
|
t.Fatalf("address item = %q", navAddress(t, body).Get("item"))
|
|
}
|
|
if strings.Contains(body, "that item is not in this Visit") {
|
|
t.Fatalf("default open should not show the missing-item message: %s", body)
|
|
}
|
|
}
|
|
|
|
func TestAppNavigationMissingItemSelectsDefault(t *testing.T) {
|
|
body := navBody(t, "/?folder=share&item=gone")
|
|
if !strings.Contains(body, "<p>domains-page</p>") {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
if !strings.Contains(body, "that item is not in this Visit") {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
q := navAddress(t, body)
|
|
if q.Get("item") != "domains" || q.Get("folder") != "share" {
|
|
t.Fatalf("address = %s", q.Encode())
|
|
}
|
|
}
|
|
|
|
func TestAppNavigationPageRefusalSelectsDefault(t *testing.T) {
|
|
nav := sampleNavigation()
|
|
inner := nav.Page
|
|
nav.Page = func(r *http.Request, id string) (string, bool) {
|
|
if id == "keys" {
|
|
return "", false
|
|
}
|
|
return inner(r, id)
|
|
}
|
|
rec := httptest.NewRecorder()
|
|
nav.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/?folder=share&item=keys", nil))
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("got %d", rec.Code)
|
|
}
|
|
body := rec.Body.String()
|
|
if !strings.Contains(body, "<p>domains-page</p>") || !strings.Contains(body, "that item is not in this Visit") {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
q := navAddress(t, body)
|
|
if q.Get("item") != "domains" || q.Get("folder") != "share" {
|
|
t.Fatalf("address = %s", q.Encode())
|
|
}
|
|
}
|
|
|
|
func TestAppNavigationChildExpandsParents(t *testing.T) {
|
|
body := navBody(t, "/?item=example.com")
|
|
if !strings.Contains(body, "<p>zone-page</p>") {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
if !strings.Contains(body, `data-id="domains" data-expanded="true"`) {
|
|
t.Fatalf("got %s", body)
|
|
}
|
|
}
|
|
|
|
func TestAppNavigationHeaderPrecedesTree(t *testing.T) {
|
|
body := navBody(t, "/?item=domains")
|
|
header := strings.Index(body, "folder-line")
|
|
tree := strings.Index(body, ">Domains<")
|
|
if header < 0 || tree < 0 || header > tree {
|
|
t.Fatalf("header %d tree %d in %s", header, tree, body)
|
|
}
|
|
}
|