flowproof 0.23.26__tar.gz → 0.23.28__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flowproof-0.23.26 → flowproof-0.23.28}/Cargo.lock +7 -7
- {flowproof-0.23.26 → flowproof-0.23.28}/Cargo.toml +1 -1
- {flowproof-0.23.26 → flowproof-0.23.28}/PKG-INFO +1 -1
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/sap_com.rs +82 -2
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/recorder.rs +141 -1
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/lib.rs +400 -12
- {flowproof-0.23.26 → flowproof-0.23.28}/flowproof/__init__.py +1 -1
- {flowproof-0.23.26 → flowproof-0.23.28}/pyproject.toml +1 -1
- {flowproof-0.23.26 → flowproof-0.23.28}/README.md +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/Cargo.toml +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/examples/vision_bench.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/examples/vision_do.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/agent_proxy.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/agent_runner.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_linux.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/audit.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/capture.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/env_block.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/filters.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/identity.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/logon.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/netevents.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/run.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/spawn.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows/wfp.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/egress_windows.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/fiori.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/fs_observe.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/lib.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/mcp_core.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/mcp_http.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/mcp_stdio.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/vision/start.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/vision.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/vision_bench.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/vision_elements.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/vision_winocr.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/src/web.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/a11y_capture.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/drag_spike.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/fingerprint_capture.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/fixtures/windows-start.png +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/fs_observation_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/grid_scene_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/list_scene_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/network_idle_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/password_recording_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/shared_browser_shutdown_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/side_effect_http_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/static_area_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/visibility_detach_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-adapters/tests/windows_containment_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/Cargo.toml +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/agent_steps.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/author.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/clarify.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/code_author.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/doc_author.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/doc_formats/hp_alm.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/doc_formats.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/draft_assembly.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/heal.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/lib.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/llm.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/repair.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/rules.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/src/spec.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-agent/tests/model_client_http.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/Cargo.toml +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/skills/flowproof-config/SKILL.md +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/agent_flow.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/agent_surface.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/capture.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/config.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/doctor.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/main.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/suite.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/suite_checkpoint.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/suite_inputs.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/suite_journal.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/src/update_check.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/a11y_selector_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/agent_conversation_anthropic_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/agent_conversation_streaming_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/agent_flow_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/agent_url_record_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/api_pipeline.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/audit_record_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/author_from_code_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/calc_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/citrix_start.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/clock_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/config_cli_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/config_seed_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/config_skill_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/cookie_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/doctor_ai_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/doctor_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/documented_flags.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/egress_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/examples_resolve.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/export_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_arg_matchers.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_audit_since.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_call_order.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_capability_regression.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_control_verdict.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_mcp_kill.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_no_tool_call.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_reply_contains.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/falsifiability_side_effect.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/fiori_field_commit_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/grid_cell_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/heal_apply_proposal.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/heal_vars_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/iframe_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/llm_author_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/mcp_http_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/mcp_stdio_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/notepad_author_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/notepad_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/page_title_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/pinned_random_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/record_progress.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/sap_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/sap_pipeline.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/sap_sim_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/scoped_container_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/select_missing_option_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/sigterm_shared_browser_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/skip_unless_env.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/suite_checkpoint.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/suite_env_from.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/suite_flow_isolation.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/suite_missing_trace.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/suite_policy.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/support/sap_simulator.py +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/visibility_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/vision_pipeline.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/vision_start_replay.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-cli/tests/web_e2e.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/Cargo.toml +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/app.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/backend.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/gdi.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/lib.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/mock.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/oob.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/recording.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/redact.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/surface.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/uia_scan.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/visual.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-driver/src/window.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-python/Cargo.toml +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-python/src/lib.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-replay/Cargo.toml +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-replay/src/lib.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-replay/src/report.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-replay/src/runrecord.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-replay/tests/replay_calc.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/Cargo.toml +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/schema/cassette-v1.schema.json +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/schema/side-effect-v1.schema.json +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/schema/trace-v1.schema.json +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/captures.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/cassette.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/cassette_diff.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/egress.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/format.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/lib.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/secret.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/secret_scan.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/side_effect.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/substitution.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/toolcalls.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/src/tosca.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/cassette_conformance.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/fixtures/guarded.trace.jsonl +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/fixtures/multi.trace.jsonl +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/fixtures/repeat.trace.jsonl +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/fixtures/sample.trace.jsonl +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/schema_conformance.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/side_effect_conformance.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/crates/flowproof-trace/tests/tosca_export.rs +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/flowproof/cli.py +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/flowproof/flow.py +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/flowproof/mcp_server.py +0 -0
- {flowproof-0.23.26 → flowproof-0.23.28}/flowproof/py.typed +0 -0
|
@@ -930,7 +930,7 @@ dependencies = [
|
|
|
930
930
|
|
|
931
931
|
[[package]]
|
|
932
932
|
name = "flowproof-adapters"
|
|
933
|
-
version = "0.23.
|
|
933
|
+
version = "0.23.28"
|
|
934
934
|
dependencies = [
|
|
935
935
|
"anyhow",
|
|
936
936
|
"flowproof-driver",
|
|
@@ -949,7 +949,7 @@ dependencies = [
|
|
|
949
949
|
|
|
950
950
|
[[package]]
|
|
951
951
|
name = "flowproof-agent"
|
|
952
|
-
version = "0.23.
|
|
952
|
+
version = "0.23.28"
|
|
953
953
|
dependencies = [
|
|
954
954
|
"base64",
|
|
955
955
|
"chrono",
|
|
@@ -968,7 +968,7 @@ dependencies = [
|
|
|
968
968
|
|
|
969
969
|
[[package]]
|
|
970
970
|
name = "flowproof-cli"
|
|
971
|
-
version = "0.23.
|
|
971
|
+
version = "0.23.28"
|
|
972
972
|
dependencies = [
|
|
973
973
|
"ab_glyph",
|
|
974
974
|
"chrono",
|
|
@@ -994,7 +994,7 @@ dependencies = [
|
|
|
994
994
|
|
|
995
995
|
[[package]]
|
|
996
996
|
name = "flowproof-driver"
|
|
997
|
-
version = "0.23.
|
|
997
|
+
version = "0.23.28"
|
|
998
998
|
dependencies = [
|
|
999
999
|
"calamine",
|
|
1000
1000
|
"flowproof-trace",
|
|
@@ -1010,7 +1010,7 @@ dependencies = [
|
|
|
1010
1010
|
|
|
1011
1011
|
[[package]]
|
|
1012
1012
|
name = "flowproof-python"
|
|
1013
|
-
version = "0.23.
|
|
1013
|
+
version = "0.23.28"
|
|
1014
1014
|
dependencies = [
|
|
1015
1015
|
"flowproof-agent",
|
|
1016
1016
|
"flowproof-cli",
|
|
@@ -1022,7 +1022,7 @@ dependencies = [
|
|
|
1022
1022
|
|
|
1023
1023
|
[[package]]
|
|
1024
1024
|
name = "flowproof-replay"
|
|
1025
|
-
version = "0.23.
|
|
1025
|
+
version = "0.23.28"
|
|
1026
1026
|
dependencies = [
|
|
1027
1027
|
"chrono",
|
|
1028
1028
|
"flowproof-agent",
|
|
@@ -1036,7 +1036,7 @@ dependencies = [
|
|
|
1036
1036
|
|
|
1037
1037
|
[[package]]
|
|
1038
1038
|
name = "flowproof-trace"
|
|
1039
|
-
version = "0.23.
|
|
1039
|
+
version = "0.23.28"
|
|
1040
1040
|
dependencies = [
|
|
1041
1041
|
"jsonschema",
|
|
1042
1042
|
"regex",
|
|
@@ -22,7 +22,7 @@ members = ["crates/flowproof-driver", "crates/flowproof-trace", "crates/flowproo
|
|
|
22
22
|
exclude = ["spike/windows-containment"]
|
|
23
23
|
|
|
24
24
|
[workspace.package]
|
|
25
|
-
version = "0.23.
|
|
25
|
+
version = "0.23.28"
|
|
26
26
|
edition = "2021"
|
|
27
27
|
license = "Apache-2.0"
|
|
28
28
|
repository = "https://github.com/automators-com/flowproof"
|
|
@@ -98,6 +98,13 @@ pub trait SapEngine {
|
|
|
98
98
|
/// VKey table has no Alt chords, so this is the only way to send it.
|
|
99
99
|
fn close_window(&mut self) -> Result<(), DriverError>;
|
|
100
100
|
fn screen_size(&mut self) -> Result<(u32, u32), DriverError>;
|
|
101
|
+
/// Maximize the connected session's main window and bring it to the
|
|
102
|
+
/// foreground, as a headed browser is at launch, so a person watching
|
|
103
|
+
/// the run sees the screen being driven. Scripting works on a window
|
|
104
|
+
/// that is behind others, so this is for the watcher, not the driver.
|
|
105
|
+
fn present_window(&mut self) -> Result<(), DriverError> {
|
|
106
|
+
Ok(())
|
|
107
|
+
}
|
|
101
108
|
/// The session's `Info`: where the user is, for a failure report.
|
|
102
109
|
/// `Ok(None)` when the engine cannot say.
|
|
103
110
|
fn session_info(&mut self) -> Result<Option<SapSessionInfo>, DriverError> {
|
|
@@ -307,7 +314,15 @@ impl<E: SapEngine> AppDriver for SapAppDriver<E> {
|
|
|
307
314
|
// `command` carries the SAP Logon connection description (may be
|
|
308
315
|
// empty: attach to whatever logged-in session exists).
|
|
309
316
|
self.engine
|
|
310
|
-
.connect(command, self.credentials.as_ref(), timeout)
|
|
317
|
+
.connect(command, self.credentials.as_ref(), timeout)?;
|
|
318
|
+
// Every launch presents, including a multi-surface return visit, so
|
|
319
|
+
// coming back from the browser puts SAP in front again. A window
|
|
320
|
+
// that will not come forward is no reason to fail a run that can
|
|
321
|
+
// still drive it.
|
|
322
|
+
if let Err(error) = self.engine.present_window() {
|
|
323
|
+
eprintln!("sap: could not bring the SAP GUI window to the front: {error}");
|
|
324
|
+
}
|
|
325
|
+
Ok(())
|
|
311
326
|
}
|
|
312
327
|
|
|
313
328
|
fn stage_credentials(&mut self, credentials: LoginCredentials) -> Result<(), DriverError> {
|
|
@@ -577,6 +592,10 @@ pub mod fake {
|
|
|
577
592
|
pub vkeys: Vec<u16>,
|
|
578
593
|
pub set_texts: Vec<(String, String)>,
|
|
579
594
|
pub session_info: Option<SapSessionInfo>,
|
|
595
|
+
/// How many times the window was maximized and brought to the front.
|
|
596
|
+
pub presented: u32,
|
|
597
|
+
/// Make `present_window` fail, as a refused foreground would.
|
|
598
|
+
pub refuse_present: bool,
|
|
580
599
|
}
|
|
581
600
|
|
|
582
601
|
impl FakeEngine {
|
|
@@ -693,6 +712,18 @@ pub mod fake {
|
|
|
693
712
|
Ok((1920, 1080))
|
|
694
713
|
}
|
|
695
714
|
|
|
715
|
+
/// Only a connected session has a window to present.
|
|
716
|
+
fn present_window(&mut self) -> Result<(), DriverError> {
|
|
717
|
+
if self.connected.is_none() {
|
|
718
|
+
return Err(DriverError::Uia("fake sap: not connected".into()));
|
|
719
|
+
}
|
|
720
|
+
if self.refuse_present {
|
|
721
|
+
return Err(DriverError::Uia("fake sap: foreground refused".into()));
|
|
722
|
+
}
|
|
723
|
+
self.presented += 1;
|
|
724
|
+
Ok(())
|
|
725
|
+
}
|
|
726
|
+
|
|
696
727
|
fn session_info(&mut self) -> Result<Option<SapSessionInfo>, DriverError> {
|
|
697
728
|
Ok(self.session_info.clone())
|
|
698
729
|
}
|
|
@@ -748,6 +779,7 @@ pub mod com {
|
|
|
748
779
|
use std::time::{Duration, Instant};
|
|
749
780
|
|
|
750
781
|
use windows::core::{Interface, BSTR, GUID, PCWSTR};
|
|
782
|
+
use windows::Win32::Foundation::HWND;
|
|
751
783
|
use windows::Win32::System::Com::{
|
|
752
784
|
CoInitializeEx, CoTaskMemFree, CreateBindCtx, GetRunningObjectTable, IDispatch,
|
|
753
785
|
COINIT_APARTMENTTHREADED, DISPATCH_FLAGS, DISPATCH_METHOD, DISPATCH_PROPERTYGET,
|
|
@@ -755,7 +787,9 @@ pub mod com {
|
|
|
755
787
|
};
|
|
756
788
|
use windows::Win32::System::Ole::DISPID_PROPERTYPUT;
|
|
757
789
|
use windows::Win32::System::Variant::VARIANT;
|
|
758
|
-
use windows::Win32::UI::WindowsAndMessaging::{
|
|
790
|
+
use windows::Win32::UI::WindowsAndMessaging::{
|
|
791
|
+
GetSystemMetrics, SetForegroundWindow, SM_CXSCREEN, SM_CYSCREEN,
|
|
792
|
+
};
|
|
759
793
|
|
|
760
794
|
use super::{SapElement, SapEngine, SapSessionInfo};
|
|
761
795
|
use flowproof_driver::{DriverError, LoginCredentials};
|
|
@@ -1462,6 +1496,25 @@ pub mod com {
|
|
|
1462
1496
|
Ok((w.unsigned_abs().max(1), h.unsigned_abs().max(1)))
|
|
1463
1497
|
}
|
|
1464
1498
|
|
|
1499
|
+
/// The main window, not the active one: a popup open at launch is
|
|
1500
|
+
/// owned by `wnd[0]` and comes forward with it, and maximizing the
|
|
1501
|
+
/// popup itself would fill the screen with a dialog.
|
|
1502
|
+
fn present_window(&mut self) -> Result<(), DriverError> {
|
|
1503
|
+
let window = self
|
|
1504
|
+
.find_disp("wnd[0]")?
|
|
1505
|
+
.ok_or_else(|| DriverError::Uia("sap-com: no main window".into()))?;
|
|
1506
|
+
// Maximize also restores a minimized window.
|
|
1507
|
+
window.call("Maximize", vec![])?;
|
|
1508
|
+
// `Handle` is the window's HWND as a Long; Windows sign-extends
|
|
1509
|
+
// 32-bit handles, so the cast is the documented conversion.
|
|
1510
|
+
if let Some(handle) = window.get_i32("Handle").filter(|handle| *handle != 0) {
|
|
1511
|
+
// Foregrounding can be refused (focus-steal prevention); the
|
|
1512
|
+
// window is still maximized and drivable, so not an error.
|
|
1513
|
+
let _ = unsafe { SetForegroundWindow(HWND(handle as isize as *mut _)) };
|
|
1514
|
+
}
|
|
1515
|
+
Ok(())
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1465
1518
|
fn session_info(&mut self) -> Result<Option<SapSessionInfo>, DriverError> {
|
|
1466
1519
|
let info = self.session()?.get_disp("Info")?;
|
|
1467
1520
|
Ok(Some(SapSessionInfo {
|
|
@@ -1813,6 +1866,33 @@ mod tests {
|
|
|
1813
1866
|
);
|
|
1814
1867
|
}
|
|
1815
1868
|
|
|
1869
|
+
/// A person watching a run sees SAP in front, as they see the browser,
|
|
1870
|
+
/// and again on every return visit from another surface.
|
|
1871
|
+
#[test]
|
|
1872
|
+
fn every_launch_brings_the_connected_window_to_the_front() {
|
|
1873
|
+
let mut driver = driver();
|
|
1874
|
+
driver
|
|
1875
|
+
.launch("TS3", "SAP", Duration::from_secs(1))
|
|
1876
|
+
.expect("connects");
|
|
1877
|
+
assert_eq!(driver.engine.presented, 1);
|
|
1878
|
+
driver
|
|
1879
|
+
.launch("TS3", "SAP", Duration::from_secs(1))
|
|
1880
|
+
.expect("re-attaches");
|
|
1881
|
+
assert_eq!(driver.engine.presented, 2);
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
#[test]
|
|
1885
|
+
fn a_window_that_will_not_come_forward_does_not_fail_the_launch() {
|
|
1886
|
+
let mut driver = SapAppDriver::with_engine(FakeEngine {
|
|
1887
|
+
refuse_present: true,
|
|
1888
|
+
..FakeEngine::with_elements(order_screen())
|
|
1889
|
+
});
|
|
1890
|
+
driver
|
|
1891
|
+
.launch("TS3", "SAP", Duration::from_secs(1))
|
|
1892
|
+
.expect("scripting still drives a window behind others");
|
|
1893
|
+
assert_eq!(driver.engine.connected.as_deref(), Some("TS3"));
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1816
1896
|
/// A password that reaches a log line or an error is a password that
|
|
1817
1897
|
/// reaches CI output.
|
|
1818
1898
|
#[test]
|
|
@@ -1562,8 +1562,13 @@ fn driver_key_mod(m: &flowproof_trace::format::KeyModifier) -> flowproof_driver:
|
|
|
1562
1562
|
/// the inverse of `selectors_for` for the shapes the recorder itself
|
|
1563
1563
|
/// emits. `None` = not reconstructable (reuse falls back to fresh
|
|
1564
1564
|
/// authoring, never to a guess).
|
|
1565
|
+
///
|
|
1566
|
+
/// An `a11y` selector is not the target the step was recorded with: it is a
|
|
1567
|
+
/// hint [`enrich_a11y_hint`] prepends afterwards so replay tries it first,
|
|
1568
|
+
/// and it carries a role and name, not a target. Read past it, or every web
|
|
1569
|
+
/// step with an accessible name is authored again on reuse.
|
|
1565
1570
|
fn target_from_selector(selectors: &[Selector]) -> Option<Target> {
|
|
1566
|
-
let primary = selectors.
|
|
1571
|
+
let primary = selectors.iter().find(|s| s.tier != SelectorTier::A11y)?;
|
|
1567
1572
|
let get = |key: &str| primary.payload.get(key).and_then(|v| v.as_str());
|
|
1568
1573
|
let base = if let Some(css) = get("css") {
|
|
1569
1574
|
Target::css(css)
|
|
@@ -3108,6 +3113,12 @@ struct LiveSteps {
|
|
|
3108
3113
|
finished: usize,
|
|
3109
3114
|
current: Option<(String, std::time::Instant)>,
|
|
3110
3115
|
lines: Vec<String>,
|
|
3116
|
+
/// The trace steps of every flow step that passed, kept as each one
|
|
3117
|
+
/// finishes so a failure can hand them back (see
|
|
3118
|
+
/// [`LocatedRecordError::completed`]).
|
|
3119
|
+
completed: Vec<Step>,
|
|
3120
|
+
/// How far into the recorder's `steps` [`Self::completed`] has read.
|
|
3121
|
+
kept_through: usize,
|
|
3111
3122
|
}
|
|
3112
3123
|
|
|
3113
3124
|
impl LiveSteps {
|
|
@@ -3122,6 +3133,20 @@ impl LiveSteps {
|
|
|
3122
3133
|
self.current = Some((intent.to_string(), std::time::Instant::now()));
|
|
3123
3134
|
}
|
|
3124
3135
|
|
|
3136
|
+
/// Keep the trace steps the flow step that just passed added, less the
|
|
3137
|
+
/// writes the page refused - the same pruning the trace gets - so what a
|
|
3138
|
+
/// failure hands back is exactly what the trace would have held.
|
|
3139
|
+
fn keep_passed(&mut self, steps: &[Step], superseded: &std::collections::BTreeSet<usize>) {
|
|
3140
|
+
for (index, step) in steps.iter().enumerate().skip(self.kept_through) {
|
|
3141
|
+
if !superseded.contains(&index) {
|
|
3142
|
+
let mut step = step.clone();
|
|
3143
|
+
step.id = format!("s{:04}", self.completed.len() + 1);
|
|
3144
|
+
self.completed.push(step);
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
self.kept_through = steps.len();
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3125
3150
|
/// Closes the step in progress, if any: an error raised between steps
|
|
3126
3151
|
/// (a `when:` condition, a surface switch, writing the trace) names none.
|
|
3127
3152
|
fn finish(&mut self, mark: &str) {
|
|
@@ -3188,6 +3213,11 @@ pub struct LocatedRecordError {
|
|
|
3188
3213
|
pub error: RecordError,
|
|
3189
3214
|
/// Boxed so a `Result` carrying this stays small (clippy::result_large_err).
|
|
3190
3215
|
pub at: Box<FailurePoint>,
|
|
3216
|
+
/// The trace steps of every flow step that passed before the failure,
|
|
3217
|
+
/// pruned as the trace would be. Nothing of the step that failed is in
|
|
3218
|
+
/// it, even the actions it got through. Held in memory only: a failed
|
|
3219
|
+
/// recording writes no trace, and this is not one.
|
|
3220
|
+
pub completed: Vec<Step>,
|
|
3191
3221
|
}
|
|
3192
3222
|
|
|
3193
3223
|
impl From<RecordError> for LocatedRecordError {
|
|
@@ -3195,6 +3225,7 @@ impl From<RecordError> for LocatedRecordError {
|
|
|
3195
3225
|
Self {
|
|
3196
3226
|
error,
|
|
3197
3227
|
at: Box::default(),
|
|
3228
|
+
completed: Vec::new(),
|
|
3198
3229
|
}
|
|
3199
3230
|
}
|
|
3200
3231
|
}
|
|
@@ -3257,6 +3288,7 @@ fn record_with_reuse_located<D: AppDriver, C: ModelClient>(
|
|
|
3257
3288
|
step,
|
|
3258
3289
|
screen: driver.failure_screen(),
|
|
3259
3290
|
}),
|
|
3291
|
+
completed: std::mem::take(&mut live.completed),
|
|
3260
3292
|
}
|
|
3261
3293
|
})
|
|
3262
3294
|
}
|
|
@@ -4915,6 +4947,7 @@ fn record_steps<D: AppDriver, C: ModelClient>(
|
|
|
4915
4947
|
if let Some(last) = prior_history.last_mut() {
|
|
4916
4948
|
*last = step_history(&step_intent, &executed_summaries, is_check);
|
|
4917
4949
|
}
|
|
4950
|
+
live.keep_passed(&steps, &superseded_steps);
|
|
4918
4951
|
live.finish("PASS");
|
|
4919
4952
|
}
|
|
4920
4953
|
|
|
@@ -6041,6 +6074,82 @@ steps:
|
|
|
6041
6074
|
std::fs::remove_file(&out).ok();
|
|
6042
6075
|
}
|
|
6043
6076
|
|
|
6077
|
+
/// What a failed recording hands back is what a repair retry replays:
|
|
6078
|
+
/// every step that passed, none of the one that failed, so the retry
|
|
6079
|
+
/// asks the model only about the step it patched.
|
|
6080
|
+
#[test]
|
|
6081
|
+
fn a_failed_recording_hands_back_the_steps_that_passed() {
|
|
6082
|
+
let spec = FlowSpec::parse(
|
|
6083
|
+
"name: Signup\napp: web\nurl: https://example.test\nsteps:\n \
|
|
6084
|
+
- Fill in the email and continue\n - Save the profile\n - Publish it\n",
|
|
6085
|
+
)
|
|
6086
|
+
.expect("spec parses");
|
|
6087
|
+
let scene = r##"[{"target":"css:#email","tag":"input","label":"Email"},
|
|
6088
|
+
{"target":"css:#next","tag":"button","text":"Next"},
|
|
6089
|
+
{"target":"css:#save","tag":"button","text":"Save"},
|
|
6090
|
+
{"target":"css:#publish","tag":"button","text":"Publish"}]"##;
|
|
6091
|
+
let dir = std::env::temp_dir().join("flowproof-failed-hands-back-passed");
|
|
6092
|
+
std::fs::create_dir_all(&dir).expect("temp dir");
|
|
6093
|
+
let out = dir.join("signup.trace.jsonl");
|
|
6094
|
+
let _ = std::fs::remove_file(&out);
|
|
6095
|
+
let options = flowproof_driver::RecordingOptions::default;
|
|
6096
|
+
|
|
6097
|
+
// The model answers the first two steps and has nothing for the third.
|
|
6098
|
+
let mut driver = MockAppDriver::new(&["#email", "#next", "#save", "#publish"]);
|
|
6099
|
+
driver.scene = Some(scene.into());
|
|
6100
|
+
let mut client = ScriptedClient::new(&[
|
|
6101
|
+
r##"[{"action":"type_text","target":"css:#email","text":"me@work.example"},
|
|
6102
|
+
{"action":"click","target":"css:#next"}]"##,
|
|
6103
|
+
r##"[{"action":"click","target":"css:#save"}]"##,
|
|
6104
|
+
]);
|
|
6105
|
+
let failed = record_with_reuse_located(
|
|
6106
|
+
&spec,
|
|
6107
|
+
&mut driver,
|
|
6108
|
+
&out,
|
|
6109
|
+
Author::Auto,
|
|
6110
|
+
Some(&mut client),
|
|
6111
|
+
None,
|
|
6112
|
+
options(),
|
|
6113
|
+
)
|
|
6114
|
+
.expect_err("the third step has no answer");
|
|
6115
|
+
let intents: Vec<&str> = failed.completed.iter().map(|s| s.intent.as_str()).collect();
|
|
6116
|
+
assert_eq!(
|
|
6117
|
+
intents,
|
|
6118
|
+
[
|
|
6119
|
+
"Fill in the email and continue",
|
|
6120
|
+
"Fill in the email and continue",
|
|
6121
|
+
"Save the profile"
|
|
6122
|
+
]
|
|
6123
|
+
);
|
|
6124
|
+
let ids: Vec<&str> = failed.completed.iter().map(|s| s.id.as_str()).collect();
|
|
6125
|
+
assert_eq!(ids, ["s0001", "s0002", "s0003"]);
|
|
6126
|
+
assert!(!out.exists(), "a failed recording writes no trace");
|
|
6127
|
+
|
|
6128
|
+
// Replaying them, the model is asked about the third step only.
|
|
6129
|
+
let mut driver = MockAppDriver::new(&["#email", "#next", "#save", "#publish"]);
|
|
6130
|
+
driver.scene = Some(scene.into());
|
|
6131
|
+
let mut client =
|
|
6132
|
+
ScriptedClient::new(&[r##"[{"action":"click","target":"css:#publish"}]"##]);
|
|
6133
|
+
let summary = record_with_reuse_located(
|
|
6134
|
+
&spec,
|
|
6135
|
+
&mut driver,
|
|
6136
|
+
&out,
|
|
6137
|
+
Author::Auto,
|
|
6138
|
+
Some(&mut client),
|
|
6139
|
+
Some(&failed.completed),
|
|
6140
|
+
options(),
|
|
6141
|
+
)
|
|
6142
|
+
.expect("the retry records");
|
|
6143
|
+
assert_eq!(
|
|
6144
|
+
client.prompts.len(),
|
|
6145
|
+
1,
|
|
6146
|
+
"only the step that failed is authored"
|
|
6147
|
+
);
|
|
6148
|
+
assert_eq!(summary.reused_steps, 3);
|
|
6149
|
+
assert_eq!(summary.steps, 4);
|
|
6150
|
+
std::fs::remove_dir_all(&dir).ok();
|
|
6151
|
+
}
|
|
6152
|
+
|
|
6044
6153
|
/// The rejected-form guard is scoped to model authoring, and stays that
|
|
6045
6154
|
/// way.
|
|
6046
6155
|
///
|
|
@@ -7049,6 +7158,37 @@ steps:
|
|
|
7049
7158
|
std::fs::remove_dir_all(&dir).ok();
|
|
7050
7159
|
}
|
|
7051
7160
|
|
|
7161
|
+
#[test]
|
|
7162
|
+
fn a_step_with_an_a11y_hint_still_decodes_for_reuse() {
|
|
7163
|
+
let click = ResolvedAction::Press {
|
|
7164
|
+
target: Target::css("#greet"),
|
|
7165
|
+
label: "Press the greet button".into(),
|
|
7166
|
+
dialog: None,
|
|
7167
|
+
};
|
|
7168
|
+
let mut step = step_for(1, "Press the greet button", "web", &click);
|
|
7169
|
+
enrich_a11y_hint(
|
|
7170
|
+
&mut step,
|
|
7171
|
+
&flowproof_driver::A11yHints {
|
|
7172
|
+
role: "button".into(),
|
|
7173
|
+
name: "Greet".into(),
|
|
7174
|
+
ancestor_role: Some("RootWebArea".into()),
|
|
7175
|
+
ancestor_name: Some("Greeter".into()),
|
|
7176
|
+
},
|
|
7177
|
+
);
|
|
7178
|
+
assert_eq!(step.selectors[0].tier, SelectorTier::A11y, "the hint leads");
|
|
7179
|
+
assert_eq!(decode_step(&step), Some(click));
|
|
7180
|
+
|
|
7181
|
+
let only_a_hint = Step {
|
|
7182
|
+
selectors: step.selectors[..1].to_vec(),
|
|
7183
|
+
..step
|
|
7184
|
+
};
|
|
7185
|
+
assert_eq!(
|
|
7186
|
+
decode_step(&only_a_hint),
|
|
7187
|
+
None,
|
|
7188
|
+
"a role and name alone are not a target to reuse"
|
|
7189
|
+
);
|
|
7190
|
+
}
|
|
7191
|
+
|
|
7052
7192
|
#[test]
|
|
7053
7193
|
fn upload_and_right_click_decode_back_for_reuse() {
|
|
7054
7194
|
// decode_step must be a strict inverse of step_for so `record
|