| 1 | package render |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | |
| 7 | "amadan.net/oficina/slides/internal/slides/deck" |
| 8 | ) |
| 9 | |
| 10 | // imageDeck is one slide with one image block and an image background, |
| 11 | // both naming the same registered asset. |
| 12 | func imageDeck(assets map[string]deck.Asset) *deck.Deck { |
| 13 | return &deck.Deck{ |
| 14 | Meta: deck.Meta{Theme: "paper"}, |
| 15 | Assets: assets, |
| 16 | Slides: []deck.Slide{{ |
| 17 | ID: "slide001aaaa", Layout: "flow", Template: "image-full", |
| 18 | Background: deck.Background{Kind: "image", Value: "asset0001aaa"}, |
| 19 | Blocks: []deck.Block{{ |
| 20 | ID: "b001aaaaaaaa", Type: "image", Region: "media", |
| 21 | Props: map[string]any{"asset": "asset0001aaa", "alt": "A chart"}, |
| 22 | }}, |
| 23 | }}, |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | // The renderer writes the deck's own REF into the src, under whichever |
| 28 | // surface's base the caller passed — a slide is drawn for four |
| 29 | // audiences authenticated four different ways, and the picture has to |
| 30 | // be fetchable by whoever is looking at it (§2.7). |
| 31 | func TestImageSrcIsTheRefUnderTheSurfacesOwnBase(t *testing.T) { |
| 32 | d := imageDeck(map[string]deck.Asset{ |
| 33 | "asset0001aaa": {Ref: "aa01bb02cc03dd04", Name: "chart.png", Width: 800, Height: 600, Alt: "A chart"}, |
| 34 | }) |
| 35 | |
| 36 | for _, base := range []string{"/d/k7m3/assets", "/s/tok3n/assets", "/p/sess1/assets"} { |
| 37 | html, err := SlideWith(d, 0, Desktop, Options{AssetBase: base}) |
| 38 | if err != nil { |
| 39 | t.Fatalf("SlideWith(%q): %v", base, err) |
| 40 | } |
| 41 | body := string(html) |
| 42 | want := `src="` + base + `/aa01bb02cc03dd04"` |
| 43 | if !strings.Contains(body, want) { |
| 44 | t.Errorf("base %q: no %s in\n%s", base, want, body) |
| 45 | } |
| 46 | if !strings.Contains(body, "url("+base+"/aa01bb02cc03dd04)") { |
| 47 | t.Errorf("base %q: the background image does not follow the same base:\n%s", base, body) |
| 48 | } |
| 49 | // The deck's own id for the asset never appears in a URL: it |
| 50 | // means something inside this document and nothing at all to |
| 51 | // the route that serves the bytes. |
| 52 | if strings.Contains(body, base+"/asset0001aaa") { |
| 53 | t.Errorf("base %q: the asset ID leaked into a URL:\n%s", base, body) |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | // A trailing slash on the base is not two slashes in the src. |
| 58 | html, _ := SlideWith(d, 0, Desktop, Options{AssetBase: "/d/k7m3/assets/"}) |
| 59 | if strings.Contains(string(html), "//aa01bb02") { |
| 60 | t.Errorf("a trailing slash doubled up:\n%s", html) |
| 61 | } |
| 62 | |
| 63 | // No options at all still renders something, rather than an <img> |
| 64 | // with no src. |
| 65 | plain, err := Slide(d, 0, Desktop) |
| 66 | if err != nil { |
| 67 | t.Fatalf("Slide: %v", err) |
| 68 | } |
| 69 | if !strings.Contains(string(plain), `src="`+DefaultAssetBase+`/aa01bb02cc03dd04"`) { |
| 70 | t.Errorf("Slide without Options did not fall back to %s:\n%s", DefaultAssetBase, plain) |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // An asset id nothing registered — a block whose asset was removed, a |
| 75 | // document a peer left half-written, a ref that could not survive a URL |
| 76 | // — is an empty frame, never an <img> pointing somewhere that cannot |
| 77 | // resolve. |
| 78 | func TestAnUnregisteredAssetIsAnEmptyFrame(t *testing.T) { |
| 79 | for name, assets := range map[string]map[string]deck.Asset{ |
| 80 | "no assets at all": {}, |
| 81 | "a different asset": {"asset0002bbb": {Ref: "ee05ff06"}}, |
| 82 | "a ref that is empty": {"asset0001aaa": {Ref: ""}}, |
| 83 | "a ref with a slash": {"asset0001aaa": {Ref: "decks/k7m/x.png"}}, |
| 84 | "a ref with a scheme": {"asset0001aaa": {Ref: "https://elsewhere.test/x.png"}}, |
| 85 | } { |
| 86 | html, err := SlideWith(imageDeck(assets), 0, Desktop, Options{AssetBase: "/d/k7m3/assets"}) |
| 87 | if err != nil { |
| 88 | t.Fatalf("%s: %v", name, err) |
| 89 | } |
| 90 | body := string(html) |
| 91 | if !strings.Contains(body, `data-missing-asset="true"`) { |
| 92 | t.Errorf("%s: the image block is not marked missing:\n%s", name, body) |
| 93 | } |
| 94 | if strings.Contains(body, "<img") { |
| 95 | t.Errorf("%s: an <img> was drawn anyway:\n%s", name, body) |
| 96 | } |
| 97 | if strings.Contains(body, "--slides-bg-image") { |
| 98 | t.Errorf("%s: the background still points at something:\n%s", name, body) |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |