Skip to content

test(hotkey): 补齐热键自动注入门禁#41

Merged
H-Chris233 merged 2 commits into
Open-Less:mainfrom
H-Chris233:main
Apr 30, 2026
Merged

test(hotkey): 补齐热键自动注入门禁#41
H-Chris233 merged 2 commits into
Open-Less:mainfrom
H-Chris233:main

Conversation

@H-Chris233
Copy link
Copy Markdown
Collaborator

@H-Chris233 H-Chris233 commented Apr 30, 2026

摘要

Fixes #35

本 PR 按最小改动补齐 dev/test 专用热键注入门禁,用于验证热键事件是否能进入 coordinator 状态机。

当前 Windows 真机回归中,物理全局热键仍需要人工按键验证,CI 很难稳定证明热键事件是否进入业务链路。本 PR 新增测试注入入口,让自动化脚本可以直接走 handle_pressed / handle_released 路径,并通过 coordinator 日志断言热键按下事件已进入状态机。

本 PR 不尝试替代真实 OS hook / 物理热键测试,只补齐非人工的 coordinator 热键链路门禁。

修复 / 新增 / 改进

  • 新增 dev/test 专用热键注入入口。

  • 注入路径走真实 coordinator 处理函数:

    • handle_pressed
    • handle_released
  • handle_pressed / handle_released 增加 coordinator 日志:

    • [coord] hotkey pressed
  • dry-run 注入后会自动 cancel,避免测试流程触发长时间录音。

  • 新增检查脚本:

    • npm run check:hotkey-injection
  • 检查脚本会断言日志中包含:

    • [coord] hotkey pressed
  • README 补充 Hotkey Injection Gate 用法说明。

兼容

  • 不包含:

    • 不改变真实用户的物理热键行为。
    • 不替代 Windows 真机物理热键测试。
    • 不调整 OS hook / rdev / 平台适配实现。
    • 不引入新的运行时依赖。
    • 不改变默认热键配置。
  • 对现有用户 / 本地环境 / 构建流程的影响:

    • 对普通用户无行为影响。
    • dev/test 环境新增一个可自动化验证的热键注入门禁。
    • 物理热键测试仍作为额外真机门禁保留,用于覆盖 OS hook 层。
    • CI 或本地 push 前可以通过 npm run check:hotkey-injection 验证 coordinator 热键链路。

测试计划

  • 命令:npm run check:hotkey-injection

  • 结果:通过

  • 证据路径:本地脚本输出,日志包含 [coord] hotkey pressed

  • 命令:npm run build

  • 结果:通过

  • 证据路径:本地构建输出

  • 命令:cargo check

  • 结果:通过

  • 证据路径:本地检查输出

  • 命令:cargo check --release

  • 结果:通过

  • 证据路径:本地 release 检查输出

  • 命令:git diff --check

  • 结果:通过

  • 证据路径:本地命令输出

备注

本 PR 是针对 issue #35 的最小闭环修复:只补齐热键事件进入 coordinator 状态机的自动化门禁,不扩大到真实 Windows OS hook 行为验证。真实物理热键链路仍需要 Windows 真机测试覆盖。

Summary by Sourcery

Add a dev/test-only hotkey injection gate to validate that hotkey events reach the coordinator state machine without requiring physical hotkey input.

New Features:

  • Introduce a coordinator API and Tauri command to inject a synthetic hotkey click in dev/test builds, exercising the real hotkey handling path and automatically cancelling the session.
  • Add a Node-based npm run check:hotkey-injection script that runs a Rust test and asserts coordinator logs contain the hotkey pressed marker.

Enhancements:

  • Log coordinator hotkey press and release events with mode and phase context, and add a dry-run path that skips microphone permission and recording when an environment flag is set.

Documentation:

  • Document the Hotkey Injection Gate and its usage in the README.

Tests:

  • Add a Rust async test to cover the hotkey injection gate, ensuring it leaves the coordinator session phase idle after a dry-run.

feat(settings): 硅基流动 ASR/LLM 配置 + 关于页完善 + onAsrProviderChange bug 修复
Windows regression currently depends on a tester pressing a real modifier key, which cannot distinguish OS-hook delivery failures from coordinator state-machine failures. Add a debug/test-only injection path that drives handle_pressed and handle_released, logs the coordinator hotkey edge, and cancels the dry-run session automatically.

