diff --git a/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart b/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart index a652dfc20..f2163900d 100644 --- a/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart +++ b/flutter/lib/common/widgets/keyboard_shortcuts/shortcut_constants.dart @@ -23,7 +23,8 @@ const kShortcutActionSwitchTabPrev = 'switch_tab_prev'; /// that consumed the press also sees its repeats and its release. On Linux a /// legacy-mode session matches in Dart while a map-mode session matches in /// Rust, and neither knows the other's fired keys. Mirrors `runs_on_release` -/// in `src/keyboard/shortcuts.rs`. +/// in `src/keyboard/shortcuts.rs`; both are checked against +/// `flutter/test/fixtures/key_up_shortcut_actions.json`. const kShortcutActionsRunOnKeyUp = { kShortcutActionCloseTab, kShortcutActionSwitchTabNext, diff --git a/flutter/test/fixtures/key_up_shortcut_actions.json b/flutter/test/fixtures/key_up_shortcut_actions.json new file mode 100644 index 000000000..4bf77dc8d --- /dev/null +++ b/flutter/test/fixtures/key_up_shortcut_actions.json @@ -0,0 +1,5 @@ +[ + "close_tab", + "switch_tab_next", + "switch_tab_prev" +] diff --git a/flutter/test/keyboard_shortcuts_test.dart b/flutter/test/keyboard_shortcuts_test.dart index 094deb785..6326b2b38 100644 --- a/flutter/test/keyboard_shortcuts_test.dart +++ b/flutter/test/keyboard_shortcuts_test.dart @@ -509,6 +509,25 @@ void main() { expect(shortcutKeyNameForEvent(qwertzZ), 'y'); }); + test('kShortcutActionsRunOnKeyUp matches the fixture and names real actions', + () { + // Rust has a mirror test against the same file + // (`key_up_actions_match_fixture` in src/keyboard/shortcuts.rs). + final fixture = (jsonDecode( + File('test/fixtures/key_up_shortcut_actions.json') + .readAsStringSync()) as List) + .cast() + .toSet(); + expect(kShortcutActionsRunOnKeyUp, equals(fixture), + reason: 'kShortcutActionsRunOnKeyUp drifted from the fixture — update ' + 'shortcut_constants.dart, the fixture, and Rust runs_on_release ' + 'together'); + final actions = idSet(kKeyboardShortcutActionGroups); + for (final id in kShortcutActionsRunOnKeyUp) { + expect(actions, contains(id), reason: '"$id" is not a configurable action'); + } + }); + test('configurable shortcut list does not include known-removed action IDs', () { // These IDs were briefly defined without handlers (a "ghost action" diff --git a/src/keyboard/shortcuts.rs b/src/keyboard/shortcuts.rs index 9f23c4df4..1574c80ee 100644 --- a/src/keyboard/shortcuts.rs +++ b/src/keyboard/shortcuts.rs @@ -343,12 +343,16 @@ lazy_static::lazy_static! { /// that consumed the press also sees its repeats and its release. The next /// session may route its keys through the other matcher (Flutter's legacy /// path on Linux), which never saw the press. Mirrors -/// `kShortcutActionsRunOnKeyUp` in `shortcut_constants.dart`. +/// `kShortcutActionsRunOnKeyUp` in `shortcut_constants.dart`; both are +/// checked against `flutter/test/fixtures/key_up_shortcut_actions.json`. +const RELEASE_ACTION_IDS: &[&str] = &[ + action_id::CLOSE_TAB, + action_id::SWITCH_TAB_NEXT, + action_id::SWITCH_TAB_PREV, +]; + pub fn runs_on_release(action_id: &str) -> bool { - matches!( - action_id, - action_id::CLOSE_TAB | action_id::SWITCH_TAB_NEXT | action_id::SWITCH_TAB_PREV - ) + RELEASE_ACTION_IDS.contains(&action_id) } /// Forget the modifiers a session released on its remote and the actions it @@ -1237,6 +1241,29 @@ mod tests { release_chord(chord); } + /// The Dart matcher keeps the same list (`kShortcutActionsRunOnKeyUp`); + /// the fixture is the shared source of truth. + #[test] + fn key_up_actions_match_fixture() { + use std::collections::BTreeSet; + + let fixture: Vec = serde_json::from_str(include_str!( + "../../flutter/test/fixtures/key_up_shortcut_actions.json" + )) + .expect("parse fixture"); + let expected: BTreeSet<&str> = fixture.iter().map(String::as_str).collect(); + let actual: BTreeSet<&str> = RELEASE_ACTION_IDS.iter().copied().collect(); + assert_eq!( + actual, expected, + "runs_on_release drifted from key_up_shortcut_actions.json — update \ + shortcuts.rs, the fixture, and Dart kShortcutActionsRunOnKeyUp together" + ); + for id in &fixture { + assert!(runs_on_release(id)); + } + assert!(!runs_on_release(action_id::SCREENSHOT)); + } + /// Close tab and tab switching move focus away before the key is /// released; they run on the release so the same matcher sees the whole /// press.