Files
neovim/test/functional/autocmd/textyankpost_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

257 lines
5.9 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local describe, it, before_each = t.describe, t.it, t.before_each
local clear, eval, eq = n.clear, n.eval, t.eq
local feed, command, expect = n.feed, n.command, n.expect
local api, fn, neq = n.api, n.fn, t.neq
describe('TextYankPost', function()
before_each(function()
clear()
-- emulate the clipboard so system clipboard isn't affected
command('set rtp^=test/functional/fixtures')
command('let g:count = 0')
command('autocmd TextYankPost * let g:event = copy(v:event)')
command('autocmd TextYankPost * let g:count += 1')
api.nvim_buf_set_lines(0, 0, -1, true, {
'foo\0bar',
'baz text',
})
end)
it('is executed after yank and handles register types', function()
feed('yy')
eq({
inclusive = false,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
regtype = 'V',
visual = false,
}, eval('g:event'))
eq(1, eval('g:count'))
-- v:event is cleared after the autocommand is done
eq({}, eval('v:event'))
feed('+yw')
eq({
inclusive = false,
operator = 'y',
regcontents = { 'baz ' },
regname = '',
regtype = 'v',
visual = false,
}, eval('g:event'))
eq(2, eval('g:count'))
feed('<c-v>eky')
eq({
inclusive = true,
operator = 'y',
regcontents = { 'foo', 'baz' },
regname = '',
regtype = '\0223', -- ^V + block width
visual = true,
}, eval('g:event'))
eq(3, eval('g:count'))
end)
it('makes v:event immutable', function()
feed('yy')
eq({
inclusive = false,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
regtype = 'V',
visual = false,
}, eval('g:event'))
command('set debug=msg')
-- the regcontents should not be changed without copy.
local status, err = pcall(command, 'call extend(g:event.regcontents, ["more text"])')
eq(false, status)
neq(nil, string.find(err, ':E742:'))
-- can't mutate keys inside the autocommand
command('autocmd! TextYankPost * let v:event.regcontents = 0')
status, err = pcall(command, 'normal yy')
eq(false, status)
neq(nil, string.find(err, ':E46:'))
-- can't add keys inside the autocommand
command('autocmd! TextYankPost * let v:event.mykey = 0')
status, err = pcall(command, 'normal yy')
eq(false, status)
neq(nil, string.find(err, ':E742:'))
end)
it('is not invoked recursively', function()
command('autocmd TextYankPost * normal "+yy')
feed('yy')
eq({
inclusive = false,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
regtype = 'V',
visual = false,
}, eval('g:event'))
eq(1, eval('g:count'))
eq({ 'foo\nbar' }, fn.getreg('+', 1, 1))
end)
it('is executed after delete and change', function()
feed('dw')
eq({
inclusive = false,
operator = 'd',
regcontents = { 'foo' },
regname = '',
regtype = 'v',
visual = false,
}, eval('g:event'))
eq(1, eval('g:count'))
feed('dd')
eq({
inclusive = false,
operator = 'd',
regcontents = { '\nbar' },
regname = '',
regtype = 'V',
visual = false,
}, eval('g:event'))
eq(2, eval('g:count'))
feed('cwspam<esc>')
eq({
inclusive = true,
operator = 'c',
regcontents = { 'baz' },
regname = '',
regtype = 'v',
visual = false,
}, eval('g:event'))
eq(3, eval('g:count'))
end)
it('is not executed after black-hole operation', function()
feed('"_dd')
eq(0, eval('g:count'))
feed('"_cwgood<esc>')
eq(0, eval('g:count'))
expect([[
good text]])
feed('"_yy')
eq(0, eval('g:count'))
command('delete _')
eq(0, eval('g:count'))
end)
it('gives the correct register name', function()
feed('$"byiw')
eq({
inclusive = true,
operator = 'y',
regcontents = { 'bar' },
regname = 'b',
regtype = 'v',
visual = false,
}, eval('g:event'))
feed('"*yy')
eq({
inclusive = true,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '*',
regtype = 'V',
visual = false,
}, eval('g:event'))
command('set clipboard=unnamed')
-- regname still shows the name the user requested
feed('yy')
eq({
inclusive = true,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
regtype = 'V',
visual = false,
}, eval('g:event'))
feed('"*yy')
eq({
inclusive = true,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '*',
regtype = 'V',
visual = false,
}, eval('g:event'))
end)
it('works with Ex commands', function()
command('1delete +')
eq({
inclusive = false,
operator = 'd',
regcontents = { 'foo\nbar' },
regname = '+',
regtype = 'V',
visual = false,
}, eval('g:event'))
eq(1, eval('g:count'))
command('yank')
eq({
inclusive = false,
operator = 'y',
regcontents = { 'baz text' },
regname = '',
regtype = 'V',
visual = false,
}, eval('g:event'))
eq(2, eval('g:count'))
command('normal yw')
eq({
inclusive = false,
operator = 'y',
regcontents = { 'baz ' },
regname = '',
regtype = 'v',
visual = false,
}, eval('g:event'))
eq(3, eval('g:count'))
command('normal! dd')
eq({
inclusive = false,
operator = 'd',
regcontents = { 'baz text' },
regname = '',
regtype = 'V',
visual = false,
}, eval('g:event'))
eq(4, eval('g:count'))
end)
it('updates numbered registers correctly #10225', function()
command('autocmd TextYankPost * let g:reg = getreg("1")')
feed('"adj')
eq('foo\nbar\nbaz text\n', eval('g:reg'))
end)
end)