| 1 | package render_test |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | |
| 7 | "amadan.net/oficina/slides/internal/slides/deck" |
| 8 | "amadan.net/oficina/slides/internal/slides/render" |
| 9 | ) |
| 10 | |
| 11 | // Codex 7, reproduced then fixed: two columns blocks naming each other |
| 12 | // used to vanish from the page with everything under them — every |
| 13 | // member was "claimed", so nothing started the traversal. The ops |
| 14 | // refuse the shape now, but a document can still hold it (two peers' |
| 15 | // concurrent assignments merge into a cycle; a document written past |
| 16 | // the ops), so the renderer appends whatever nothing reached at the |
| 17 | // end, marked, rather than dropping it. |
| 18 | func cyclicDeck(layout string) *deck.Deck { |
| 19 | blocks := []deck.Block{ |
| 20 | {ID: "intro", Type: "text", Region: "body", Text: txt("Reached")}, |
| 21 | {ID: "one", Type: "columns", Region: "body", |
| 22 | Props: map[string]any{"children": []any{"two", "kid"}}}, |
| 23 | {ID: "two", Type: "columns", Region: "body", |
| 24 | Props: map[string]any{"children": []any{"one"}}}, |
| 25 | {ID: "kid", Type: "text", Region: "body", Text: txt("Under the cycle")}, |
| 26 | } |
| 27 | if layout == "canvas" { |
| 28 | for i := range blocks { |
| 29 | blocks[i].Region = "" |
| 30 | blocks[i].Box = box(0.1, 0.1+0.2*float64(i), 0.8, 0.15) |
| 31 | blocks[i].Z = i |
| 32 | } |
| 33 | } |
| 34 | return &deck.Deck{ |
| 35 | Meta: deck.Meta{Theme: "paper"}, |
| 36 | Slides: []deck.Slide{{ID: "s", Layout: layout, Template: "blank", Blocks: blocks}}, |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | func TestBlocksNothingReachesRenderAtTheEndMarked(t *testing.T) { |
| 41 | for _, layout := range []string{"flow", "canvas"} { |
| 42 | t.Run(layout, func(t *testing.T) { |
| 43 | out, err := render.Slide(cyclicDeck(layout), 0, render.Desktop) |
| 44 | if err != nil { |
| 45 | t.Fatal(err) |
| 46 | } |
| 47 | s := string(out) |
| 48 | for _, id := range []string{"intro", "one", "two", "kid"} { |
| 49 | if n := strings.Count(s, `data-id="`+id+`"`); n != 1 { |
| 50 | t.Errorf("%s appears %d times, want 1:\n%s", id, n, s) |
| 51 | } |
| 52 | } |
| 53 | if !strings.Contains(s, "Under the cycle") { |
| 54 | t.Errorf("the text under the cycle is gone:\n%s", s) |
| 55 | } |
| 56 | // After the reached content, and marked: the cycle's first |
| 57 | // member is the root of the late pass, everything under it |
| 58 | // renders inside it, and every block nothing reached says |
| 59 | // so — the reached one does not. |
| 60 | if strings.Index(s, `data-id="intro"`) > strings.Index(s, `data-id="one"`) { |
| 61 | t.Errorf("the unreached blocks come before the reached one:\n%s", s) |
| 62 | } |
| 63 | if strings.Index(s, `data-id="one"`) > strings.Index(s, `data-id="two"`) || |
| 64 | strings.Index(s, `data-id="two"`) > strings.Index(s, `data-id="kid"`) { |
| 65 | t.Errorf("the cycle does not render from its first member down:\n%s", s) |
| 66 | } |
| 67 | if got := strings.Count(s, `data-unreached="true"`); got != 3 { |
| 68 | t.Errorf("the three unreached blocks are marked; got %d marks:\n%s", got, s) |
| 69 | } |
| 70 | intro := s[strings.Index(s, `data-id="intro"`):] |
| 71 | intro = intro[:strings.Index(intro, ">")] |
| 72 | if strings.Contains(intro, "data-unreached") { |
| 73 | t.Errorf("the reached block is marked unreached: %s", intro) |
| 74 | } |
| 75 | if layout == "flow" && !strings.Contains(s, `data-region="unreached"`) { |
| 76 | t.Errorf("a flow slide wraps the late blocks in their own region:\n%s", s) |
| 77 | } |
| 78 | }) |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | // A well-formed slide has nothing unreached, so nothing is marked and |
| 83 | // no extra region appears — the fixture goldens hold that for every |
| 84 | // template, and this says it in one line. |
| 85 | func TestAWellFormedSlideHasNoUnreachedRegion(t *testing.T) { |
| 86 | out, err := render.Slide(fixture(), 2, render.Desktop) |
| 87 | if err != nil { |
| 88 | t.Fatal(err) |
| 89 | } |
| 90 | if strings.Contains(string(out), "unreached") { |
| 91 | t.Errorf("a well-formed two-column slide renders an unreached region:\n%s", out) |
| 92 | } |
| 93 | } |
| 94 | |