Spaces:
Sleeping
Sleeping
| import inspect | |
| import json | |
| from pathlib import Path | |
| from PIL import Image | |
| import app | |
| from sovereign_bench.models import AgentTurn, EvidenceItem, JurorVote, TrialEvent, Verdict | |
| OLD_CARD_CLASSES = [ | |
| "paper-panel", | |
| "juror-panel", | |
| "mind-panel", | |
| "empty-state", | |
| "trial-downloads", | |
| ] | |
| def _event_with_lower_tab_data() -> TrialEvent: | |
| evidence = EvidenceItem( | |
| id="E1", | |
| title="Ledger entry", | |
| source="Archive", | |
| excerpt="A short exhibit excerpt.", | |
| supports="claimant", | |
| reliability=0.82, | |
| note="Useful but incomplete.", | |
| ) | |
| vote = JurorVote( | |
| juror="Karl Marx", | |
| persona=app.JUROR_PERSONAS["Karl Marx"], | |
| vote="liable", | |
| reason="The exhibit supports the claim.", | |
| evidence_ids=["E1"], | |
| ) | |
| return TrialEvent( | |
| phase="deliberation", | |
| title="Jury weighs the record", | |
| body="The jury reviews the record.", | |
| turns=[ | |
| AgentTurn( | |
| agent="Nemotron Jury", | |
| role="juror panel", | |
| content="The jurors compare E1 and state their votes.", | |
| model="nvidia/Nemotron-Orchestrator-8B", | |
| confidence=0.84, | |
| input="SYSTEM:\nYou are the jury.\n\nUSER:\nWeigh E1 and explain the vote.", | |
| ) | |
| ], | |
| evidence=[evidence], | |
| votes=[vote], | |
| trace={"mode": "test"}, | |
| ) | |
| def _speaker_event(agent: str, phase: str = "questions") -> TrialEvent: | |
| return TrialEvent( | |
| phase=phase, | |
| title=f"{agent} speaks", | |
| body="A single speaker takes the floor.", | |
| turns=[ | |
| AgentTurn( | |
| agent=agent, | |
| role="test speaker", | |
| content=f"{agent} has the visible floor.", | |
| model="test-model", | |
| confidence=0.9, | |
| input="SYSTEM:\nTest prompt.", | |
| ) | |
| ], | |
| ) | |
| def _verdict_event(finding: str = "liable") -> TrialEvent: | |
| return TrialEvent( | |
| phase="verdict", | |
| title="The Court Announces Judgment", | |
| body="Judgment is announced.", | |
| verdict=Verdict( | |
| finding=finding, | |
| decree="The court enters the final judgment.", | |
| rationale="The jury majority decides the record.", | |
| evidence_ids=["E1"], | |
| uncertainty="Some uncertainty remains.", | |
| remedy="Record the judgment.", | |
| ), | |
| turns=[ | |
| AgentTurn( | |
| agent=app.JUDGE_NAME, | |
| role="verdict writer", | |
| content="The judgment of the court is guilty.", | |
| model="test-model", | |
| confidence=0.9, | |
| input="SYSTEM:\nAnnounce verdict.", | |
| ) | |
| ], | |
| ) | |
| def test_lower_tab_renderers_emit_plain_text_classes(): | |
| event = _event_with_lower_tab_data() | |
| html = "\n".join( | |
| [ | |
| app.render_evidence([]), | |
| app.render_evidence([event]), | |
| app.render_jurors([]), | |
| app.render_jurors([event]), | |
| app.render_mind([], True), | |
| app.render_mind([event], True), | |
| app.render_mind([event], False), | |
| ] | |
| ) | |
| for class_name in OLD_CARD_CLASSES: | |
| assert class_name not in html | |
| assert "drawer-text-block" in html | |
| assert "drawer-empty" in html | |
| assert "mind-text" in html | |
| def test_download_controls_are_not_wired_into_app(): | |
| source = inspect.getsource(app.build_app) | |
| assert "DownloadButton" not in source | |
| assert "Download decree" not in source | |
| assert "Download agent trace" not in source | |
| def test_case_dropdown_only_exposes_demo_and_custom_cases(): | |
| assert list(app.CASE_OPTIONS) == ["Trial of Socrates", "Greg Heffley vs Mom", "Custom"] | |
| assert "The People v. Barnaby Buttons" not in app.CASE_OPTIONS | |
| assert "Live Search Tribunal" not in app.CASE_OPTIONS | |
| def test_courtroom_splits_six_jurors_between_side_benches(): | |
| html = app.render_court([_event_with_lower_tab_data()], started=True) | |
| assert "jury-benches left" in html | |
| assert "jury-benches right" in html | |
| assert html.count("<a class='juror") == 6 | |
| assert html.find("jury-benches left") < html.find("jury-benches right") | |
| assert ".jury-benches.left {\n left: 1%;" in app.CSS | |
| assert ".jury-benches.right {\n right: 1%;" in app.CSS | |
| assert ".jury-benches.left {\n left: .5%;" in app.CSS | |
| assert ".jury-benches.right {\n right: .5%;" in app.CSS | |
| def test_courtroom_threads_show_model_input_output_on_hover_and_click(): | |
| html = app.render_court([_event_with_lower_tab_data()], started=True) | |
| assert "tooltip-io-label'>Input" in html | |
| assert "tooltip-io-label'>Output" in html | |
| assert "Click to open full thread" in html | |
| assert "class='ai-thread-modal'" in html | |
| assert "thread-block'>SYSTEM:" in html | |
| assert "The jurors compare E1 and state their votes." in html | |
| assert "href='#ai-thread-karl-marx'" in html | |
| def test_courtroom_renders_historical_judge_and_juror_assets(): | |
| html = app.render_court([_event_with_lower_tab_data()], started=True) | |
| assert "Marcus Aurelius" in html | |
| assert "assets/characters/marcus-aurelius.png" in html | |
| assert "<img class='puppet-portrait' src='/gradio_api/file=assets/characters/marcus-aurelius.png'" in html | |
| assert ".puppet.judge::before,\n.puppet.judge::after {\n display: none;\n}" in app.CSS | |
| assert ".puppet.judge .mouth {\n display: none;\n}" in app.CSS | |
| for name, image in app.JUROR_IMAGES.items(): | |
| assert name in html | |
| assert image in html | |
| assert html.count("class='juror-portrait'") == 6 | |
| assert "class='juror-face'" not in html | |
| assert "class='juror-body'" not in html | |
| def test_courtroom_renders_foreground_fences_and_judge_table_above_characters(): | |
| html = app.render_court([_event_with_lower_tab_data()], started=True) | |
| assert html.count("assets/foreground/foregroundFence.png") == 2 | |
| assert "assets/foreground/JudgeTable.png" in html | |
| assert html.find("class='puppet judge") < html.find("class='foreground-props'") | |
| assert ".foreground-props {\n position: absolute;\n inset: 0;\n z-index: 13;" in app.CSS | |
| assert ".puppet {\n --skin: #c99257;" in app.CSS | |
| assert "z-index: 8;" in app.CSS | |
| assert ".puppet.clerk {\n left: 43%;\n top: 66%;\n z-index: 14;" in app.CSS | |
| def test_trial_progress_defaults_to_pretrial_and_renders_all_stages(): | |
| html = app.render_court([]) | |
| assert "class='trial-progress'" in html | |
| assert "data-phase='pretrial' aria-current='step'" in html | |
| for _key, label in app.TRIAL_PROGRESS_STAGES: | |
| assert label in html | |
| def test_trial_progress_marks_questions_current(): | |
| html = app.render_court([_speaker_event("Mike OSS", phase="questions")], started=True) | |
| assert "class='trial-progress-segment current' data-phase='questions' aria-current='step'" in html | |
| assert "data-phase='evidence'" in html | |
| def test_trial_progress_marks_deliberation_current(): | |
| html = app.render_court([_event_with_lower_tab_data()], started=True) | |
| assert "class='trial-progress-segment current' data-phase='deliberation' aria-current='step'" in html | |
| assert "class='trial-progress-segment complete' data-phase='questions'" in html | |
| def test_trial_progress_marks_verdict_current_and_complete(): | |
| html = app.render_court([_speaker_event(app.JUDGE_NAME, phase="verdict")], started=True) | |
| assert "class='trial-progress-segment current complete' data-phase='verdict' aria-current='step'" in html | |
| assert "class='trial-progress-segment complete' data-phase='deliberation'" in html | |
| def test_verdict_popup_renders_only_when_final_verdict_is_revealed(): | |
| event = _verdict_event("liable") | |
| announcement = app.render_court([event], started=True) | |
| sealed = app.render_court([event], started=True, show_verdict_popup=True) | |
| assert "class='speech-bubble active-dialogue speaker-judge'" in announcement | |
| assert "class='verdict-popup'" not in announcement | |
| assert "class='speech-bubble active-dialogue speaker-judge'" in sealed | |
| assert "class='verdict-popup'" in sealed | |
| assert "data-finding='liable'" in sealed | |
| assert "Verdict: Guilty" in sealed | |
| def test_run_ui_reveals_verdict_popup_after_judge_speech(monkeypatch): | |
| event = _verdict_event("not_liable") | |
| monkeypatch.setattr(app, "get_events", lambda request: iter([event])) | |
| monkeypatch.setattr(app, "_reading_duration", lambda text: 0) | |
| outputs = list(app.run_ui("Trial of Socrates", "", "", "", "swift", True)) | |
| assert "class='speech-bubble active-dialogue speaker-judge'" in outputs[1][0] | |
| assert "class='verdict-popup'" not in outputs[1][0] | |
| assert outputs[-1][-1] == "Verdict sealed." | |
| assert "class='verdict-popup'" in outputs[-1][0] | |
| assert "Verdict: Not Guilty" in outputs[-1][0] | |
| def test_trial_progress_ignores_unknown_phase_without_extra_segment(): | |
| html = app.render_court([_speaker_event("Clerk Meridian", phase="appeal")], started=True) | |
| assert "class='trial-progress'" in html | |
| assert html.count("class='trial-progress-segment") == len(app.TRIAL_PROGRESS_STAGES) | |
| assert "aria-current='step'" not in html | |
| assert "class='trial-progress-segment' data-phase='appeal'" not in html | |
| def test_trial_progress_css_is_fixed_and_translucent_theme_matched(): | |
| assert ".trial-progress {\n position: fixed;\n top: 0;" in app.CSS | |
| assert "background: rgba(23, 13, 8, .58);" in app.CSS | |
| assert "backdrop-filter: blur(8px);" in app.CSS | |
| assert "background: #ffd675;" in app.CSS | |
| assert ".trial-progress-abbrev {\n display: inline;" in app.CSS | |
| def test_foreground_prop_assets_have_real_transparency(): | |
| for path in [ | |
| Path("assets/foreground/foregroundFence.png"), | |
| Path("assets/foreground/JudgeTable.png"), | |
| ]: | |
| alpha = Image.open(path).convert("RGBA").getchannel("A") | |
| histogram = alpha.histogram() | |
| assert histogram[0] > 0, f"{path} has no fully transparent pixels" | |
| assert histogram[255] > 0, f"{path} has no fully opaque prop pixels" | |
| def test_latest_speaker_sets_stage_class_and_speech_bubble(): | |
| html = app.render_court([_speaker_event("Mike OSS", phase="claims")], started=True) | |
| assert "speaker-auric" in html | |
| assert "class='speech-bubble active-dialogue speaker-auric'" in html | |
| assert "data-speaker='Mike OSS'" in html | |
| assert "<strong>Mike OSS</strong>" in html | |
| assert "test speaker" in html | |
| assert "Mike OSS has the visible floor." in html | |
| assert "puppet auric active walking" in html | |
| assert "puppet sable active" not in html | |
| assert html.count("class='speech-bubble") == 1 | |
| assert html.find("class='foreground-props'") < html.find("class='speech-bubble active-dialogue") | |
| assert ".speech-bubble.active-dialogue,\n.speech-bubble.active-dialogue * {\n color: #141413 !important;\n}" in app.CSS | |
| assert "border: 2px solid #141413;" in app.CSS | |
| assert "font-size: 12px;" in app.CSS | |
| def test_speech_bubble_uses_full_turn_content_not_event_body(): | |
| long_text = " ".join(["The record speaks plainly"] * 18) + " with a final visible phrase." | |
| event = TrialEvent( | |
| phase="questions", | |
| title="Counsel answers", | |
| body="Narration only, not spoken dialogue.", | |
| turns=[ | |
| AgentTurn( | |
| agent="Harvey Vector", | |
| role="respondent advocate", | |
| content=long_text, | |
| model="test-model", | |
| confidence=0.9, | |
| ) | |
| ], | |
| ) | |
| html = app.render_court([event], started=True) | |
| bubble = html[html.index("<div class='speech-bubble") : html.index("<div class='gallery-benches")] | |
| assert "with a final visible phrase." in bubble | |
| assert "Narration only" not in bubble | |
| assert "..." not in bubble | |
| def test_pending_speaker_renders_single_preparing_bubble(): | |
| pending = app.SpeakerCue( | |
| name="Harvey Vector", | |
| role="respondent advocate", | |
| text="Harvey Vector is preparing a response.", | |
| pending=True, | |
| ) | |
| html = app.render_court([], started=True, pending_speaker=pending) | |
| assert "class='speech-bubble active-dialogue speaker-sable pending'" in html | |
| assert "data-pending='true'" in html | |
| assert "Harvey Vector is preparing a response." in html | |
| assert "puppet sable active walking" in html | |
| assert html.count("class='speech-bubble") == 1 | |
| def test_reading_duration_scales_with_words_and_caps(): | |
| assert app._reading_duration("short line") == app.MIN_READ_SECONDS | |
| assert app._reading_duration("word " * 18) > app.MIN_READ_SECONDS | |
| assert app._reading_duration("word " * 200) == app.MAX_READ_SECONDS | |
| def test_individual_juror_can_be_active_speaker(): | |
| event = TrialEvent( | |
| phase="deliberation", | |
| title="Juror Karl Marx Votes", | |
| body=app.JUROR_PERSONAS["Karl Marx"], | |
| turns=[ | |
| AgentTurn( | |
| agent="Karl Marx", | |
| role="juror", | |
| content="Liable. E1 carries the record.", | |
| model="nvidia/Nemotron-Orchestrator-8B", | |
| confidence=0.86, | |
| input="SYSTEM:\nJury JSON prompt.", | |
| ) | |
| ], | |
| votes=[ | |
| JurorVote( | |
| juror="Karl Marx", | |
| persona=app.JUROR_PERSONAS["Karl Marx"], | |
| vote="liable", | |
| reason="E1 carries the record.", | |
| evidence_ids=["E1"], | |
| ) | |
| ], | |
| ) | |
| html = app.render_court([event], started=True) | |
| assert "speaker-karl-marx" in html | |
| assert "<a class='juror active'" in html | |
| assert "class='speech-bubble active-dialogue speaker-karl-marx juror-dialogue'" in html | |
| assert "Liable. E1 carries the record." in html | |
| assert html.count("class='speech-bubble") == 1 | |
| def test_juror_speech_bubbles_anchor_above_side_benches(): | |
| assert ".speech-bubble.active-dialogue.juror-dialogue {\n top: 42%;" in app.CSS | |
| assert ".speech-bubble.active-dialogue.speaker-karl-marx,\n.speech-bubble.active-dialogue.speaker-john-stuart-mill,\n.speech-bubble.active-dialogue.speaker-confucius {\n left: 1.5%;" in app.CSS | |
| assert ".speech-bubble.active-dialogue.speaker-cleopatra-vii,\n.speech-bubble.active-dialogue.speaker-niccolo-machiavelli,\n.speech-bubble.active-dialogue.speaker-jensen-huang {\n right: 1.5%;" in app.CSS | |
| assert "--bubble-tail-x: 19%;" in app.CSS | |
| assert "--bubble-tail-x: 81%;" in app.CSS | |
| assert ".speech-bubble.active-dialogue.juror-dialogue,\n .speech-bubble.active-dialogue.speaker-karl-marx" in app.CSS | |
| assert "top: 500px;" in app.CSS | |
| def test_lawyer_movement_css_is_speaker_specific_not_phase_wide(): | |
| assert ".speaker-auric .puppet.auric" in app.CSS | |
| assert ".speaker-sable .puppet.sable" in app.CSS | |
| assert ".phase-claims .puppet.auric" not in app.CSS | |
| assert ".phase-opening .puppet.sable" not in app.CSS | |
| def test_closed_book_and_key_characters_align_with_judge_table(): | |
| assert ".episode-book {\n position: absolute;\n left: 50%;\n top: 122px;\n z-index: 14;" in app.CSS | |
| assert "width: min(980px, calc(100% - 32px));" in app.CSS | |
| assert ".episode-book.closed {\n top: 50%;\n width: min(163px, 20vw);" in app.CSS | |
| assert ".foreground-fence {\n bottom: -6.5%;\n width: 47%;" in app.CSS | |
| assert ".judge-table-foreground {\n left: 50%;\n top: 20%;\n z-index: 1;\n width: 39.1%;" in app.CSS | |
| assert ".puppet.judge {\n left: 50%;\n top: calc(40% + 156px);" in app.CSS | |
| assert ".puppet.auric {\n left: 24%;\n top: 87%;" in app.CSS | |
| assert ".speaker-auric .puppet.auric {\n left: 43%;\n top: 87%;" in app.CSS | |
| assert ".puppet.sable {\n left: 75%;\n top: 87%;" in app.CSS | |
| assert ".speaker-sable .puppet.sable {\n left: 75%;\n top: 87%;" in app.CSS | |
| assert ".puppet.clerk {\n left: 43%;\n top: 66%;" in app.CSS | |
| assert ".puppet.auditor" not in app.CSS | |
| assert ".episode-book.closed {\n top: 640px;\n width: 140px;" in app.CSS | |
| assert ".episode-book {\n top: 218px;\n width: min(680px, calc(100% - 20px));" in app.CSS | |
| assert ".foreground-fence {\n bottom: -66px;\n width: 64%;" in app.CSS | |
| assert ".judge-table-foreground {\n top: 213px;\n width: 646px;" in app.CSS | |
| assert ".puppet.auric {\n left: 20%;\n top: 970px;" in app.CSS | |
| assert ".puppet.sable {\n left: 80%;\n top: 970px;" in app.CSS | |
| assert ".speaker-sable .puppet.sable {\n left: 80%;\n top: 970px;" in app.CSS | |
| assert ".puppet.judge {\n top: 576px;" not in app.CSS | |
| assert ".puppet.sable {\n left: 80%;\n top: 640px;" not in app.CSS | |
| assert ".speaker-sable .puppet.sable {\n left: 80%;\n top: 640px;" not in app.CSS | |
| assert ".puppet.clerk {\n left: 35%;\n top: 880px;" in app.CSS | |
| assert ".speech-bubble.active-dialogue.speaker-auditor" not in app.CSS | |
| def test_open_docket_book_renders_text_above_book_art(): | |
| html = app.render_court([]) | |
| assert "class='episode-book'" in html | |
| assert "class='book-open-content'" in html | |
| assert "Trial details" in html | |
| assert "Evidence" in html | |
| def test_greg_case_preview_uses_cached_context_and_evidence_columns(): | |
| html = app.render_case_preview("Greg Heffley vs Mom") | |
| assert "Greg Heffley v. Mom" in html | |
| assert "diary" in html | |
| assert "Evidence for Greg Heffley" in html | |
| assert "Evidence for Susan Heffley" in html | |
| def test_custom_case_preview_renders_fillable_book_fields(): | |
| html = app.render_case_preview("Custom") | |
| assert "episode-book custom-book" in html | |
| assert "book-context-field" in html | |
| assert html.count("book-claimant-field") == 3 | |
| assert html.count("book-respondent-field") == 3 | |
| def test_custom_payload_builds_trial_request_packet(monkeypatch): | |
| captured = {} | |
| def fake_events(request): | |
| captured["request"] = request | |
| return iter([_event_with_lower_tab_data()]) | |
| monkeypatch.setattr(app, "get_events", fake_events) | |
| monkeypatch.setattr(app, "_reading_duration", lambda text: 0) | |
| payload = json.dumps( | |
| { | |
| "context": "A missing bicycle is traced to a disputed garage visit.", | |
| "claimant_evidence": ["Garage text", "", "Scuffed tire mark"], | |
| "respondent_evidence": ["Neighbor saw bike later", "", ""], | |
| } | |
| ) | |
| outputs = list(app.run_ui("Custom", "", "", payload, "swift", True)) | |
| assert outputs[-1][-1] == "Verdict sealed." | |
| request = captured["request"] | |
| assert request.case_id == "custom" | |
| assert request.custom_case is not None | |
| assert request.custom_case.context.startswith("A missing bicycle") | |
| assert [item.supports for item in request.custom_case.evidence] == ["claimant", "claimant", "respondent"] | |
| def test_custom_payload_requires_context_and_both_evidence_sides(): | |
| payload = json.dumps({"context": "", "claimant_evidence": ["Only one side"], "respondent_evidence": []}) | |
| outputs = list(app.run_ui("Custom", "", "", payload, "swift", True)) | |
| assert outputs[-1][-1] == "Custom requires a trial details paragraph." | |
| def test_run_ui_yields_five_outputs_without_download_status(monkeypatch): | |
| event = _event_with_lower_tab_data() | |
| monkeypatch.setattr(app, "get_events", lambda request: iter([event])) | |
| monkeypatch.setattr(app, "_reading_duration", lambda text: 0) | |
| outputs = list(app.run_ui("Trial of Socrates", "", "", "", "swift", True)) | |
| assert outputs | |
| assert all(len(output) == 5 for output in outputs) | |
| assert outputs[0][-1] == "Clerk Meridian is preparing their response." | |
| assert outputs[1][-1] == "Step 1: Nemotron Jury - Jury weighs the record" | |
| assert outputs[-1][-1] == "Verdict sealed." | |
| assert "download" not in outputs[-1][-1].lower() | |
| def test_run_ui_stops_with_model_unavailable_error(monkeypatch): | |
| def broken_events(request): | |
| raise RuntimeError("Marcus Aurelius unavailable: offline") | |
| yield | |
| monkeypatch.setattr(app, "get_events", broken_events) | |
| outputs = list(app.run_ui("Trial of Socrates", "", "", "", "swift", True)) | |
| assert outputs[-1][-1] == "Model response required. Trial stopped: Marcus Aurelius unavailable: offline" | |
| assert "Claimant score" not in outputs[-1][0] | |
| def test_remote_events_uses_default_modal_endpoint_without_local_token(monkeypatch): | |
| captured = {} | |
| class FakeResponse: | |
| def __enter__(self): | |
| return self | |
| def __exit__(self, exc_type, exc, traceback): | |
| return False | |
| def raise_for_status(self): | |
| return None | |
| def iter_lines(self): | |
| event = _speaker_event("Clerk Meridian", phase="intake") | |
| yield json.dumps(event.model_dump()) | |
| def fake_stream(method, endpoint, json, timeout): | |
| captured["method"] = method | |
| captured["endpoint"] = endpoint | |
| captured["payload"] = json | |
| captured["timeout"] = timeout | |
| return FakeResponse() | |
| monkeypatch.delenv("MODAL_TRIAL_URL", raising=False) | |
| monkeypatch.delenv("HF_TOKEN", raising=False) | |
| monkeypatch.setattr(app.httpx, "stream", fake_stream) | |
| event = next(app.get_events(app.TrialRequest(case_id="socrates"), delay=0.0)) | |
| assert captured["method"] == "POST" | |
| assert captured["endpoint"] == app.DEFAULT_MODAL_TRIAL_URL | |
| assert captured["timeout"] == 900.0 | |
| assert event.turns[0].agent == "Clerk Meridian" | |
| def test_court_renders_sound_toggle(): | |
| html = app.render_court([]) | |
| assert "sound-toggle" in html | |
| assert "aria-label='Toggle sound'" in html | |
| assert "aria-pressed='false'" in html | |
| def test_audio_controller_has_score_breathing_and_mute_toggle(): | |
| assert "SCORE_BREATH_INTERVAL_MS = 20000" in app.APP_JS | |
| assert "SCORE_BREATH_DURATION_MS = 5000" in app.APP_JS | |
| assert "toggleMuted()" in app.APP_JS | |
| assert "this.fadeScore(SCORE_QUIET_VOLUME, halfDuration" in app.APP_JS | |
| def test_courtroom_background_has_no_overlay_or_character_shadow(): | |
| assert "background: #141413 !important;" in app.CSS | |
| assert "background-color: #141413 !important;" in app.CSS | |
| assert "cover fixed no-repeat" not in app.CSS | |
| assert ".court-episode-stage::before {\n content: \"\";\n display: none;" in app.CSS | |
| assert ".court-episode-stage::after {\n content: \"\";\n display: none;" in app.CSS | |
| assert "url('/gradio_api/file=assets/background/CourtRoom.png') center center / 100% 100% no-repeat" in app.CSS | |
| assert "filter: drop-shadow(0 12px 14px" not in app.CSS | |
| assert "filter: drop-shadow(0 8px 10px" not in app.CSS | |
| def test_synthetic_stage_props_do_not_tint_background(): | |
| assert ".bench-front {\n display: none;" in app.CSS | |
| assert ".trial-floor-mark {\n display: none;" in app.CSS | |
| assert ".gallery-benches {\n display: none;" in app.CSS | |
| assert ".prop-label {\n display: none;" in app.CSS | |
| assert ".counsel-table" in app.CSS | |
| assert "background: transparent;\n box-shadow: none;" in app.CSS | |
| assert ".witness-area" in app.CSS | |