oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1package home
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "strings"
7 "testing"
8)
9
10// The door's words are estilo's chrome.English, the one copy set every
11// app's door renders (Paul, 2026-10-01: the submit reads "Sign in", never
12// "Continue" or "Email me a link"). They are pinned here as literals, not
13// read back from chrome.English, so a change upstream shows up as a red
14// test on the hub rather than as silently different copy.
15const (
16 doorHelp = "We’ll email you a link that signs you in. It works once and expires in 15 minutes."
17 doorSentBody = "If that address can sign in here, we’ve sent it a link. It works once and expires in 15 minutes."
18)
19
20func getCredential(t *testing.T, app *testApp, path string) string {
21 t.Helper()
22 req := httptest.NewRequest(http.MethodGet, path, nil)
23 req.Host = credentialHost
24 w := httptest.NewRecorder()
25 app.Router().ServeHTTP(w, req)
26 if w.Code != http.StatusOK {
27 t.Fatalf("GET %s: %d", path, w.Code)
28 }
29 return w.Body.String()
30}
31
32func mustContain(t *testing.T, page, body string, wants ...string) {
33 t.Helper()
34 for _, want := range wants {
35 if !strings.Contains(body, want) {
36 t.Errorf("%s: missing %q", page, want)
37 }
38 }
39}
40
41func mustNotContain(t *testing.T, page, body string, nots ...string) {
42 t.Helper()
43 for _, not := range nots {
44 if strings.Contains(body, not) {
45 t.Errorf("%s: still says %q", page, not)
46 }
47 }
48}
49
50func TestDoorSpeaksTheSuiteCopy(t *testing.T) {
51 app := newTestApp(t)
52
53 form := getCredential(t, app, "/signin")
54 mustContain(t, "/signin", form,
55 "<title>Sign in · Oficina</title>",
56 "<h1>Sign in</h1>",
57 ">Email address</label>",
58 doorHelp,
59 `<button type="submit" rst-btn="primary">Sign in</button>`)
60 mustNotContain(t, "/signin", form, ">Continue<", "Email me a link", "Home")
61
62 sent := getCredential(t, app, "/signin/sent")
63 mustContain(t, "/signin/sent", sent,
64 "<title>Check your email · Oficina</title>",
65 "<h1>Check your email</h1>",
66 doorSentBody,
67 `<a href="/signin">Use a different address</a>`)
68 mustNotContain(t, "/signin/sent", sent, "fifteen", "Check your mail", "Home")
69
70 failed := redeem(t, app, "not-a-token").Body.String()
71 mustContain(t, "signin failed", failed, "Sign-in links work once and expire after 15 minutes.")
72 mustNotContain(t, "signin failed", failed, "fifteen")
73
74 f := newMountedChooser(t)
75 out := f.request("GET", "/signed-out", "", "", nil)
76 if out.Code != http.StatusOK {
77 t.Fatalf("GET /signed-out: %d", out.Code)
78 }
79 mustContain(t, "/signed-out", out.Body.String(),
80 "<title>Signed out · Oficina</title>",
81 "<h1>You’re signed out</h1>",
82 "You’ve signed out of Oficina on this device.",
83 `rst-btn="primary">Sign in</a>`)
84 mustNotContain(t, "/signed-out", out.Body.String(), "You are signed out", "Home")
85}
86
87// The hub builds its own signin.Flow, so it sets the email itself: the
88// suite's subject, and a body that says how long the link lasts rather
89// than keymaildev's default "expires shortly".
90func TestSigninEmailSpeaksTheSuiteCopy(t *testing.T) {
91 app := newTestApp(t)
92 signIn(t, app, "someone@example.com")
93 mail, ok := app.mailer.last()
94 if !ok {
95 t.Fatal("no mail was sent")
96 }
97 if mail.Subject != "Sign in to Oficina" {
98 t.Errorf("subject = %q", mail.Subject)
99 }
100 for _, want := range []string{"Here’s your link to sign in to Oficina:", Origin + linkPath + "?token=", "It works once and expires in 15 minutes."} {
101 if !strings.Contains(mail.Body, want) {
102 t.Errorf("body missing %q:\n%s", want, mail.Body)
103 }
104 }
105 if strings.Contains(mail.Body, "shortly") || strings.Contains(mail.Body, "Home") {
106 t.Errorf("body is not the suite's copy:\n%s", mail.Body)
107 }
108}
109
110// Never name the hub to people (Paul, 2026-09-23): the suite is Oficina.
111func TestHubNeverCallsItselfHome(t *testing.T) {
112 f := newMountedChooser(t)
113 page := f.request("GET", "/", "", "", f.session(t, chooserPerson))
114 mustNotContain(t, "directory", page.Body.String(), "to Home")
115 for _, app := range directoryCatalog(Runtime{}) {
116 if strings.Contains(app.Status, "Home") {
117 t.Errorf("%s: status %q names Home", app.Name, app.Status)
118 }
119 }
120 files, err := templatesFS.ReadDir("templates")
121 if err != nil {
122 t.Fatal(err)
123 }
124 for _, e := range files {
125 b, err := templatesFS.ReadFile("templates/" + e.Name())
126 if err != nil {
127 t.Fatal(err)
128 }
129 for _, bad := range []string{"Back to Home", "Return to Home", "to Home"} {
130 if strings.Contains(string(b), bad) {
131 t.Errorf("%s says %q", e.Name(), bad)
132 }
133 }
134 }
135}
136