Files
neovim/test/functional/editor/undo_spec.lua
Justin M. Keyes 6d8c8b18d5 test(harness): migrate away from magic globals
Problem:
The magic globals `it`, `describe`, etc., are more trouble than they are
worth.

- Hooking into `after_each` requires `getfenv()` hacks.
- They confuse luals/emmylua, because the top-level `.luarc.json` isn't
  merged with `test/.luarc.json` (apparently a luals limitation?)
- They totally defeat discoverability because the user just has to
  "know" about the various magic symbols.

So they harm DX, which means they serve no purpose at all.

Solution:
- Expose the test API from `testutil`, so tests can call `t.it()`,
  `t.describe()`, etc., in the conventional way.
- Drop `getfenv()` hacks.
- Drop the `setfenv()` injection in `load_chunk`.
- Drop `test/_meta.lua`.
2026-07-21 13:22:39 +02:00

241 lines
5.5 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local describe, it, before_each, after_each = t.describe, t.it, t.before_each, t.after_each
local clear = n.clear
local command = n.command
local expect = n.expect
local eq = t.eq
local feed = n.feed
local insert = n.insert
local fn = n.fn
local exec = n.exec
local exec_lua = n.exec_lua
local pcall_err = t.pcall_err
local function lastmessage()
local messages = fn.split(fn.execute('messages'), '\n')
return messages[#messages]
end
describe('u CTRL-R g- g+', function()
before_each(clear)
local function create_history(num_steps)
if num_steps == 0 then
return
end
insert('1')
if num_steps == 1 then
return
end
feed('o2<esc>')
feed('o3<esc>')
feed('u')
if num_steps == 2 then
return
end
feed('o4<esc>')
if num_steps == 3 then
return
end
feed('u')
end
local function undo_and_redo(hist_pos, undo, redo, expect_str)
command('enew!')
create_history(hist_pos)
local cur_contents = n.curbuf_contents()
feed(undo)
expect(expect_str)
feed(redo)
expect(cur_contents)
end
-- TODO Look for message saying 'Already at oldest change'
it('does nothing when no changes have happened', function()
undo_and_redo(0, 'u', '<C-r>', '')
undo_and_redo(0, 'g-', 'g+', '')
end)
it('undoes a change when at a leaf', function()
undo_and_redo(1, 'u', '<C-r>', '')
undo_and_redo(1, 'g-', 'g+', '')
end)
it('undoes a change when in a non-leaf', function()
undo_and_redo(2, 'u', '<C-r>', '1')
undo_and_redo(2, 'g-', 'g+', '1')
end)
it('undoes properly around a branch point', function()
undo_and_redo(
3,
'u',
'<C-r>',
[[
1
2]]
)
undo_and_redo(
3,
'g-',
'g+',
[[
1
2
3]]
)
end)
it('can find the previous sequence after undoing to a branch', function()
undo_and_redo(4, 'u', '<C-r>', '1')
undo_and_redo(4, 'g-', 'g+', '1')
end)
describe('undo works correctly when writing in Insert mode', function()
before_each(function()
exec([[
edit Xtestfile.txt
set undolevels=100 undofile
write
]])
end)
after_each(function()
command('bwipe!')
os.remove('Xtestfile.txt')
os.remove('Xtestfile.txt.un~')
end)
-- oldtest: Test_undo_after_write()
it('using <Cmd> mapping', function()
command('imap . <Cmd>write<CR>')
feed('Otest.<CR>boo!!!<Esc>')
expect([[
test
boo!!!
]])
feed('u')
expect([[
test
]])
feed('u')
expect('')
end)
it('using Lua mapping', function()
exec_lua([[
vim.api.nvim_set_keymap('i', '.', '', {callback = function()
vim.cmd('write')
end})
]])
feed('Otest.<CR>boo!!!<Esc>')
expect([[
test
boo!!!
]])
feed('u')
expect([[
test
]])
feed('u')
expect('')
end)
it('using RPC call', function()
feed('Otest')
command('write')
feed('<CR>boo!!!<Esc>')
expect([[
test
boo!!!
]])
feed('u')
expect([[
test
]])
feed('u')
expect('')
end)
end)
end)
describe(':undo! command', function()
before_each(function()
clear()
feed('i1 little bug in the code<Esc>')
feed('o1 little bug in the code<Esc>')
feed('oTake 1 down, patch it around<Esc>')
feed('o99 little bugs in the code<Esc>')
end)
it('works', function()
command('undo!')
expect([[
1 little bug in the code
1 little bug in the code
Take 1 down, patch it around]])
feed('<C-r>')
eq('Already at newest change', lastmessage())
end)
it('works with arguments', function()
command('undo! 2')
expect([[
1 little bug in the code
1 little bug in the code]])
feed('<C-r>')
eq('Already at newest change', lastmessage())
end)
it('correctly sets alternative redo', function()
feed('uo101 little bugs in the code<Esc>')
command('undo!')
feed('<C-r>')
expect([[
1 little bug in the code
1 little bug in the code
Take 1 down, patch it around
99 little bugs in the code]])
feed('uuoTake 2 down, patch them around<Esc>')
feed('o101 little bugs in the code<Esc>')
command('undo! 2')
feed('<C-r><C-r>')
expect([[
1 little bug in the code
1 little bug in the code
Take 1 down, patch it around
99 little bugs in the code]])
end)
it('fails when attempting to redo or move to different undo branch', function()
eq(
'Vim(undo):E5767: Cannot use :undo! to redo or move to a different undo branch',
pcall_err(command, 'undo! 4')
)
feed('u')
eq(
'Vim(undo):E5767: Cannot use :undo! to redo or move to a different undo branch',
pcall_err(command, 'undo! 4')
)
feed('o101 little bugs in the code<Esc>')
feed('o101 little bugs in the code<Esc>')
eq(
'Vim(undo):E5767: Cannot use :undo! to redo or move to a different undo branch',
pcall_err(command, 'undo! 4')
)
end)
end)
describe("opening file when 'undofile' is on", function()
before_each(function()
clear({ args = { '--cmd', 'set undofile' } })
end)
it("does not crash when 'undodir' contains empty entry", function()
command('set undodir=,.')
command('edit test/functional/fixtures/bigfile.txt')
n.assert_alive()
end)
end)