Spaces:
Running
Running
| import sys | |
| import re | |
| import json | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | |
| import app | |
| def _assert(cond: bool, msg: str) -> None: | |
| if not cond: | |
| raise AssertionError(msg) | |
| def _css_block(selector: str) -> str: | |
| pat = re.compile(rf"{re.escape(selector)}\s*\{{([^}}]+)\}}", re.DOTALL) | |
| m = pat.search(app.css) | |
| _assert(m is not None, f"missing CSS selector block: {selector}") | |
| return m.group(1) | |
| def _css_prop(block: str, prop: str) -> str: | |
| pat = re.compile(rf"{re.escape(prop)}\s*:\s*([^;]+);", re.IGNORECASE) | |
| m = pat.search(block) | |
| _assert(m is not None, f"missing CSS property '{prop}' in block: {block}") | |
| return m.group(1).strip().lower() | |
| def test_toggle_color_contract_matches_legend() -> None: | |
| legend_bg_by_origin = { | |
| "rewrite": _css_prop(_css_block(".source-legend .chip.rewrite"), "background"), | |
| "selection": _css_prop(_css_block(".source-legend .chip.selection"), "background"), | |
| "structural": _css_prop(_css_block(".source-legend .chip.structural"), "background"), | |
| "classifier": _css_prop(_css_block(".source-legend .chip.classifier"), "background"), | |
| "implied": _css_prop(_css_block(".source-legend .chip.implied"), "background"), | |
| "user": _css_prop(_css_block(".source-legend .chip.user"), "background"), | |
| } | |
| on_bg2_by_origin = { | |
| "rewrite": _css_prop(_css_block('.lego-tags label[data-psq-preselected="1"][data-psq-origin="rewrite"] span'), "--on-bg2"), | |
| "selection": _css_prop(_css_block('.lego-tags label[data-psq-preselected="1"][data-psq-origin="selection"] span'), "--on-bg2"), | |
| "structural": _css_prop(_css_block('.lego-tags label[data-psq-preselected="1"][data-psq-origin="structural"] span'), "--on-bg2"), | |
| "classifier": _css_prop(_css_block('.lego-tags label[data-psq-preselected="1"][data-psq-origin="classifier"] span'), "--on-bg2"), | |
| "implied": _css_prop(_css_block('.lego-tags label[data-psq-preselected="1"][data-psq-origin="implied"] span'), "--on-bg2"), | |
| "user": _css_prop(_css_block('.lego-tags label[data-psq-preselected="0"] span'), "--on-bg2"), | |
| } | |
| for origin, legend_bg in legend_bg_by_origin.items(): | |
| _assert( | |
| on_bg2_by_origin[origin] == legend_bg, | |
| f"legend/toggle color mismatch for '{origin}': toggle={on_bg2_by_origin[origin]} legend={legend_bg}", | |
| ) | |
| def test_client_js_stamps_source_color_attributes() -> None: | |
| js = app.client_js | |
| has_origin_attr = ("data-psq-origin" in js) or ("dataset.psqOrigin" in js) | |
| has_preselected_attr = ("data-psq-preselected" in js) or ("dataset.psqPreselected" in js) | |
| _assert(has_origin_attr, "client_js does not stamp per-tag origin attributes on checkbox labels") | |
| _assert(has_preselected_attr, "client_js does not stamp per-tag preselected attributes on checkbox labels") | |
| def test_tooltip_payload_includes_row_meta_for_color_mapping() -> None: | |
| row_defs = [ | |
| { | |
| "name": "r1", | |
| "label": "R1", | |
| "tags": ["solo", "female"], | |
| "tag_meta": { | |
| "solo": {"origin": "rewrite", "preselected": True}, | |
| "female": {"origin": "selection", "preselected": False}, | |
| }, | |
| } | |
| ] | |
| raw = app._build_tooltip_payload(row_defs, max_rows=app.display_max_rows_default) | |
| payload = json.loads(raw) | |
| _assert("meta_rows" in payload, "tooltip payload missing meta_rows") | |
| _assert(isinstance(payload["meta_rows"], list), "tooltip payload meta_rows must be a list") | |
| _assert(len(payload["meta_rows"]) == 1, "meta_rows should align with visible row count") | |
| _assert(isinstance(payload["meta_rows"][0], list), "meta_rows row must be a list") | |
| _assert(len(payload["meta_rows"][0]) == 2, "meta_rows tag metadata count must align with tags") | |
| first = payload["meta_rows"][0][0] | |
| second = payload["meta_rows"][0][1] | |
| _assert(first.get("origin") == "rewrite", "first tag origin metadata mismatch") | |
| _assert(first.get("preselected") is True, "first tag preselected metadata mismatch") | |
| _assert(second.get("origin") == "selection", "second tag origin metadata mismatch") | |
| _assert(second.get("preselected") is False, "second tag preselected metadata mismatch") | |
| def test_prompt_uses_visible_rows_only() -> None: | |
| # If selected state contains stale hidden tags, prompt should still reflect visible-row selections only. | |
| row_defs = [ | |
| {"name": "r1", "label": "R1", "tags": ["solo", "female"], "tag_meta": {}}, | |
| {"name": "r2", "label": "R2", "tags": ["cub"], "tag_meta": {}}, | |
| ] | |
| payload = app._build_ui_payload( | |
| console_text="x", | |
| row_defs=row_defs, | |
| selected_tags=["solo", "rosalina_(mario)"], | |
| ) | |
| prompt_text = payload[4]["value"] | |
| selected_state = payload[5] | |
| _assert("rosalina \\(mario\\)" not in prompt_text, "stale hidden tag leaked into prompt") | |
| _assert("solo" in prompt_text, "visible selected tag missing from prompt") | |
| _assert("rosalina_(mario)" not in selected_state, "stale hidden tag leaked into selected state") | |
| def test_row_deduping() -> None: | |
| row_defs = [ | |
| { | |
| "name": "other_retrieved", | |
| "label": "Other (Retrieved)", | |
| "tags": ["cub", "expressions", "invalid_tag", "cub", "expressions"], | |
| "tag_meta": {}, | |
| } | |
| ] | |
| prompt_text, row_values_state, _, checkbox_updates = app._build_row_component_updates( | |
| row_defs=row_defs, | |
| selected_tags=["cub", "expressions"], | |
| max_rows=app.display_max_rows_default, | |
| ) | |
| _assert(prompt_text == "cub, expressions", "prompt should be deduped and ordered from row") | |
| _assert(row_values_state[0] == ["cub", "expressions"], "row selected values should be deduped") | |
| first_choices = checkbox_updates[0]["choices"] | |
| first_values = [v for _, v in first_choices] | |
| _assert(first_values == ["cub", "expressions", "invalid_tag"], "row choices should be deduped") | |
| def test_rebuild_ignores_stale_selected_state() -> None: | |
| row_defs = [ | |
| {"name": "selected_other", "label": "Selected (Other)", "tags": ["solo", "female", "anthro"], "tag_meta": {}}, | |
| {"name": "other_retrieved", "label": "Other (Retrieved)", "tags": ["cub", "expressions"], "tag_meta": {}}, | |
| ] | |
| # Simulate UI state where user has deselected anthro, but stale selected state still contains it. | |
| selected_state = ["solo", "female", "anthro", "cub"] | |
| row_values_state = [["solo", "female"], ["cub"]] | |
| out = app._rebuild_rows_from_selected( | |
| selected_state, | |
| row_defs, | |
| row_values_state, | |
| app.display_top_groups_default, | |
| app.display_top_tags_per_group_default, | |
| app.display_rank_top_k_default, | |
| ) | |
| prompt = out[1] | |
| selected_after = out[2] | |
| _assert("anthro" not in selected_after, "rebuild should not resurrect stale deselected tags") | |
| _assert("anthro" not in prompt, "prompt should not include stale deselected tags") | |
| _assert("solo" in prompt and "female" in prompt and "cub" in prompt, "rebuild should retain current row selections") | |
| def test_toggle_then_rebuild_does_not_resurrect_removed_tag() -> None: | |
| row_defs = [ | |
| {"name": "selected_other", "label": "Selected (Other)", "tags": ["solo", "anthro", "female"], "tag_meta": {}}, | |
| {"name": "other_retrieved", "label": "Other (Retrieved)", "tags": ["cub", "expressions"], "tag_meta": {}}, | |
| ] | |
| selected_state = ["solo", "anthro", "female", "cub"] | |
| row_values_state = [["solo", "anthro", "female"], ["cub"]] | |
| # User unchecks anthro in row 0. | |
| toggle_out = app._on_toggle_row( | |
| 0, | |
| ["solo", "female"], | |
| selected_state, | |
| False, | |
| row_defs, | |
| row_values_state, | |
| app.display_max_rows_default, | |
| ) | |
| selected_after_toggle = toggle_out[0] | |
| row_values_after_toggle = toggle_out[3] | |
| _assert("anthro" not in selected_after_toggle, "toggle should remove anthro from selected state") | |
| # Rebuild from current row values must preserve the user-toggle result. | |
| rebuild_out = app._rebuild_rows_from_selected( | |
| selected_after_toggle, | |
| row_defs, | |
| row_values_after_toggle, | |
| app.display_top_groups_default, | |
| app.display_top_tags_per_group_default, | |
| app.display_rank_top_k_default, | |
| ) | |
| prompt_after_rebuild = rebuild_out[1] | |
| selected_after_rebuild = rebuild_out[2] | |
| _assert("anthro" not in selected_after_rebuild, "rebuild should not resurrect deselected anthro") | |
| _assert("anthro" not in prompt_after_rebuild, "prompt should not contain deselected anthro after rebuild") | |
| _assert("solo" in prompt_after_rebuild and "female" in prompt_after_rebuild, "kept selections should remain") | |
| _assert("cub" in prompt_after_rebuild, "other retrieved selection should remain") | |
| def test_rebuild_preserves_user_toggled_provenance() -> None: | |
| row_defs = [ | |
| { | |
| "name": "demo_row", | |
| "label": "Demo Row", | |
| "tags": ["base_tag", "manual_tag"], | |
| "tag_meta": { | |
| "base_tag": {"origin": "selection", "preselected": True}, | |
| "manual_tag": {"origin": "selection", "preselected": False}, | |
| }, | |
| } | |
| ] | |
| selected_state = ["base_tag", "manual_tag"] | |
| row_values_state = [["base_tag", "manual_tag"]] | |
| out = app._rebuild_rows_from_selected( | |
| selected_state, | |
| row_defs, | |
| row_values_state, | |
| app.display_top_groups_default, | |
| app.display_top_tags_per_group_default, | |
| app.display_rank_top_k_default, | |
| ) | |
| rebuilt_rows = out[6] | |
| manual_meta = None | |
| for row in rebuilt_rows: | |
| tags = row.get("tags", []) if isinstance(row, dict) else [] | |
| if "manual_tag" in tags: | |
| row_meta = row.get("tag_meta", {}) if isinstance(row.get("tag_meta", {}), dict) else {} | |
| manual_meta = row_meta.get("manual_tag") | |
| if manual_meta: | |
| break | |
| _assert(manual_meta is not None, "manual_tag metadata missing after rebuild") | |
| _assert(manual_meta.get("origin") == "user", "rebuild should preserve manual_tag origin as user") | |
| _assert(manual_meta.get("preselected") is False, "rebuild should preserve manual_tag preselected=False") | |
| def test_toggle_does_not_cross_activate_unrelated_row_tag() -> None: | |
| row_defs = [ | |
| {"name": "organization", "label": "Organization", "tags": ["pinup", "close-up"], "tag_meta": {}}, | |
| {"name": "color_markings", "label": "Color Markings", "tags": ["shoulder_markings", "black_markings"], "tag_meta": {}}, | |
| ] | |
| selected_state = [] | |
| row_values_state = [[], []] | |
| # User enables close-up in organization row. | |
| out = app._on_toggle_row( | |
| 0, | |
| ["close-up"], | |
| selected_state, | |
| False, | |
| row_defs, | |
| row_values_state, | |
| app.display_max_rows_default, | |
| ) | |
| selected_after = out[0] | |
| row_values_after = out[3] | |
| _assert("close-up" in selected_after, "close-up should be selected") | |
| _assert("shoulder_markings" not in selected_after, "unrelated row tag should not be auto-selected") | |
| _assert(row_values_after[0] == ["close-up"], "organization row values should include close-up only") | |
| _assert(row_values_after[1] == [], "color markings row should remain unselected") | |
| def test_shared_tag_mirrors_without_unrelated_cross_toggle() -> None: | |
| row_defs = [ | |
| {"name": "objects_props", "label": "Objects Props", "tags": ["holding_face", "holding_clothing"], "tag_meta": {}}, | |
| {"name": "expression_detail", "label": "Expression Detail", "tags": ["open_mouth", "closed_smile"], "tag_meta": {}}, | |
| {"name": "pose_action_detail", "label": "Pose Action Detail", "tags": ["holding_face", "walking"], "tag_meta": {}}, | |
| ] | |
| selected_state = [] | |
| row_values_state = [[], [], []] | |
| # Enable open_mouth; should not affect holding_face rows. | |
| out1 = app._on_toggle_row( | |
| 1, | |
| ["open_mouth"], | |
| selected_state, | |
| False, | |
| row_defs, | |
| row_values_state, | |
| app.display_max_rows_default, | |
| ) | |
| sel1 = out1[0] | |
| vals1 = out1[3] | |
| _assert("open_mouth" in sel1, "open_mouth should be selected") | |
| _assert("holding_face" not in sel1, "holding_face must remain unselected") | |
| _assert(vals1[0] == [], "objects props row should remain unselected") | |
| _assert(vals1[1] == ["open_mouth"], "expression row should select open_mouth") | |
| _assert(vals1[2] == [], "pose row should remain unselected") | |
| # Enable holding_face in objects row; should mirror only to pose row, not expression row. | |
| out2 = app._on_toggle_row( | |
| 0, | |
| ["holding_face"], | |
| sel1, | |
| True, | |
| row_defs, | |
| vals1, | |
| app.display_max_rows_default, | |
| ) | |
| sel2 = out2[0] | |
| vals2 = out2[3] | |
| _assert("holding_face" in sel2 and "open_mouth" in sel2, "both explicitly selected tags should be present") | |
| _assert(vals2[0] == ["holding_face"], "objects row should select holding_face") | |
| _assert(vals2[1] == ["open_mouth"], "expression row should keep open_mouth only") | |
| _assert(vals2[2] == ["holding_face"], "pose row should mirror holding_face") | |
| def main() -> None: | |
| test_toggle_color_contract_matches_legend() | |
| test_client_js_stamps_source_color_attributes() | |
| test_tooltip_payload_includes_row_meta_for_color_mapping() | |
| test_prompt_uses_visible_rows_only() | |
| test_row_deduping() | |
| test_rebuild_ignores_stale_selected_state() | |
| test_toggle_then_rebuild_does_not_resurrect_removed_tag() | |
| test_rebuild_preserves_user_toggled_provenance() | |
| test_toggle_does_not_cross_activate_unrelated_row_tag() | |
| test_shared_tag_mirrors_without_unrelated_cross_toggle() | |
| print("ui state smoke: ok") | |
| if __name__ == "__main__": | |
| main() | |