Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/tests/std/user_command_spec.lua

Raw
local user_command = require('std.user_command')

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

  after_each(function()
    pcall(vim.api.nvim_del_user_command, 'TestCommand')
    pcall(vim.api.nvim_del_user_command, 'AnotherCommand')
    pcall(vim.api.nvim_del_user_command, 'NoDescCommand')
    pcall(vim.api.nvim_del_user_command, 'NoOptsCommand')
    pcall(vim.api.nvim_del_user_command, 'FullChainCommand')
    pcall(vim.api.nvim_buf_del_user_command, 0, 'TestBufCmd')
    pcall(vim.api.nvim_buf_del_user_command, 0, 'TestBufCmd2')
    pcall(vim.api.nvim_buf_del_user_command, 0, 'AnotherBufCmd')
    pcall(vim.api.nvim_buf_del_user_command, 0, 'OptsBufCmd')
    pcall(vim.api.nvim_buf_del_user_command, 0, 'ExecBufCmd')
  end)

  local function get_user_command(name)
    local commands = vim.api.nvim_get_commands {}
    return commands[name]
  end

  describe('full chain: name -> description -> options -> command', function()
    it('should create a user command with all parameters', function()
      local deleter = user_command.FullChainCommand 'A test command' {
        nargs = '*',
        complete = 'file',
      } (function()
        return 'executed'
      end)

      assert.is_function(deleter)
      local info = get_user_command('FullChainCommand')
      assert.is_not_nil(info)
      assert.equal('FullChainCommand', info.name)
      assert.equal('*', info.nargs)
      assert.equal('file', info.complete)
    end)

    it('should return a deleter function that removes the command', function()
      local deleter = user_command.TestCommand 'Description' { nargs = '?' } (function() end)
      assert.is_function(deleter)
      local info_before = get_user_command('TestCommand')
      assert.is_not_nil(info_before)
      deleter()
      local info_after = get_user_command('TestCommand')
      assert.is_nil(info_after)
    end)
  end)

  describe('chain: name -> description -> command (no options)', function()
    it('should create a user command with just description and command', function()
      local deleter = user_command.TestCommand 'Just a description' (function()
        return 'simple'
      end)

      assert.is_function(deleter)
      local info = get_user_command('TestCommand')
      assert.is_not_nil(info)
      assert.equal('TestCommand', info.name)
    end)
  end)

  describe('command execution', function()
    it('should execute the command function when called', function()
      local executed = false
      local deleter = user_command.TestCommand 'Execute test' (function()
        executed = true
        return 'done'
      end)

      vim.cmd('TestCommand')
      assert.is_true(executed)
    end)
  end)

  describe('error handling', function()
    it('should error on invalid command_name (non-string)', function()
      local ok, err = pcall(function() return user_command[123] end)
      assert.is_false(ok)
      assert.is_not_nil(err:match('command_name: expected string'))
    end)

    it('should error on invalid description (non-string)', function()
      local desc_stage = user_command.TestCommand
      local ok, err = pcall(function() return desc_stage(123) end)
      assert.is_false(ok)
      assert.is_not_nil(err:match('description: expected string'))
    end)

    it('should error on invalid command (non-function when expected)', function()
      local ok, err = pcall(function() return user_command.TestCommand('description')({ nargs = '?' })(123) end)
      assert.is_false(ok)
      assert.is_not_nil(err:match('command: expected function'))
    end)

    it('should error on invalid options_or_command (non-table and non-function)', function()
      local ok, err = pcall(function() return user_command.TestCommand('description')(123) end)
      assert.is_false(ok)
      assert.is_not_nil(err:match('options_or_command: expected'))
    end)
  end)

  describe('metatable chaining behavior', function()
    it('should return DESCRIPTION_SETTER metatable after setting name', function()
      local mt = getmetatable(user_command.TestCommand)
      assert.is_function(mt.__call)
    end)

    it('should return OPTIONS_OR_COMMAND_SETTER metatable after setting description', function()
      local desc_stage = user_command.TestCommand 'description'
      local mt = getmetatable(desc_stage)
      assert.is_function(mt.__call)
    end)

    it('should return COMMAND_SETTER metatable after setting options', function()
      local opts_stage = user_command.TestCommand 'description' { nargs = '?' }
      local mt = getmetatable(opts_stage)
      assert.is_function(mt.__call)
    end)

    it('should return deleter function after setting command', function()
      local result = user_command.TestCommand 'description' (function() end)
      assert.is_function(result)
    end)
  end)

  describe('options merging', function()
    it('should create command with correct options', function()
      local deleter = user_command.TestCommand 'My description' (function() end)
      local info = get_user_command('TestCommand')
      assert.is_not_nil(info)
    end)

    it('should allow custom options to override defaults', function()
      local deleter = user_command.TestCommand 'Custom desc' {
        nargs = '*',
        bang = true,
      } (function() end)

      local info = get_user_command('TestCommand')
      assert.is_not_nil(info)
      assert.equal('*', info.nargs)
      assert.is_true(info.bang == 1 or info.bang == true)
    end)
  end)

  describe('multiple commands', function()
    it('should create multiple independent commands', function()
      local deleter1 = user_command.TestCommand 'First' (function() end)
      local deleter2 = user_command.AnotherCommand 'Second' (function() end)

      local info1 = get_user_command('TestCommand')
      local info2 = get_user_command('AnotherCommand')
      assert.is_not_nil(info1)
      assert.is_not_nil(info2)
      assert.equal('TestCommand', info1.name)
      assert.equal('AnotherCommand', info2.name)

      deleter1()
      deleter2()
      local after1 = get_user_command('TestCommand')
      local after2 = get_user_command('AnotherCommand')
      assert.is_nil(after1)
      assert.is_nil(after2)
    end)
  end)

  describe('buffer commands', function()
    local function get_buf_commands(bufnr)
      return vim.api.nvim_buf_get_commands(bufnr, {})
    end

    it('should create a buffer-local command', function()
      local deleter = user_command.buffer(0).TestBufCmd 'A buffer command' (function() end)
      local buf_cmds = get_buf_commands(0)
      assert.is_not_nil(buf_cmds['TestBufCmd'])
    end)

    it('should NOT appear in global command namespace', function()
      local deleter = user_command.buffer(0).TestBufCmd 'A buffer command' (function() end)
      local global_cmds = vim.api.nvim_get_commands {}
      assert.is_nil(global_cmds['TestBufCmd'])
    end)

    it('should appear in buffer-local command namespace', function()
      local deleter = user_command.buffer(0).TestBufCmd 'A buffer command' (function() end)
      local buf_cmds = get_buf_commands(0)
      assert.is_table(buf_cmds['TestBufCmd'])
      assert.equal('TestBufCmd', buf_cmds['TestBufCmd'].name)
    end)

    it('should return a deleter that removes the buffer command', function()
      local deleter = user_command.buffer(0).TestBufCmd 'To be deleted' (function() end)
      local buf_cmds_before = get_buf_commands(0)
      assert.is_not_nil(buf_cmds_before['TestBufCmd'])
      deleter()
      local buf_cmds_after = get_buf_commands(0)
      assert.is_nil(buf_cmds_after['TestBufCmd'])
    end)

    it('should create multiple independent buffer commands', function()
      local deleter1 = user_command.buffer(0).TestBufCmd 'First' (function() end)
      local deleter2 = user_command.buffer(0).TestBufCmd2 'Second' (function() end)
      local buf_cmds = get_buf_commands(0)
      assert.is_not_nil(buf_cmds['TestBufCmd'])
      assert.is_not_nil(buf_cmds['TestBufCmd2'])
      deleter1()
      deleter2()
      local buf_cmds_after = get_buf_commands(0)
      assert.is_nil(buf_cmds_after['TestBufCmd'])
      assert.is_nil(buf_cmds_after['TestBufCmd2'])
    end)

    it('should error on invalid buffer argument', function()
      local ok = pcall(function() return user_command.buffer('not_a_number') end)
      assert.is_false(ok)
    end)

    it('should accept an options table before command', function()
      local deleter = user_command.buffer(0).OptsBufCmd 'With options' {
        nargs = '*',
        bang = true,
      } (function() end)
      local buf_cmds = get_buf_commands(0)
      assert.is_not_nil(buf_cmds['OptsBufCmd'])
      assert.equal('*', buf_cmds['OptsBufCmd'].nargs)
      deleter()
    end)

    it('should execute the command function when called', function()
      local executed = false
      local deleter = user_command.buffer(0).ExecBufCmd 'Execute test' (function()
        executed = true
      end)
      vim.cmd('ExecBufCmd')
      assert.is_true(executed)
      deleter()
    end)

    it('should pass opts to command function', function()
      ---@type vim.api.keyset.create_user_command.command_args?
      local received_opts
      local deleter = user_command.buffer(0).TestBufCmd 'Opts test' (function(opts)
        received_opts = opts
      end)
      vim.cmd('TestBufCmd')
      assert.is_table(received_opts)
      assert.equal('TestBufCmd', assert(received_opts).name)
      deleter()
    end)

    it('should be scoped to the specified buffer only', function()
      local bufnr1 = vim.api.nvim_create_buf(false, true)
      local bufnr2 = vim.api.nvim_create_buf(false, true)
      local deleter1 = user_command.buffer(bufnr1).TestBufCmd 'On buffer 1' (function() end)
      local deleter2 = user_command.buffer(bufnr2).AnotherBufCmd 'On buffer 2' (function() end)
      local cmds1 = get_buf_commands(bufnr1)
      local cmds2 = get_buf_commands(bufnr2)
      assert.is_not_nil(cmds1['TestBufCmd'])
      assert.is_nil(cmds1['AnotherBufCmd'])
      assert.is_not_nil(cmds2['AnotherBufCmd'])
      assert.is_nil(cmds2['TestBufCmd'])
      deleter1()
      deleter2()
      vim.api.nvim_buf_delete(bufnr1, { force = true })
      vim.api.nvim_buf_delete(bufnr2, { force = true })
    end)
  end)
end)