Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/tests/std/map_spec.lua

Raw
local map = require 'std.map'

describe( 'std.map', function ()
	before_each( function ()
		package.loaded['std.map'] = nil
		map = require 'std.map'
	end )

	after_each( function ()
		pcall( vim.keymap.del, 'n', '<test-map-keys>' )
		pcall( vim.keymap.del, 'n', '<test-map-keys-2>' )
		pcall( vim.keymap.del, 'i', 'k' )
		pcall( vim.keymap.del, 'n', 'gd' )
		pcall( vim.keymap.del, 'x', 'gd' )
		pcall( vim.keymap.del, 'n', 'gx' )
	end )

	local function find_keymap( mode, lhs )
		local keymaps = vim.api.nvim_get_keymap( mode )
		for _, km in ipairs( keymaps ) do
			if km.lhsraw == lhs then
				return km
			end
		end
		return nil
	end

	local function get_keymap( mode, lhs )
		return assert( find_keymap( mode, lhs ) )
	end

	describe( 'mode aliases', function ()
		it( 'should support "n" as alias for normal mode', function ()
			local deleter = map.n { keys = '<test-map-keys>', command = 'echo "test"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "normal" as explicit normal mode', function ()
			local deleter = map.normal { keys = '<test-map-keys>', command = 'echo "test"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "i" as alias for insert mode', function ()
			local deleter = map.i { keys = 'k', command = 'echo "insert"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'i', 'k' )
			assert.is_not_nil( mapping )
			assert.equal( 'k', mapping.lhsraw )
		end )

		it( 'should support "insert" as explicit insert mode', function ()
			local deleter = map.insert { keys = 'k', command = 'echo "insert"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'i', 'k' )
			assert.is_not_nil( mapping )
			assert.equal( 'k', mapping.lhsraw )
		end )

		it( 'should support "x" as alias for visual mode', function ()
			local deleter = map.x { keys = '<test-map-keys>', command = 'echo "visual"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'x', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "visual" as explicit visual mode', function ()
			local deleter = map.visual { keys = '<test-map-keys>', command = 'echo "visual"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'x', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "v" as alias for visual_select mode', function ()
			local deleter = map.v { keys = '<test-map-keys>', command = 'echo "v"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'v', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "s" as alias for select mode', function ()
			local deleter = map.s { keys = '<test-map-keys>', command = 'echo "s"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 's', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "o" as alias for operator_pending mode', function ()
			local deleter = map.o { keys = '<test-map-keys>', command = 'echo "o"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'o', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "c" as alias for command_line mode', function ()
			local deleter = map.c { keys = '<test-map-keys>', command = 'echo "c"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'c', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should support "t" as alias for terminal mode', function ()
			local deleter = map.t { keys = '<test-map-keys>', command = 'echo "t"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 't', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )
	end )

	describe( 'mode chaining', function ()
		it( 'should chain normal and visual modes', function ()
			local deleter = map.normal.visual { keys = 'gd', command = 'echo "chained"' }
			assert.is_function( deleter )
			local normal_mapping = get_keymap( 'n', 'gd' )
			local visual_mapping = get_keymap( 'x', 'gd' )
			assert.is_not_nil( normal_mapping )
			assert.is_not_nil( visual_mapping )
		end )

		it( 'should chain multiple modes', function ()
			local deleter = map.n.x.v { keys = 'gx', command = 'echo "multi"' }
			assert.is_function( deleter )
			local n_mapping = get_keymap( 'n', 'gx' )
			local x_mapping = get_keymap( 'x', 'gx' )
			local v_mapping = get_keymap( 'v', 'gx' )
			assert.is_not_nil( n_mapping )
			assert.is_not_nil( x_mapping )
			assert.is_not_nil( v_mapping )
		end )

		it( 'should return self after adding mode for further chaining', function ()
			local result = map.normal
			assert.equal( map, result )
			result = map.normal.visual
			assert.equal( map, result )
		end )

		it( 'should not duplicate mode when same mode is chained multiple times', function ()
			local m = map.normal.normal.normal
			local modes = m.modes
			local n_count = 0
			for _, mode in ipairs( modes ) do
				if mode == 'n' then n_count = n_count + 1 end
			end
			assert.equal( 1, n_count, 'mode "n" should appear exactly once in modes array before bind' )
		end )
	end )

	describe( 'keys handling', function ()
		it( 'should accept keys as string', function ()
			local deleter = map.normal { keys = '<test-map-keys>', command = 'echo "string keys"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( '<test-map-keys>', mapping.lhsraw )
		end )

		it( 'should accept keys as table (array of keys)', function ()
			local deleter = map.normal { keys = { '<test-map-keys>', '<test-map-keys-2>' }, command = 'echo "table keys"' }
			assert.is_function( deleter )
			local mapping1 = get_keymap( 'n', '<test-map-keys>' )
			local mapping2 = get_keymap( 'n', '<test-map-keys-2>' )
			assert.is_not_nil( mapping1 )
			assert.is_not_nil( mapping2 )
			assert.equal( '<test-map-keys>',   mapping1.lhsraw )
			assert.equal( '<test-map-keys-2>', mapping2.lhsraw )
		end )
	end )

	describe( 'command handling', function ()
		it( 'should accept command as string', function ()
			local deleter = map.normal { keys = '<test-map-keys>', command = 'echo "string command"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( 'echo "string command"', mapping.rhs )
		end )

		it( 'should accept command as function', function ()
			local called = false
			local function test_func()
				called = true
			end
			local deleter = map.normal { keys = '<test-map-keys>', command = test_func }
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.is_function( mapping.callback )
			mapping.callback()
			assert.is_true( called )
		end )
	end )

	describe( 'options handling', function ()
		it( 'should set silent=true by default', function ()
			local deleter = map.normal { keys = '<test-map-keys>', command = 'echo "silent"' }
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.is_true( mapping.silent == 1 or mapping.silent == true )
		end )

		it( 'should concatenate category and description into desc', function ()
			local deleter = map.normal {
				keys = '<test-map-keys>',
				command = 'echo "desc"',
				category = 'TestCategory',
				description = 'TestDescription',
			}
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( 'TestCategory: TestDescription', mapping.desc )
		end )

		it( 'should use description alone when category is not present', function ()
			local deleter = map.normal {
				keys = '<test-map-keys>',
				command = 'echo "desc"',
				description = 'JustDescription',
			}
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.equal( 'JustDescription', mapping.desc )
		end )

		it( 'should support buffer option for buffer-local keymap', function ()
			vim.api.nvim_buf_set_lines( 0, 0, -1, true, { 'buffer test content' } )
			local deleter = map.normal {
				keys = '<test-map-keys>',
				command = 'echo "buffer"',
				buffer = 0,
			}
			assert.is_function( deleter )
			local keymaps = vim.api.nvim_buf_get_keymap( 0, 'n' )
			local found = false
			for _, km in ipairs( keymaps ) do
				if km.lhsraw == '<test-map-keys>' then
					found = true
					break
				end
			end
			assert.is_true( found )
		end )

		it( 'should merge options field with defaults', function ()
			local deleter = map.normal {
				keys = '<test-map-keys>',
				command = 'echo "merged"',
				options = {
					noremap = true,
				},
			}
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
			assert.is_true( mapping.noremap == 1 or mapping.noremap == true )
		end )

		it( 'should support remap option', function ()
			local deleter = map.normal {
				keys = '<test-map-keys>',
				command = 'echo "remap"',
				remap = true,
			}
			assert.is_function( deleter )
			local mapping = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping )
		end )
	end )

	describe( 'cleanup function', function ()
		it( 'should return a cleanup function (deleter)', function ()
			local deleter = map.normal { keys = '<test-map-keys>', command = 'echo "delete"' }
			assert.is_function( deleter )
			local mapping_before = get_keymap( 'n', '<test-map-keys>' )
			assert.is_not_nil( mapping_before )
			deleter()
			local mapping_after = find_keymap( 'n', '<test-map-keys>' )
			assert.is_nil( mapping_after )
		end )

		it( 'should delete multiple keys when keys is a table', function ()
			local deleter = map.normal {
				keys = { '<test-map-keys>', '<test-map-keys-2>' },
				command = 'echo "delete multiple"',
			}
			local mapping1_before = get_keymap( 'n', '<test-map-keys>' )
			local mapping2_before = get_keymap( 'n', '<test-map-keys-2>' )
			assert.is_not_nil( mapping1_before )
			assert.is_not_nil( mapping2_before )
			deleter()
			local mapping1_after = find_keymap( 'n', '<test-map-keys>' )
			local mapping2_after = find_keymap( 'n', '<test-map-keys-2>' )
			assert.is_nil( mapping1_after )
			assert.is_nil( mapping2_after )
		end )

		it( 'should delete buffer-local keymaps correctly', function ()
			vim.api.nvim_buf_set_lines( 0, 0, -1, true, { 'buffer test content' } )
			local deleter = map.normal {
				keys = '<test-map-keys>',
				command = 'echo "buffer delete"',
				buffer = 0,
			}
			local keymaps_before = vim.api.nvim_buf_get_keymap( 0, 'n' )
			local found_before = false
			for _, km in ipairs( keymaps_before ) do
				if km.lhsraw == '<test-map-keys>' then
					found_before = true
					break
				end
			end
			assert(found_before, 'keymap should exist before deletion')
			deleter()
			local keymaps_after = vim.api.nvim_buf_get_keymap( 0, 'n' )
			local found_after = false
			for _, km in ipairs( keymaps_after ) do
				if km.lhsraw == '<test-map-keys>' then
					found_after = true
					break
				end
			end
			assert(not found_after, 'keymap should be deleted after calling deleter')
		end )
	end )

	describe( 'default modes', function ()
		it( 'should use normal_visual_select_operator_pending when no modes specified', function ()
			local deleter = map { keys = '<test-map-keys>', command = 'echo "default modes"' }
			assert.is_function( deleter )
			local n_mapping = get_keymap( 'n', '<test-map-keys>' )
			local x_mapping = get_keymap( 'x', '<test-map-keys>' )
			local v_mapping = get_keymap( 'v', '<test-map-keys>' )
			local o_mapping = get_keymap( 'o', '<test-map-keys>' )
			assert.is_not_nil( n_mapping )
			assert.is_not_nil( x_mapping )
			assert.is_not_nil( v_mapping )
			assert.is_not_nil( o_mapping )
		end )
	end )

	describe( 'error handling', function ()
		it( 'should error on unknown mode', function ()
			assert.has_error( function ()
													map.unknown_mode { keys = '<test-map-keys>', command = 'echo "error"' }
												end, 'unknown keymap mode requested: unknown_mode' )
		end )

		it( 'should error when map.keys is not string or table', function ()
			local invalid = 123
			---@cast invalid any
			local ok, err = pcall( function () map.normal { keys = invalid, command = 'echo "error"' } end )
			assert.is_false( ok )
			assert.is_not_nil( vim.split( tostring( err ), '\n' )[1]:match 'map.keys: expected' )
		end )

		it( 'should error when map.command is not function or string', function ()
			local ok, err = pcall( function () map.normal { keys = '<test-map-keys>', command = nil } end )
			assert.is_false( ok )
			assert.is_not_nil( vim.split( tostring( err ), '\n' )[1]:match 'map.command: expected' )
		end )

		it( 'should error when map is not a table', function ()
			local invalid = 123
			---@cast invalid any
			local ok, err = pcall( function () map( invalid ) end )
			assert.is_false( ok )
			assert.is_not_nil( vim.split( tostring( err ), '\n' )[1]:match 'map: expected' )
		end )

		it( 'should error on assignment to map', function ()
			local ok, err = pcall( function () map.foo = 'bar' end )
			assert.is_false( ok )
			assert.is_not_nil( tostring( err ):match 'Assignments are not supported for std.map' )
		end )
	end )
end )