Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from app import ( | |
| CSS, | |
| handle_stage_nav, | |
| handle_generate, | |
| handle_grade, | |
| pending_activity_message, | |
| readable_current_artifact_markdown, | |
| render_activity_message, | |
| render_stage_score_gate_panel, | |
| render_stage_score_strip, | |
| stage_score_rows, | |
| workflow_progress_summary, | |
| workspace_visibility_updates, | |
| ) | |
| from course_slide_factory.constants import STAGE_IDS | |
| from course_slide_factory.models import IssueSeverity, IssueType | |
| from course_slide_factory.quality import approve_current_artifact, upsert_issue | |
| from course_slide_factory.workflow import build_empty_state, generate_stage, update_setup_from_inputs | |
| def _activity_value(update): | |
| return update["value"] if isinstance(update, dict) else update | |
| def _update_visible(update): | |
| return update["visible"] if isinstance(update, dict) else None | |
| def test_workflow_progress_summary_starts_empty(): | |
| state = build_empty_state(deck_title="UI Test", source_url="mock://source") | |
| summary = workflow_progress_summary(state) | |
| rows = stage_score_rows(state, "setup_inputs") | |
| assert summary["complete"] == 0 | |
| assert summary["total"] == len(STAGE_IDS) | |
| assert summary["percent"] == 0 | |
| assert rows[0]["stage_id"] == "setup_inputs" | |
| assert rows[0]["active"] | |
| assert rows[0]["score"] is None | |
| assert rows[0]["status"] == "pending" | |
| def test_stage_score_rows_track_complete_review_stale_and_blockers(): | |
| state = build_empty_state(deck_title="UI Test", source_url="mock://source") | |
| state, _message = generate_stage(state, "setup_inputs") | |
| state.stages["setup_inputs"].score = 92 | |
| approve_current_artifact(state, "setup_inputs") | |
| state, _message = generate_stage(state, "text_generation") | |
| state.stages["text_generation"].score = 72 | |
| state.stages["title_generation"].is_stale = True | |
| upsert_issue( | |
| state, | |
| IssueType.TECHNICAL_INACCURACY, | |
| IssueSeverity.BLOCKER, | |
| "Blocking issue", | |
| stage_id="technical_review", | |
| ) | |
| state.stages["technical_review"].is_stale = False | |
| summary = workflow_progress_summary(state) | |
| rows = {row["stage_id"]: row for row in stage_score_rows(state, "text_generation")} | |
| score_strip = render_stage_score_strip(state, "text_generation") | |
| assert summary["complete"] == 1 | |
| assert summary["percent"] == round(1 / len(STAGE_IDS) * 100) | |
| assert summary["unresolved_blockers"] >= 1 | |
| assert rows["setup_inputs"]["status"] == "complete" | |
| assert rows["text_generation"]["status"] == "review" | |
| assert rows["text_generation"]["active"] | |
| assert rows["title_generation"]["status"] == "stale" | |
| assert rows["technical_review"]["status"] == "blocked" | |
| assert "Text" in score_strip | |
| assert "72/100" in score_strip | |
| def test_setup_stage_shows_feedback_workspaces(): | |
| updates = workspace_visibility_updates("setup_inputs") | |
| assert [update["visible"] for update in updates] == [True, False, False, False, True] | |
| def test_activity_messages_render_distinct_pending_and_complete_states(): | |
| pending = pending_activity_message("Creating setup candidate...") | |
| complete = render_activity_message("Setup candidate created.", "complete") | |
| assert "activity-card pending" in pending | |
| assert "Working" in pending | |
| assert "Creating setup candidate..." in pending | |
| assert "activity-card complete" in complete | |
| assert "Done" in complete | |
| def test_light_theme_css_covers_gradio_dataframes_and_action_hierarchy(): | |
| assert ":root" in CSS | |
| assert "color-scheme: light" in CSS | |
| assert "--border-color-primary: #d9dee7" in CSS | |
| assert "--body-text-color-subdued: #4b5563" in CSS | |
| assert "--ag-background-color: #ffffff" in CSS | |
| assert ".compact-panel .ag-header" in CSS | |
| assert '.compact-panel [role="gridcell"]' in CSS | |
| assert ".compact-panel .virtual-row" in CSS | |
| assert ".compact-panel .body-cell" in CSS | |
| assert '.gradio-container [data-testid="dataframe"] tbody tr:nth-child(even)' in CSS | |
| assert '.gradio-container [role="gridcell"]' in CSS | |
| assert ".gradio-container .header-table" in CSS | |
| assert ".gradio-container .virtual-row.row-odd" in CSS | |
| assert ".production-action" in CSS | |
| assert ".approval-action" in CSS | |
| assert ".score-gate" in CSS | |
| assert '.score-gate [data-testid="column"]' in CSS | |
| def test_stage_score_gate_panel_renders_pending_and_scored_states(): | |
| state = build_empty_state(deck_title="UI Test", source_url="mock://source") | |
| pending = render_stage_score_gate_panel(state, "setup_inputs") | |
| state.stages["setup_inputs"].score = 91 | |
| scored = render_stage_score_gate_panel(state, "setup_inputs") | |
| assert "Setup & Inputs score" in pending | |
| assert "Pending" in pending | |
| assert "stage-result-card scored" in scored | |
| assert "91/100" in scored | |
| def test_setup_candidate_button_returns_visible_snapshot_feedback(tmp_path): | |
| instructions = tmp_path / "instructions.txt" | |
| instructions.write_text("Course plan\n- Explain the core course concept.", encoding="utf-8") | |
| generated = handle_generate( | |
| None, | |
| "Course Slide Deck", | |
| "", | |
| "mock://template/course", | |
| "", | |
| True, | |
| "obj_1: Explain the core course concept.", | |
| [str(instructions)], | |
| "", | |
| "", | |
| "instructions_file", | |
| None, | |
| "", | |
| False, | |
| "setup_inputs", | |
| ) | |
| assert _update_visible(generated[1]) is True | |
| message = _activity_value(generated[2]) | |
| assert "activity-card complete" in message | |
| assert "Done" in message | |
| assert "Setup candidate created as setup_inputs_v1." in message | |
| assert "1 parsed upload(s)" in message | |
| assert "Check Setup Score" in message | |
| assert "### Setup & Inputs output" in generated[3] | |
| assert "Input summary" in generated[3] | |
| assert "Parsed uploads: 1" in generated[3] | |
| assert "instructions.txt" in generated[3] | |
| assert '"input_summary"' not in generated[3] | |
| assert generated[4] == generated[3] | |
| def test_readable_artifact_markdown_uses_human_summary_not_json(tmp_path): | |
| instructions = tmp_path / "instructions.txt" | |
| instructions.write_text("Course plan\n- Explain the core course concept.", encoding="utf-8") | |
| state = build_empty_state(deck_title="Course Slide Deck", source_url="") | |
| update_setup_from_inputs( | |
| state, | |
| deck_title="Course Slide Deck", | |
| source_url="", | |
| template_url="mock://template/course", | |
| output_folder_id="", | |
| dry_run=True, | |
| objectives_text="obj_1: Explain the core course concept.", | |
| material_files=[str(instructions)], | |
| start_mode="instructions_file", | |
| ) | |
| state, _message = generate_stage(state, "setup_inputs") | |
| output = readable_current_artifact_markdown(state, "setup_inputs") | |
| assert output.startswith("### Setup & Inputs output") | |
| assert "Input summary" in output | |
| assert "Uploaded materials" in output | |
| assert '"uploaded_materials"' not in output | |
| def test_stage_navigation_clears_activity_message(): | |
| generated = handle_generate( | |
| None, | |
| "Course Slide Deck", | |
| "mock://source", | |
| "mock://template/course", | |
| "", | |
| True, | |
| "", | |
| [], | |
| "", | |
| "", | |
| "instructions_file", | |
| None, | |
| "", | |
| False, | |
| "setup_inputs", | |
| ) | |
| navigated = handle_stage_nav(generated[0], "source_extraction_objective_mapping") | |
| assert navigated[4]["visible"] is False | |
| assert navigated[5] == "" | |
| def test_setup_grade_button_path_returns_score_feedback(): | |
| generated = handle_generate( | |
| None, | |
| "Course Slide Deck", | |
| "mock://source", | |
| "mock://template/course", | |
| "", | |
| True, | |
| "", | |
| [], | |
| "", | |
| "", | |
| "instructions_file", | |
| None, | |
| "", | |
| False, | |
| "setup_inputs", | |
| ) | |
| graded = handle_grade(generated[0], "setup_inputs") | |
| message = _activity_value(graded[2]) | |
| assert "activity-card complete" in message | |
| assert "Grade for Setup & Inputs:" in message | |