[OCaml] High Intensity Training Online
feat Add Work Sans favicon
Serve an SVG mark and link it from every page head. Cover asset delivery and document integration with web tests. sim: N/A
Changed files
lib/presenter/page_shell.ml
@@ -208,6 +208,12 @@
208
208
"width=device-width,initial-scale=1";
209
209
];
210
210
void "link" [ rel "stylesheet"; href Routes.stylesheet ];
211
Added:
void "link"
212
Added:
[
213
Added:
rel "icon";
214
Added:
Dream_html.string_attr "type" "image/svg+xml";
215
Added:
href Routes.favicon;
216
Added:
];
211
217
];
212
218
tag "body"
213
219
[ Dream_html.string_attr "class" "hito-app" ]
lib/presenter/routes.ml
@@ -39,3 +39,4 @@
39
39
let%path stylesheet = "/assets/hito.css"
40
40
let%path workout_client = "/assets/workout-client.js"
41
41
let%path font = "/assets/fonts/%s"
42
Added:
let%path favicon = "/assets/favicon.svg"
lib/web/assets/favicon.svg
@@ -0,0 +1,6 @@
1
Added:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-labelledby="title description">
2
Added:
<title id="title">Hito</title>
3
Added:
<desc id="description">A blood-red letter H on a white circle.</desc>
4
Added:
<circle cx="64" cy="64" r="60" fill="#fff"/>
5
Added:
<text x="64" y="64" dy="0.35em" text-anchor="middle" font-family="Work Sans, sans-serif" font-size="88" font-weight="700" fill="#8b0000">H</text>
6
Added:
</svg>
lib/web/handlers_assets.ml
@@ -13,6 +13,11 @@
13
13
| Some script -> script
14
14
| None -> failwith "Embedded workout client is missing"
15
15
16
Added:
let favicon =
17
Added:
match Stylesheet.read "favicon.svg" with
18
Added:
| Some favicon -> favicon
19
Added:
| None -> failwith "Embedded favicon.svg is missing"
20
Added:
16
21
(* Embedded assets change whenever the binary is rebuilt, and the URLs carry no
17
22
version, so [no-cache] makes the browser revalidate on every load rather than
18
23
serve a stale copy after a deploy. Cheap: the payloads are small. *)
@@ -47,5 +52,9 @@
47
52
Dream.respond
48
53
~headers:(asset_headers "application/javascript; charset=utf-8")
49
54
workout_client);
55
Added:
Dream_html.get Routes.favicon (fun _ ->
56
Added:
Dream.respond
57
Added:
~headers:(asset_headers "image/svg+xml; charset=utf-8")
58
Added:
favicon);
50
59
Dream_html.get Routes.font (fun _ name -> font name);
51
60
]
test/web/support.ml
@@ -290,3 +290,13 @@
290
290
let _ = record_single "2" "10" "9" in
291
291
let _ = record_pair "3" "0" in
292
292
()
293
Added:
294
Added:
let contains_substring ~needle haystack =
295
Added:
let needle_length = String.length needle in
296
Added:
let haystack_length = String.length haystack in
297
Added:
let rec search offset =
298
Added:
if offset + needle_length > haystack_length then false
299
Added:
else if String.sub haystack offset needle_length = needle then true
300
Added:
else search (offset + 1)
301
Added:
in
302
Added:
search 0
test/web/workout.ml
@@ -72,6 +72,30 @@
72
72
let missing = get c "/assets/fonts/does-not-exist.woff2" in
73
73
Alcotest.(check int)
74
74
"an unknown font is not found" 404 (status missing) );
75
Added:
( "the favicon is served as an SVG with the Hito mark",
76
Added:
`Quick,
77
Added:
fun () ->
78
Added:
let c = client () in
79
Added:
let response = get c "/assets/favicon.svg" in
80
Added:
let markup = body response in
81
Added:
Alcotest.(check int) "serves the favicon" 200 (status response);
82
Added:
Alcotest.(check (option string))
83
Added:
"declares SVG content" (Some "image/svg+xml; charset=utf-8")
84
Added:
(Dream.header response "Content-Type");
85
Added:
Alcotest.(check bool)
86
Added:
"contains the requested H mark" true
87
Added:
(contains_substring
88
Added:
~needle:"font-family=\"Work Sans, sans-serif\"" markup
89
Added:
&& contains_substring
90
Added:
~needle:
91
Added:
"<circle cx=\"64\" cy=\"64\" r=\"60\" fill=\"#fff\"/>"
92
Added:
markup
93
Added:
&& contains_substring ~needle:"fill=\"#8b0000\">H</text>" markup);
94
Added:
let page = body (get c "/login") in
95
Added:
Alcotest.(check bool)
96
Added:
"links the favicon from the document head" true
97
Added:
(contains_substring ~needle:"href=\"/assets/favicon.svg\"" page)
98
Added:
);
75
99
( "beginning before recovery is refused without an override",
76
100
`Quick,
77
101
fun () ->