oficina / slides Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build browser
2
3package slidestest
4
5import (
6 "math"
7 "strings"
8 "testing"
9
10 "github.com/chromedp/chromedp"
11)
12
13// TestSlideStylesSurviveTheProductionCSP: Slides in Chromium behind the
14// production serving stack (newRig: rastrillo's security headers and
15// Slides' CSP). A slide's theme reaches the slide, the canvas slide
16// places its heading where the deck put it, and the viewer's own
17// <style> lays out the pager.
18//
19// Every one of those is inline: the theme is custom properties on the
20// slide's style attribute, the box is left/top/width/height on the
21// block's, and the pager is a <style> element in viewer.html. Under
22// rastrillo fb806a2's default style-src the browser refuses them all —
23// the attribute stays in the DOM but declares nothing, the canvas
24// heading falls back into the slide's corner, the pager stacks. Checked
25// here by what the page looks like, because the rig does not fail on a
26// CSP report by itself.
27func TestSlideStylesSurviveTheProductionCSP(t *testing.T) {
28 rig, mailer := newRig(t)
29 browserSignIn(t, rig, mailer)
30 var deck string
31 rig.Run(chromedp.Evaluate(`(document.querySelector('a[href^="/d/"]')?.getAttribute('href') ?? '')`, &deck))
32 if deck == "" {
33 t.Fatal("the deck list links no deck")
34 }
35
36 var flow struct {
37 Attr, Applied, Display string
38 }
39 rig.Run(
40 chromedp.Navigate(rig.Origin+deck+"?slide=1"),
41 chromedp.WaitVisible(`[slides-slide]`, chromedp.ByQuery),
42 chromedp.Evaluate(`(() => {
43 const s = document.querySelector('[slides-slide]');
44 return {
45 Attr: s.getAttribute('style') ?? '',
46 Applied: s.style.getPropertyValue('--slides-font-heading'),
47 Display: getComputedStyle(document.querySelector('[slides-pager]')).display,
48 };
49 })()`, &flow),
50 )
51 if !strings.Contains(flow.Attr, "--slides-font-heading:") {
52 t.Fatalf("the slide carries no theme on its style attribute: %q", flow.Attr)
53 }
54 if flow.Applied == "" {
55 t.Error("the slide's theme declares nothing: its style attribute was refused")
56 }
57 if flow.Display != "flex" {
58 t.Errorf("the pager is display %q, want flex: the viewer's <style> was refused", flow.Display)
59 }
60
61 // The canvas slide: its heading's box is percentages of the slide on
62 // the heading's own style attribute.
63 var box struct {
64 Attr string
65 Left, Top, SlideLeft, SlideTop, W, H float64
66 WantLeft, WantTop float64
67 }
68 rig.Run(
69 chromedp.Navigate(rig.Origin+deck+"?slide=8"),
70 chromedp.WaitVisible(`[slides-slide][data-layout="canvas"] [data-block="heading"]`, chromedp.ByQuery),
71 chromedp.Evaluate(`(() => {
72 const slide = document.querySelector('[slides-slide]');
73 const heading = slide.querySelector('[data-block="heading"]');
74 const s = slide.getBoundingClientRect(), h = heading.getBoundingClientRect();
75 const attr = heading.getAttribute('style') ?? '';
76 const pct = (k) => parseFloat((attr.match(new RegExp('(?:^|;)' + k + ':([0-9.]+)%')) ?? [])[1]);
77 return {Attr: attr, Left: h.left, Top: h.top, SlideLeft: s.left, SlideTop: s.top, W: s.width, H: s.height, WantLeft: pct('left'), WantTop: pct('top')};
78 })()`, &box),
79 )
80 if math.IsNaN(box.WantLeft) || math.IsNaN(box.WantTop) || box.WantTop == 0 {
81 t.Fatalf("the canvas heading carries no box on its style attribute: %q", box.Attr)
82 }
83 wantLeft := box.SlideLeft + box.WantLeft/100*box.W
84 wantTop := box.SlideTop + box.WantTop/100*box.H
85 if math.Abs(box.Left-wantLeft) > 2 || math.Abs(box.Top-wantTop) > 2 {
86 t.Errorf("the canvas heading is at (%.1f, %.1f), want (%.1f, %.1f) from %q: its style attribute was refused", box.Left, box.Top, wantLeft, wantTop, box.Attr)
87 }
88}
89