Constraint: Must not rely on SendInput or a physical keyboard for the coordinator gate.

Constraint: Keep physical hotkey testing as the OS hook coverage layer.

Rejected: Fake log-only gate | would not exercise the coordinator hotkey path.

Rejected: Production IPC command | unnecessary surface area for a test/dev-only gate.

Confidence: high

Scope-risk: narrow

Tested: npm run check:hotkey-injection

Tested: npm run build

Tested: cargo check

Tested: cargo check --release

Tested: git diff --check

Related: Open-Less#35
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 30, 2026

Reviewer's Guide

Adds a dev/test-only hotkey injection gate that drives the real coordinator hotkey handlers, logs hotkey transitions, supports a dry-run mode via env var, wires it through a Tauri command and npm script, and provides a focused test and docs to validate that hotkey events reach the coordinator state machine without starting a real recording.

Sequence diagram for the dev/test hotkey injection gate flow

sequenceDiagram
    actor Dev as DevEngineer
    participant Npm as NpmScript_check_hotkey_injection
    participant Node as Node_check_hotkey_injection_mjs
    participant Cargo as Cargo_test
    participant Test as RustTest_hotkey_injection_gate_logs_pressed_and_cancels
    participant Coord as Coordinator
    participant Inner as Inner

    Dev->>Npm: npm run check:hotkey-injection
    Npm->>Node: node scripts/check-hotkey-injection.mjs
    Node->>Cargo: cargo test hotkey_injection_gate_logs_pressed_and_cancels
    Cargo->>Test: Run async tokio test
    Test->>Test: set OPENLESS_HOTKEY_INJECTION_DRY_RUN=1
    Test->>Coord: Coordinator::new
    Test->>Coord: inject_hotkey_click_for_dev()
    activate Coord
    Coord->>Coord: log "[coord] dev hotkey injection started"
    Coord->>Inner: handle_pressed(&inner)
    activate Inner
    Inner->>Inner: log "[coord] hotkey pressed (mode, phase)"
    Inner->>Inner: begin_session(&inner)
    activate Inner
    Inner->>Inner: hotkey_injection_dry_run_enabled()
    Inner-->>Inner: true (env var is set)
    Inner->>Inner: emit_capsule(Recording)
    Inner->>Inner: state.phase = Listening
    Inner->>Inner: log "[coord] session started (hotkey-injection dry-run)"
    deactivate Inner
    Inner-->>Coord: Ok(())
    Inner->>Inner: handle_released(&inner)
    activate Inner
    Inner->>Inner: log "[coord] hotkey released (mode, phase)"
    Inner-->>Inner: end_session skipped (dry-run already cancelled later)
    deactivate Inner
    Coord->>Inner: cancel_session(&inner)
    Inner->>Inner: state.phase = Idle
    deactivate Inner
    Coord-->>Test: Ok(())
    deactivate Coord
    Test->>Test: assert state.phase == Idle
    Test-->>Cargo: test passed
    Cargo-->>Node: exit status 0, logs
    Node->>Node: check output contains "[coord] hotkey pressed"
    Node-->>Npm: success
    Npm-->>Dev: Hotkey injection gate passed
Loading

Updated class diagram for Coordinator hotkey injection and dry-run gate

