summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_sidebar_legal_measured.py
blob: faaaec701a42ec4875f91bd07ccb03bda4f25610 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""
The legal link at the foot of the sidebar, measured in a browser.

It is pinned to the bottom of the window, not of the page: the sidebar sticks
under the navigation bar at the window's height, so on a long file list the
link is still on screen. And the music bar, pinned to the bottom of the window
as well, must not cover it — the bar publishes its height as `--music-bar-h`
(sticky.js, as the toolbars do), and the sidebar stops above it. That value
is set by hand here; what is under test is the stylesheet's arithmetic.

On a phone the sidebar is the slide-out panel, measured open.
"""

import json
import shutil
import subprocess
import textwrap
from pathlib import Path

import pytest

HARNESS = Path(__file__).parent / "harness" / "layout_probe.py"
STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static"

pytestmark = pytest.mark.skipif(
    shutil.which("google-chrome") is None or not (STATIC / "style.css").exists(),
    reason="Chrome or the SPA stylesheet is not available")

# The probe's frames are this tall.
WINDOW_H = 740
MUSIC_BAR_H = 64
WIDTHS = [390, 1024, 1440]


def _page(music: bool) -> str:
    groups = "".join(
        f'<a class="sidebar-item sidebar-group" href="#/"><span class="si-head">'
        f'<span class="sidebar-item-name">Some group {i}</span></span></a>' for i in range(4))
    bar = (f'<div class="music-player-bar" style="height:{MUSIC_BAR_H}px;'
           f'box-sizing:border-box">a track</div>') if music else ""
    style = f' style="--music-bar-h:{MUSIC_BAR_H}px"' if music else ""
    return textwrap.dedent(f"""
      <div id="app"{style}>
      <nav class="nav"><div class="nav-left"><a class="nav-brand" href="#/">MeshBay</a></div>
        <div class="nav-right"><button class="nav-btn">someone</button></div></nav>
      <div class="layout">
        <aside class="sidebar open">
          <div class="sidebar-section"><div class="sidebar-heading">My groups</div>{groups}</div>
          <a class="sidebar-legal" href="/legal/">Legal information</a>
        </aside>
        <main class="main"><h2>A long list</h2><div style="height:3000px"></div></main>
      </div>
      {bar}
      </div>
    """)


def _measure(tmp_path_factory, music: bool) -> dict:
    fragment = tmp_path_factory.mktemp("sidebar") / "fragment.html"
    fragment.write_text(_page(music), encoding="utf-8")
    proc = subprocess.run(
        ["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS),
         str(fragment), ".sidebar-legal", ".sidebar", ".music-player-bar"],
        capture_output=True, text=True, timeout=180)
    assert proc.returncode == 0, f"probe failed: {proc.stdout}{proc.stderr}"
    out = json.loads(proc.stdout)
    assert "error" not in out, f"no measurement: {out}"
    return out


@pytest.fixture(scope="module")
def plain(tmp_path_factory):
    return _measure(tmp_path_factory, music=False)


@pytest.fixture(scope="module")
def playing(tmp_path_factory):
    return _measure(tmp_path_factory, music=True)


@pytest.mark.parametrize("width", WIDTHS)
def test_the_link_is_at_the_bottom_of_the_window(plain, width):
    """Not at the bottom of a 3000px page, where nobody would find it."""
    link = plain[str(width)]["boxes"][".sidebar-legal"]
    assert link is not None, "no legal link"
    bottom = link["top"] + link["height"]
    assert WINDOW_H - 8 <= bottom <= WINDOW_H, (
        f"at {width} px the link ends at {bottom}, the window at {WINDOW_H}")
    assert link["offLeft"] == 0 and link["offRight"] == 0


@pytest.mark.parametrize("width", WIDTHS)
def test_the_music_bar_does_not_cover_it(playing, width):
    link = playing[str(width)]["boxes"][".sidebar-legal"]
    bar = playing[str(width)]["boxes"][".music-player-bar"]
    assert link is not None and bar is not None
    bottom = link["top"] + link["height"]
    assert bottom <= bar["top"] + 1, (
        f"at {width} px the link ends at {bottom} and the music bar starts at {bar['top']}")
    assert bottom >= bar["top"] - 8, (
        f"at {width} px the link floats {bar['top'] - bottom} px above the music bar")