| 1 | package movementtest |
| 2 | |
| 3 | import ( |
| 4 | "html/template" |
| 5 | "net/http" |
| 6 | "strings" |
| 7 | "testing" |
| 8 | |
| 9 | movement "amadan.net/carlos/movement/internal/movement" |
| 10 | ) |
| 11 | |
| 12 | // The all-in page hands its reader the model's output and then says so. The |
| 13 | // disclosure is the joke, so it has to be visible, on that page, and nowhere |
| 14 | // else — and since 2026-09-03 it sits AFTER the text it describes, because a |
| 15 | // note about copy that arrives before the copy is just a preamble. |
| 16 | func TestTheDisclosureFollowsTheAllInReplyAndAppearsNowhereElse(t *testing.T) { |
| 17 | h := newApp(t) |
| 18 | |
| 19 | line := movement.T("stance.enthusiast.disclosure") |
| 20 | |
| 21 | rec := get(t, h, "/all-in") |
| 22 | if rec.Code != http.StatusOK { |
| 23 | t.Fatalf("GET /all-in: %d", rec.Code) |
| 24 | } |
| 25 | body := rec.Body.String() |
| 26 | |
| 27 | at := strings.Index(body, line) |
| 28 | if at < 0 { |
| 29 | t.Fatal("the disclosure is missing from the all-in page") |
| 30 | } |
| 31 | |
| 32 | // After the reply, not before it: the last paragraph of the message has |
| 33 | // to appear earlier in the document than the note about it. |
| 34 | // |
| 35 | // Compared escaped, because that is what reaches the page — the copy has |
| 36 | // apostrophes and html/template renders them as ', so a raw |
| 37 | // comparison fails on correct output. |
| 38 | msg := movement.Paragraphs("stance.enthusiast.message") |
| 39 | var esc strings.Builder |
| 40 | template.HTMLEscape(&esc, []byte(msg[len(msg)-1])) |
| 41 | |
| 42 | if end := strings.Index(body, esc.String()); end < 0 { |
| 43 | t.Fatal("the all-in reply is missing its last paragraph") |
| 44 | } else if end > at { |
| 45 | t.Error("the disclosure appears before the text it describes") |
| 46 | } |
| 47 | |
| 48 | for _, other := range []string{"/no-ai", "/uneasy"} { |
| 49 | if strings.Contains(get(t, h, other).Body.String(), line) { |
| 50 | t.Errorf("%s carries the all-in page's disclosure", other) |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // Both fields say what goes in them. A ruled underline with nothing in it |
| 56 | // reads as a rule, not as somewhere to type. |
| 57 | func TestFieldsCarryPlaceholders(t *testing.T) { |
| 58 | h := newApp(t) |
| 59 | body := get(t, h, "/uneasy").Body.String() |
| 60 | |
| 61 | for _, id := range []string{"form.email.placeholder", "form.background.placeholder"} { |
| 62 | if !strings.Contains(body, movement.T(id)) { |
| 63 | t.Errorf("%s never reaches the form", id) |
| 64 | } |
| 65 | } |
| 66 | if strings.Count(body, "placeholder=") < 2 { |
| 67 | t.Error("both the address and the background field need a placeholder") |
| 68 | } |
| 69 | } |
| 70 | |