"""Tests for #1431 / PR #1433 — composer-footer toolsets chip is responsive. The chip must: * Be hidden by default (CSS base rule). * Be shown only at wide composer-footer widths (>= 1100px container query). * Stay hidden on mobile (@media max-width:640px and @container 520px). * Have its visibility controlled by CSS, NOT by JS (single source of truth). * Continue to track state through _applyToolsetsChip() so /api/session/toolsets keeps working for scripted callers regardless of UI visibility. """ import re def _src(name: str) -> str: with open(f"static/{name}") as f: return f.read() class TestToolsetsChipResponsiveCSS: """Visibility is controlled by CSS — base hides, container query reveals.""" def test_base_rule_defaults_chip_to_hidden(self): """The base .composer-toolsets-wrap rule must include display:none.""" css = _src("style.css") # The base rule (outside any @container or @media block) must default-hide m = re.search( r'^\s*\.composer-toolsets-wrap\{[^}]*\}', css, re.MULTILINE, ) assert m, "Base .composer-toolsets-wrap CSS rule must exist" rule = m.group(0) assert "display:none" in rule, ( f"Base rule must default-hide the chip: got {rule!r}" ) def test_wide_container_query_shows_chip(self): """An @container composer-footer (min-width: 1100px) rule must reveal the chip.""" css = _src("style.css") # Find the min-width container query — accept either display:block or display:flex # (we use block to match sibling wraps but either is a valid reveal) m = re.search( r'@container\s+composer-footer\s*\(\s*min-width:\s*1100px\s*\)\s*\{[^}]*\.composer-toolsets-wrap\s*\{[^}]*display:\s*(block|flex)[^}]*\}', css, re.DOTALL, ) assert m, ( "Must have @container composer-footer (min-width: 1100px) rule " "that shows .composer-toolsets-wrap with display:block or display:flex" ) def test_narrow_container_query_keeps_hiding(self): """The existing @container (max-width: 520px) rule must still hide the chip.""" css = _src("style.css") # Look for the existing 520px rule that already hid composer-toolsets-wrap m = re.search( r'@container\s+composer-footer\s*\(\s*max-width:\s*520px\s*\).*?\.composer-toolsets-wrap\s*\{\s*display:\s*none\s*!important', css, re.DOTALL, ) assert m, ( "@container composer-footer (max-width: 520px) must continue to " "hide .composer-toolsets-wrap with !important" ) def test_mobile_viewport_keeps_hiding(self): """The existing @media max-width:640px rule must still hide the chip on mobile.""" css = _src("style.css") m = re.search( r'@media\s*\(\s*max-width:\s*640px\s*\).*?\.composer-toolsets-wrap\s*\{\s*display:\s*none\s*!important', css, re.DOTALL, ) assert m, ( "@media (max-width:640px) must continue to hide " ".composer-toolsets-wrap on mobile viewports" ) class TestToolsetsChipJSDoesNotForceHide: """JS must NOT set display:none directly — CSS owns visibility.""" def test_applyToolsetsChip_does_not_set_display_none(self): """_applyToolsetsChip must not contain wrap.style.display = 'none'.""" js = _src("ui.js") m = re.search(r'function _applyToolsetsChip\([^)]*\)\s*\{.*?\n\}', js, re.DOTALL) assert m, "_applyToolsetsChip function must exist" body = m.group(0) # The PR initially had wrap.style.display = 'none'; we replaced with CSS. assert "wrap.style.display = 'none'" not in body, ( "_applyToolsetsChip must not hardcode display:none — visibility " "is controlled by responsive CSS (#1431)" ) assert 'wrap.style.display = "none"' not in body, ( "_applyToolsetsChip must not hardcode display:none — visibility " "is controlled by responsive CSS (#1431)" ) def test_applyToolsetsChip_clears_inline_style(self): """_applyToolsetsChip must clear inline display so CSS rules can apply.""" js = _src("ui.js") m = re.search(r'function _applyToolsetsChip\([^)]*\)\s*\{.*?\n\}', js, re.DOTALL) assert m, "_applyToolsetsChip function must exist" body = m.group(0) # Either ='' or ="" (clearing inline style) assert ( "wrap.style.display = ''" in body or 'wrap.style.display = ""' in body ), ( "_applyToolsetsChip must clear wrap.style.display so the CSS " "@container query is the single source of truth" ) def test_applyToolsetsChip_still_tracks_state(self): """State tracking must be unchanged — /api/session/toolsets keeps working.""" js = _src("ui.js") assert "_currentSessionToolsets = toolsets" in js, ( "_applyToolsetsChip must continue to update _currentSessionToolsets " "so /api/session/toolsets reflects the active state" ) class TestToolsetsChipHTMLNoInlineDisplay: """index.html must not have inline style='display:none' — CSS owns it.""" def test_html_does_not_force_inline_hide(self): """The composerToolsetsWrap div must not have inline style='display:none'.""" html = _src("index.html") # Find the composerToolsetsWrap element m = re.search(r'