ai_sections.html pastes into the no-AI dashboard.html; ai_bridge.py adds one ai_request slot that hands every AI contract request (incl. GET /roi) to the review backend's dispatch(). Desktop page check reruns the web review and cost cases plus a tampered-page gate case. Eval fixture e-pyqt-ai added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
2.2 KiB
Python
55 lines
2.2 KiB
Python
"""The AI web page's review cases, run unchanged against the desktop page.
|
|
|
|
They use this folder's `page` and `stub` fixtures: the page talks to the review backend
|
|
over the fake Qt bridge instead of HTTP.
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "page_check_ai"))
|
|
|
|
from test_ai_review import ( # noqa: E402,F401
|
|
test_approve_stays_blocked_while_rows_are_unverified,
|
|
test_band_counts_and_legend_match,
|
|
test_band_filters_show_the_right_rows,
|
|
test_draft_edit_is_not_saved_until_save,
|
|
test_page_x_of_y_updates_while_running,
|
|
)
|
|
from test_ai_review import FIELDS, FLAGGED, open_review # noqa: E402
|
|
from test_ai_cost import ( # noqa: E402,F401
|
|
cost_page,
|
|
test_by_hand_panel_is_labelled_assumptions_and_recomputes,
|
|
test_coverage_pill_counts_priced_of_all_documents,
|
|
test_month_breakdown_groups_runs_by_month,
|
|
test_runs_show_tokens_and_cost_and_unpriced_is_not_zero,
|
|
test_spend_tiles_sum_the_priced_runs,
|
|
)
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "page_check"))
|
|
from test_contract import ( # noqa: E402,F401
|
|
test_dark_toggle_flips_theme,
|
|
test_donut_and_legend_match_batch_states,
|
|
test_kpi_counts_match_batch_states,
|
|
test_log_lines_show_their_type,
|
|
test_no_bot_page_runs_on_sample_data,
|
|
test_plain_browser_first_open_shows_sample_data,
|
|
test_report_search_filters_rows_and_shows_bucket_pill,
|
|
test_step_list_follows_step_label,
|
|
)
|
|
|
|
TAMPER = """async (path) => {
|
|
const reply = new Promise(done => { const base = window.__pyMsg;
|
|
window.__pyMsg = m => { base(m); if (JSON.parse(m).event === 'ai_response') done(JSON.parse(m).data); }; });
|
|
bridge.ai_request(999, 'POST', path, '{}');
|
|
return await reply;
|
|
}"""
|
|
|
|
|
|
def test_a_changed_page_cannot_approve_over_the_bridge(page, stub):
|
|
"""The gate is Python's: a page that skips its own buttons and calls the slot is still refused."""
|
|
stub.put_run("job-t", "bands.pdf", FIELDS)
|
|
open_review(page, "job-t")
|
|
reply = page.evaluate(TAMPER, "/runs/job-t/approve")
|
|
assert reply["id"] == 999 and reply["status"] == 409 and str(FLAGGED) in reply["body"]["detail"]
|
|
assert stub.RUNS["job-t"]["approved"] is False
|