oficina / calendar Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build browser
2
3package calendartest
4
5import (
6 "context"
7 "io"
8 "log/slog"
9 "net"
10 "net/http"
11 "os/exec"
12 "path/filepath"
13 "regexp"
14 "testing"
15 "time"
16
17 calendar "amadan.net/oficina/calendar/internal/calendar"
18)
19
20// TestBookingInARealBrowser drives the stranger's whole journey in
21// Chromium with JavaScript OFF — the only proof the scriptless floor
22// is real — and the host's availability pane with it on.
23//
24// The hold and the confirm are two node invocations because the link
25// between them is in a mail only the Go side can read.
26func TestBookingInARealBrowser(t *testing.T) {
27 ln, err := net.Listen("tcp", "127.0.0.1:0")
28 if err != nil {
29 t.Fatalf("listen: %v", err)
30 }
31 origin := "http://" + ln.Addr().String()
32
33 app, err := calendar.New(calendar.Config{
34 DBPath: filepath.Join(t.TempDir(), "calendar.db"),
35 Env: "development",
36 Origin: origin,
37 Logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
38 })
39 if err != nil {
40 t.Fatalf("calendar.New: %v", err)
41 }
42 t.Cleanup(func() { app.Close() })
43 srv := &http.Server{Handler: app.Handler()}
44 go func() { _ = srv.Serve(ln) }()
45 t.Cleanup(func() { _ = srv.Close() })
46
47 session := signInOverHTTP(t, app, origin, "host@example.com")
48
49 // The host, the handle and a published page, through the store —
50 // the browser passes are about the stranger's side.
51 ctx := context.Background()
52 st := app.StoreForTest()
53 host := app.PersonByAddressForTest(t, "host@example.com")
54 if err := st.SaveHandle(ctx, host.ID, "paul"); err != nil {
55 t.Fatal(err)
56 }
57 ws, _ := calendar.ParseAvailability("every day 9-5")
58 page, err := st.CreateBookingPage(ctx, host.ID, calendar.BookingPageInput{
59 Title: "Quick chat", Description: "Half an hour.", Availability: "every day 9-5",
60 DurationMin: 30, HorizonDays: 30}, ws)
61 if err != nil {
62 t.Fatal(err)
63 }
64 if err := st.PublishBookingPage(ctx, host.ID, page.ID, true); err != nil {
65 t.Fatal(err)
66 }
67 public := "/book/paul/" + page.Code
68
69 node := func(args ...string) {
70 t.Helper()
71 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
72 defer cancel()
73 cmd := exec.CommandContext(ctx, "node", append([]string{"test/browser/booking.mjs"}, args...)...)
74 cmd.Dir = repoRootForBrowser(t)
75 out, err := cmd.CombinedOutput()
76 t.Logf("node %s:\n%s", args[0], out)
77 if err != nil {
78 t.Fatalf("the browser checks failed: %v", err)
79 }
80 }
81
82 node("hold", origin, public)
83 body, err := app.LastMailForTest()
84 if err != nil {
85 t.Fatal(err)
86 }
87 link := regexp.MustCompile(`/book/m/[0-9a-f]{64}`).FindString(body)
88 if link == "" {
89 t.Fatalf("no manage link in the hold mail:\n%s", body)
90 }
91 node("confirm", origin, link)
92 node("pane", origin, session.Name, session.Value)
93}
94