Skip to content

feat(testing): check that a contributed keybinding binds a declared command - #71

Merged
kkdev92 merged 1 commit into
mainfrom
feat/manifest-keybindings
Sep 16, 2026
Merged

kkdev92 merged 1 commit into
mainfrom
feat/manifest-keybindings

Conversation

@kkdev92

@kkdev92 kkdev92 commented Sep 16, 2026

Copy link
Copy Markdown
Owner

VS Code validates the shape of a contributes.keybindings entry and never its
command. isValidContributedKeyBinding checks types only — a keybinding naming a
command nothing registers is accepted, given a weight, and does nothing at all when the
key is pressed. Renaming or deleting a command and missing the manifest leaves a shortcut
that fails in silence, with no warning from the editor and nothing in a test suite to
catch it.

What this adds

DeclaredContributions gains an optional keybindings field:

assertManifestMatches(manifest, {
  commands: Object.values(Contracts),
  keybindings: { allow: ['workbench.action.files.save'] },
});

Passing it checks that every entry binds a command the extension declares. allow names
the built-in ids it may bind as well, because putting a key on a built-in is a supported
use of the contribution point and would otherwise fail the check.

Omitting the field checks no keybindings, so every existing caller is unaffected.
ManifestMismatch['kind'] gains 'keybinding'; a caller switching exhaustively over it
needs the new case.

Why the library and not each extension

Both extensions this was trialled against had already hand-written a check here:

what it had covered by this?
quick-utils every bound command is a declared one yes — the same comparison
ClipShot the order of a pair of entries, which decides which wins no, deliberately

Of a keybinding this reads the command id and nothing else. The key, when and args
stay the manifest's, and so does the order — so an extension that leans on it still needs
an assertion of its own, and ClipShot's is the worked example.

Verified

Trialled against both consumers' real package.json:

clipshot:    そのまま 0 件 / command に typo → 1 件 / built-in は allow 無し 1 件・有り 0 件
quick-utils: そのまま 0 件 / command に typo → 1 件 / built-in は allow 無し 1 件・有り 0 件
  • npm run quality: 1094 tests, all green (7 new)
  • npm run verify:package: all package checks passed
  • Each of the five new assertions that could fail was run against the previous source
    first, and all five did.
    The remaining two — the passing case and the opt-out — pass
    on both sides by their nature

The VS Code behaviour above was read from release/1.137
(keybindingService.ts, keybindingsRegistry.ts, keybindingResolver.ts), not inferred.

🤖 Generated with Claude Code

…ommand

VS Code validates the shape of a `contributes.keybindings` entry and never its
command. `isValidContributedKeyBinding` checks types only; a keybinding naming
a command nothing registers is accepted, given a weight, and does nothing at
all when the key is pressed. Renaming or deleting a command and missing the
manifest therefore leaves a shortcut that fails in silence, with no warning
from the editor and nothing in a test suite to catch it.

`DeclaredContributions` gains an optional `keybindings` field. Passing it
checks that every entry binds a command the extension declares; its `allow`
list names the built-in ids it may bind as well, because putting a key on a
built-in is a supported use of the point and would otherwise fail the check.
Omitting the field checks no keybindings, so every existing caller is
unaffected.

Both extensions this was trialled against had already hand-written a check
here, which is the argument for having one: quick-utils asserts that every
bound command is declared -- the same comparison -- and ClipShot pins the order
of a pair of entries, which this deliberately does not do. Of a keybinding this
reads the command id and nothing else. The key, `when` and `args` stay the
manifest's, and so does the order, which is what decides the winner when
several entries share a key.

Verified against both consumers' real manifests: neither reports anything as
it stands, a typo in a bound command is caught in each, and a built-in is
reported without `allow` and accepted with it. Each of the five new assertions
that could fail was run against the previous source first, and all five did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kkdev92
kkdev92 merged commit 1ebca29 into main Sep 16, 2026
10 checks passed
@kkdev92
kkdev92 deleted the feat/manifest-keybindings branch September 16, 2026 11:00
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