package console import ( "bytes" "net/http" "net/url" "strings" "testing" ) func TestAppSettingsSectionRoutes(t *testing.T) { fx, _ := instanceFixture(t, "code-settings-sections") c := newBrowser(t) signUp(t, fx, c, "code-settings-sections-browser") for _, section := range []string{"general", "domains", "storage", "email", "features", "advanced", "delete"} { status, _, body := get(t, c, fx.u("/apps/titogo/settings/"+section)) if status != http.StatusOK { t.Errorf("GET settings/%s = %d, want 200: %s", section, status, body) } } _, _, general := get(t, c, fx.u("/apps/titogo/settings")) for _, want := range []string{ `aria-label="Settings sections"`, ">General", ">Domains", ">Release pipeline", ">Storage", ">Email", ">Feature flags", ">Advanced", ">Delete", } { if !strings.Contains(general, want) { t.Errorf("Settings navigation missing %q", want) } } if strings.Contains(general, "Domains & TLS") { t.Fatal("General still renders the Domains section") } _, _, domains := get(t, c, fx.u("/apps/titogo/settings/domains")) if !strings.Contains(domains, "Domains & TLS") || strings.Contains(domains, "

Identity

") { t.Fatal("Domains does not render as an isolated section") } status, _, _ := get(t, c, fx.u("/apps/titogo/settings/not-a-section")) if status != http.StatusNotFound { t.Errorf("GET unknown settings section = %d, want 404", status) } assertNonMember404(t, fx, "/apps/titogo/settings/domains") } func TestAppSettingsURL(t *testing.T) { if got := appSettingsURL("a b", "an/app", "general"); got != "/a/a%20b/apps/an%2Fapp/settings" { t.Errorf("general URL = %q", got) } if got := appSettingsURL("bab", "kass", "storage"); got != "/a/bab/apps/kass/settings/storage" { t.Errorf("storage URL = %q", got) } } func TestPipelineSettingsCanonicalSectionURL(t *testing.T) { fx, _ := instanceFixture(t, "code-settings-pipeline-section") c := newBrowser(t) signUp(t, fx, c, "code-settings-pipeline-section-browser") status, _, body := get(t, c, fx.u("/apps/titogo/settings/pipeline")) if status != http.StatusOK || !strings.Contains(body, "Release pipeline") { t.Fatalf("GET settings/pipeline = %d: %s", status, body) } status, _, body = get(t, c, fx.u("/apps/titogo/pipeline")) if status != http.StatusOK || !strings.Contains(body, "Release pipeline") { t.Fatalf("legacy GET pipeline = %d: %s", status, body) } csrf := sessionToken(t, fx, c) status, _, body = postForm(t, c, fx.u("/apps/titogo/pipeline"), url.Values{ "csrf": {csrf}, "action": {"add"}, "ch.0.name": {"edge"}, }) if status != http.StatusOK || !strings.Contains(body, `aria-label="Settings sections"`) || !strings.Contains(body, `aria-current="page">Release pipeline`) { t.Fatalf("pipeline draft rerender lost Settings navigation = %d: %s", status, body) } } func TestSettingsValidationRetainsTypedValues(t *testing.T) { fx := newConsoleFixture(t) freshApp(t, fx, "sprout") c := signInPaul(t, fx) csrf := sessionToken(t, fx, c) status, _, body := postForm(t, c, fx.u("/apps/sprout/rename"), url.Values{ "csrf": {csrf}, "to": {"Still Typing"}, }) if status != http.StatusUnprocessableEntity || !strings.Contains(body, `value="Still Typing"`) { t.Fatalf("rename refusal did not retain the typed name = %d: %s", status, body) } status, _, body = postForm(t, c, fx.u("/apps/sprout/domains/attach"), url.Values{ "csrf": {csrf}, "host": {"not a host"}, "channel": {"canary/draft"}, }) if status != http.StatusUnprocessableEntity || !strings.Contains(body, `value="not a host"`) || !strings.Contains(body, `value="canary/draft"`) { t.Fatalf("domain refusal did not retain host and channel = %d: %s", status, body) } } func TestBucketlessSettingsSectionsExplainConfiguration(t *testing.T) { for section, want := range map[string]string{ "storage": "no deployment bucket on this host — this console cannot serve object storage", "email": "no deployment bucket on this host — this console cannot serve email", "features": "no deployment bucket on this host — this console cannot read features", } { p := page{ Title: "Settings", AppName: "sprout", ActiveTab: "settings", SettingsSection: section, Account: &Account{Name: "paul@example.com", Sqid: "bab"}, Detail: &AppView{App: "sprout"}, Settings: &SettingsView{NoBucket: true}, } var buf bytes.Buffer if err := templates.ExecuteTemplate(&buf, "app_settings", p); err != nil { t.Fatalf("render %s: %v", section, err) } body := buf.String() if !strings.Contains(body, want) || strings.Contains(body, "Please try again") { t.Fatalf("bucketless %s section is not explicit: %s", section, body) } } }