"""UI tests: design fidelity, escaping, and the action bridge.
The fidelity tests here are the cheap half of the check -- they assert the
stylesheets reach the page and the design's own values are used. The other
half (measuring the rendered page in a browser) cannot run in CI and is done
by hand against the deployed Space.
"""
from __future__ import annotations
from pathlib import Path
import pytest
from src import atlas
from bit_ui import bridge, icons, theme
from src.ui import chrome, shell
from tests.conftest import make_row
# --------------------------------------------------------------------------
# The design system's stylesheets must actually reach the page
# --------------------------------------------------------------------------
#
# This is the guard the design-port notes ask for: it is easy to vendor the
# CSS files and then wire up only some of them, which leaves the app looking
# *approximately* right in a way that is hard to name. Each assertion is keyed
# on a rule only that file contains.
@pytest.mark.parametrize("marker,source", [
("--bg-canvas", "colors.css"),
("--text-2xs", "typography.css"),
("--space-", "spacing.css"),
("border-radius", "base.css"),
])
def test_every_design_stylesheet_reaches_the_page(marker, source):
css = theme.full_css()
assert marker in css, f"{source} did not reach the page (missing {marker})"
def test_fonts_are_declared():
css = theme.full_css()
assert "@font-face" in css
assert "Styrene A" in css
assert "Mac Minecraft" in css
def test_theme_uses_the_designs_dense_type_scale():
"""Gradio's 16px default would be a full step too large everywhere."""
built = theme.bit_theme()
assert built.body_text_size == "10px"
assert built.block_radius == "0px"
def test_bridge_transport_elements_are_hidden_not_removed():
"""`visible=False` would delete them from the DOM and break the bridge."""
css = theme.full_css()
assert "#bit-action" in css and "#bit-trigger" in css
assert "clip:rect(0,0,0,0)" in css.replace(" ", "")
# --------------------------------------------------------------------------
# Escaping -- model cards are written by strangers
# --------------------------------------------------------------------------
XSS = '
'
def test_red_flags_are_escaped_in_badges():
row = make_row("evil/model", red_flags=[XSS])
html = shell._badges(row)
assert "
', red_flags=[XSS],
training_data_summary=XSS)
built = atlas.Index(pd.DataFrame([nasty]))
built.dataset_repo = "x/y"
state = atlas.default_state()
view = {"rows": built.rows, "hidden": 0, "tape": [], "trending": [],
"links": {}}
html = shell.page(built, state, view)
assert "" not in html
assert "
, an element that forces height from vh
feeds back into the height the parent sets, and ratchets. This is the check
that keeps it fixed.
"""
from bit_ui import nav as bit_nav
state = dict(atlas.default_state(), sel="ProsusAI/finbert")
view = {"rows": index.rows, "matched": len(index.rows), "truncated": 0,
"hidden": 1, "tape": [], "trending": [], "links": {},
"nav": bit_nav.default_document(), "palette_groups": []}
assert theme.find_forced_vh(shell.page(index, state, view)) == []
def test_gradio_own_main_padding_is_reset():
"""Gradio ships a second with padding:16px 32px around ours."""
css = chrome.full_css()
assert "main.fillable" in css
def test_sidebar_toggle_costs_no_round_trip(index):
"""Collapsing is a client-side attribute flip, not a server action.
Every action re-renders the whole page: 1.16 MB here, 95% of it the model
table. Putting that behind a purely visual toggle measured at 1.5-2.2s.
"""
from bit_ui import nav as bit_nav
view = {"rows": [], "matched": 0, "truncated": 0, "hidden": 0, "tape": [],
"trending": [], "links": {}, "nav": bit_nav.default_document(),
"palette_groups": []}
html = shell.page(index, atlas.default_state(), view)
assert "data-bit-sidebar-toggle" in html
assert "sidebar:toggle" not in html
assert "sidebar" not in chrome.ALLOWED_KEYS, (
"the sidebar action is back on the allow-list")
def test_header_and_sidebar_are_sticky(index):
from bit_ui import nav as bit_nav
css = chrome.full_css()
assert ".bit-header" in css and "position: sticky" in css
view = {"rows": [], "matched": 0, "truncated": 0, "hidden": 0, "tape": [],
"trending": [], "links": {}, "nav": bit_nav.default_document(),
"palette_groups": []}
assert 'class="bit-header"' in shell.page(index, atlas.default_state(), view)
def test_search_bar_is_live_not_enter_only(index):
"""The bug: fields sent `key=value`, which parse_action drops on the floor,
so the search box did nothing at all from the browser."""
html = shell.title_row(index, atlas.default_state(), shown=5)
assert 'data-bit-live="q"' in html
assert "⌘K" in html
def test_every_chrome_action_is_on_the_allow_list(index):
"""The sidebar and dialogs emit through this Space's emitter."""
import re
from bit_ui import dialogs as bit_dialogs
from bit_ui import nav as bit_nav
doc = bit_nav.default_document()
html = "".join([
shell.page(index, atlas.default_state(),
{"rows": [], "matched": 0, "truncated": 0, "hidden": 0,
"tape": [], "trending": [], "links": {}, "nav": doc,
"palette_groups": []}),
bit_dialogs.contact(doc, {"contact_form_open": True}, chrome.emit),
bit_dialogs.coming_soon(bit_nav.find(doc, "Dispatch"), doc, chrome.emit),
])
emitted = set(re.findall(r'data-bit="([^:"]+):', html))
emitted |= set(re.findall(r'data-bit-(?:input|live)="([^="]+)"', html))
unknown = emitted - chrome.ALLOWED_KEYS
assert not unknown, f"chrome emits actions the bridge rejects: {unknown}"
def test_the_app_serves_this_spaces_own_css_not_just_the_shared_css():
"""`theme.full_css()` is bit-ui's CSS only and silently drops the Atlas's.
This shipped once: the table had just been converted to CSS classes whose
rules live in ATLAS_CSS, and none of them reached the page. Everything
still rendered, just unstyled -- exactly the kind of break that looks like
a design regression rather than a wiring mistake.
"""
import app as module
css = module.chrome.full_css()
for marker in (".bit-row", ".bit-header", ".bit-cell", ".bit-tag"):
assert marker in css, f"{marker} is missing from the app's CSS"
source = (Path(module.__file__).read_text())
assert "css=chrome.full_css()" in source, (
"app.py is not serving this Space's CSS")
assert "css=theme.full_css()" not in source
def test_every_class_the_table_renders_has_a_rule():
"""A class with no rule is invisible styling debt."""
import re
import app as module
from bit_ui import nav as bit_nav
index = module.INDEX
css = module.chrome.full_css()
state = atlas.default_state()
view = {"rows": index.rows[:3], "matched": 3, "truncated": 0, "hidden": 0,
"tape": [], "trending": [], "links": {},
"nav": bit_nav.default_document(), "palette_groups": []}
html = shell.page(index, state, view)
rendered = set()
for attr in re.findall(r'class="([^"]+)"', html):
rendered.update(c for c in attr.split() if c.startswith("bit-"))
missing = sorted(c for c in rendered if f".{c}" not in css)
assert not missing, f"classes rendered with no CSS rule: {missing}"