ribbit/test/vim.test.ts
gsb 3368e719fd Vim keybindings for source edit mode
VimHandler activates in source (edit) mode only. Two modes:
- Insert: standard typing, Esc enters normal mode
- Normal: vim navigation and editing, i/a/o/O enter insert

Normal mode commands:
  h/j/k/l: cursor movement
  w/b: word forward/back
  0/$: line start/end
  gg/G: document start/end
  i/a/o/O: enter insert mode
  x: delete char
  dd: delete line
  u: undo
  Ctrl+r: redo
2026-04-29 07:37:09 +00:00

79 lines
3.1 KiB
TypeScript

import { ribbit, resetDOM } from './setup';
const r = ribbit();
describe('VimHandler', () => {
beforeEach(() => resetDOM('hello world'));
it('starts in insert mode', () => {
const editor = new r.Editor({});
editor.run();
editor.edit();
expect(editor.element.classList.contains('vim-insert')).toBe(true);
});
it('Esc enters normal mode', () => {
const editor = new r.Editor({});
editor.run();
editor.edit();
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' }));
expect(editor.element.classList.contains('vim-normal')).toBe(true);
expect(editor.element.classList.contains('vim-insert')).toBe(false);
});
it('i returns to insert mode', () => {
const editor = new r.Editor({});
editor.run();
editor.edit();
// Enter normal mode
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' }));
// Back to insert
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'i' }));
expect(editor.element.classList.contains('vim-insert')).toBe(true);
expect(editor.element.classList.contains('vim-normal')).toBe(false);
});
it('disables toolbar in normal mode', () => {
const editor = new r.Editor({ autoToolbar: false });
editor.run();
editor.toolbar.render();
editor.edit();
editor.toolbar.enable();
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' }));
const bold = editor.toolbar.buttons.get('bold');
expect(bold?.element?.classList.contains('disabled')).toBe(true);
});
it('re-enables toolbar in insert mode', () => {
const editor = new r.Editor({ autoToolbar: false });
editor.run();
editor.toolbar.render();
editor.edit();
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' }));
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'i' }));
const bold = editor.toolbar.buttons.get('bold');
expect(bold?.element?.classList.contains('disabled')).toBe(false);
});
it('detaches when leaving edit mode', () => {
const editor = new r.Editor({});
editor.run();
editor.edit();
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' }));
expect(editor.element.classList.contains('vim-normal')).toBe(true);
editor.wysiwyg();
// vim classes should be gone after mode switch
expect(editor.element.classList.contains('vim-normal')).toBe(false);
expect(editor.element.classList.contains('vim-insert')).toBe(false);
});
it('only activates in edit mode', () => {
const editor = new r.Editor({});
editor.run();
editor.wysiwyg();
// Esc in wysiwyg should not add vim classes
editor.element.dispatchEvent(new r.window.KeyboardEvent('keydown', { key: 'Escape' }));
expect(editor.element.classList.contains('vim-normal')).toBe(false);
});
});