classDiagram
    class Coordinator {
        +new() Coordinator
        +cancel_dictation()
        +inject_hotkey_click_for_dev() Result
    }

    class Inner {
        +prefs
        +state
        +vocab
        +asr
    }

    class SessionPhase {
        <<enum>>
        Idle
        Listening
        Transcribing
        Completed
        Error
    }

    class HotkeyMode {
        <<enum>>
        Toggle
        Hold
    }

    class CapsuleState {
        <<enum>>
        Idle
        Recording
        Error
    }

    class CoordinatorModuleHelpers {
        +handle_pressed(inner)
        +handle_released(inner)
        +begin_session(inner) Result
        +cancel_session(inner)
        +hotkey_injection_dry_run_enabled() bool
        +emit_capsule(inner,state,progress,elapsed,error,extra)
    }

    class CommandsModule {
        +cancel_dictation(coord)
        +inject_hotkey_click_for_dev(coord) Result
    }

    class HotkeyInjectionTests {
        +hotkey_injection_gate_logs_pressed_and_cancels() async
    }

    Coordinator --> Inner : inner
    CoordinatorModuleHelpers --> Inner : uses
    CoordinatorModuleHelpers --> SessionPhase : updates_phase
    CoordinatorModuleHelpers --> HotkeyMode : reads_mode
    CoordinatorModuleHelpers --> CapsuleState : emits_state
    CommandsModule --> Coordinator : delegates_methods
    HotkeyInjectionTests --> Coordinator : constructs_and_calls
    HotkeyInjectionTests --> SessionPhase : asserts_Idle_phase
    CoordinatorModuleHelpers --> HotkeyInjectionTests : logged_by_test
    Coordinator --> CoordinatorModuleHelpers : calls_helpers
Loading

File-Level Changes

Change Details Files
Introduce a dev/test-only coordinator hotkey injection entry that exercises real hotkey handlers and cancels afterward.
  • Add Coordinator::inject_hotkey_click_for_dev (debug/test only) to invoke handle_pressed, handle_released, then cancel_session.
  • Ensure the injected path uses existing coordinator behavior instead of a mock code path.
  • Make the injection async and return a Result<String, String> for use from Tauri commands.
openless-all/app/src-tauri/src/coordinator.rs
openless-all/app/src-tauri/src/commands.rs
Add logging and a dry-run gate around hotkey-driven session lifecycle for automated verification.
  • Log info messages on hotkey press and release including mode and current phase for traceability.
  • Introduce hotkey_injection_dry_run_enabled (debug/test only) reading OPENLESS_HOTKEY_INJECTION_DRY_RUN to short-circuit begin_session.
  • In dry-run mode, emit a Recording capsule, set phase to Listening, log a dry-run session start, and return early without microphone checks or long-running recording.
  • Slightly refactor AudioConsumer construction formatting for clarity.
openless-all/app/src-tauri/src/coordinator.rs
Expose the hotkey injection gate to the dev runtime via a Tauri command and npm script.
  • Add a debug-only Tauri command inject_hotkey_click_for_dev that forwards to Coordinator::inject_hotkey_click_for_dev.
  • Register the new command in the Tauri command list under a debug_assertions cfg gate so it is not available in production builds.
  • Add an npm script check:hotkey-injection that runs a focused Rust test via cargo test with appropriate args and env.
openless-all/app/src-tauri/src/commands.rs
openless-all/app/src-tauri/src/lib.rs
openless-all/app/package.json
openless-all/app/scripts/check-hotkey-injection.mjs
Add a focused test and documentation for the hotkey injection gate behavior.
  • Introduce a tokio test hotkey_injection_gate_logs_pressed_and_cancels that sets the dry-run env var, runs the dev injection, asserts the phase returns to Idle, and cleans up the env var.
  • Initialize env_logger in the test to ensure log lines are emitted for the gate check script.
  • Document the Hotkey Injection Gate in README with usage, behavior, and example npm command.
openless-all/app/src-tauri/src/coordinator.rs
openless-all/README.md

Assessment against linked issues

Issue Objective Addressed Explanation
#35 Provide a dev/test-only hotkey injection gate that does not rely on a physical keyboard and routes through the real coordinator hotkey state machine (handle_pressed / handle_released).
#35 Emit a coordinator log on hotkey press ([coord] hotkey pressed) and expose an automated script/gate that asserts this log appears.
#35 Ensure the injected hotkey session is automatically cancelled / dry-run (no long-running real recording or user impact) while preserving existing physical hotkey tests and OS hook behavior.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@H-Chris233 H-Chris233 merged commit ac611d5 into Open-Less:main Apr 30, 2026
2 checks passed
appergb pushed a commit that referenced this pull request Apr 30, 2026
将 main 的 PR #41(coordinator hotkey 测试基础设施)合入 develop。
保留 develop 上的 i18n 变更与 main 的 hotkey 测试改动,无冲突。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[windows] 热键链路缺少非人工自动注入门禁

1 participant