Skip to content

Overlay Mapper: rendered status test, wiring lint, capture→save end to end - #1327

Merged
malpern merged 1 commit into
masterfrom
test/overlay-mapper-coverage
Oct 20, 2026
Merged

malpern merged 1 commit into
masterfrom
test/overlay-mapper-coverage

Conversation

@malpern

@malpern malpern commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Follow-up to #1326. The hardware check found a declined save that nothing showed and nothing logged; the engine's tests assert on the model, so they could not see it. This covers the overlay seam in three layers:

  • Rendered. MapperStatusLine (extracted from OverlayMapperSection, identical output) is hosted for real: a message takes space and draws glyphs, an error draws in red, nil draws nothing. SwiftUI publishes no accessibility tree in-process (verified: a windowed host exposes one empty AXGroup), so the test rasterizes rather than reading text; exact appearance stays with the opt-in snapshot target.
  • Lint (OverlayMapperStatusLintTests): the overlay must render viewModel.statusMessage, and every early return in MapperViewModel.save must be preceded by a status and a log line. It immediately caught the app-specific save failure path, which had a status but no log — fixed.
  • End to end (MediaKeyCaptureToSaveTests): a synthesized system-defined event (Mute, Volume Up, Brightness Up, Play/Pause) goes through KeyboardCapture.mediaKeyPress(from:) (new pure seam, same logic) → MapperViewModel → save → generated config against the real bridge; asserts the stored rule is the Kanata key and the config never contains (macro.

Doc note in rules-admission-review.md. KEYPATH_TEST_ENFORCE_CLEAN_SUMMARY=1 Scripts/run-tests-safe.sh: 5,791 passed, 0 app warnings/errors, 0 build warnings. check-accessibility.py clean.

🤖 Generated with Claude Code

…o end

The hardware check found a declined save that nothing showed and nothing
logged; the engine's tests could not see it because they assert on the
model. Three layers now cover the overlay seam: MapperStatusLine (extracted
from OverlayMapperSection) is rendered for real and must draw a message,
draw errors in red and draw nothing for nil; a lint keeps the overlay reading
viewModel.statusMessage and keeps save() from returning without a status and
a log line (it immediately found the unlogged app-specific failure path,
fixed here); and MediaKeyCaptureToSaveTests drives a synthesized
system-defined Mute/volume/brightness/play event through KeyboardCapture
(now a pure `mediaKeyPress(from:)` seam) → Mapper → save → generated config
against the real bridge. SwiftUI publishes no accessibility tree in-process,
so the rendered test rasterizes rather than reading text; appearance stays
with the opt-in snapshot target.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T14:28:40.771980Z add5f75 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Oct 20, 2026

Copy link
Copy Markdown

Review of the diff only. No blocking correctness issues in the production changes: the mediaKeyPress extraction keeps the old behavior, and the new log line is fine. Tests and cleanup:

  1. MediaKeyCaptureToSaveTests.testCapturedMediaKeyBecomesAPostableRule reuses one coordinator and one input key (grave) across all four loop iterations.

    • After iteration 1, a grave → mute custom rule already exists.
    • Iteration 2 either replaces it or is flagged as a conflict or duplicate by admission.
    • If the rule is not replaced, customRules.first { $0.input == "grv" ... } returns the stale mute rule, so the .keystroke(key: "volu") assertion fails. If it is replaced, the test passes only because of that behavior.
    • Reset the rules per iteration, or use a distinct input key per media key, so each case is independent.
  2. OverlayMapperStatusLintTests.testMapperSaveNeverReturnsSilently

    • components(separatedBy: "func save(kanataManager:")[1] traps with an index-out-of-range crash if the signature is renamed, instead of failing with a message. Guard it with XCTUnwrap or a count check.
    • The "6 lines before return" window and the == "return" match are brittle. They miss return with a value, and one earlier status can satisfy a later unrelated return.
  3. MapperStatusLineTests

    • These tests render offscreen and sample pixels. Their results depend on appearance (light or dark), color space and headless CI rendering, so they may be flaky.
    • The red-detection thresholds (>150, <110) depend on how Color.red renders in the current appearance. Consider pinning NSAppearance in the host view.
    • testNoMessageRendersNothing depends on fittingSize.height == 0 for a width-only .frame around an empty view. Verify this on the CI Xcode.
  4. Nit, KeyboardCapture.swift: the old /// Handle media key events... doc comment is now stacked above the new mediaKeyPress doc. It describes handleMediaKeyEvent, so move it down to that function.

@malpern
malpern merged commit ffedc05 into master Oct 20, 2026
6 checks passed
@malpern
malpern deleted the test/overlay-mapper-coverage branch October 20, 2026 14:31
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.

1 participant