health_spec.lua (17657B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local clear = n.clear 6 local curbuf_contents = n.curbuf_contents 7 local command = n.command 8 local eq, matches = t.eq, t.matches 9 local getcompletion = n.fn.getcompletion 10 local insert = n.insert 11 local exec_lua = n.exec_lua 12 local source = n.source 13 local assert_alive = n.assert_alive 14 local fn = n.fn 15 local api = n.api 16 17 describe(':checkhealth', function() 18 it('detects invalid $VIMRUNTIME', function() 19 clear({ 20 env = { VIMRUNTIME = 'bogus' }, 21 }) 22 local status, err = pcall(command, 'checkhealth') 23 eq(false, status) 24 eq('Invalid $VIMRUNTIME: bogus', string.match(err, 'Invalid.*')) 25 end) 26 27 it("detects invalid 'runtimepath'", function() 28 clear() 29 command('set runtimepath=bogus') 30 local status, err = pcall(command, 'checkhealth') 31 eq(false, status) 32 eq("Invalid 'runtimepath'", string.match(err, 'Invalid.*')) 33 end) 34 35 it('detects invalid $VIM', function() 36 clear() 37 -- Do this after startup, otherwise it just breaks $VIMRUNTIME. 38 command("let $VIM='zub'") 39 command('checkhealth vim.health') 40 matches('ERROR $VIM .* zub', curbuf_contents()) 41 end) 42 43 it('getcompletion()', function() 44 clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } } 45 46 eq('vim.deprecated', getcompletion('vim', 'checkhealth')[1]) 47 eq('vim.provider', getcompletion('vim.prov', 'checkhealth')[1]) 48 eq('vim.lsp', getcompletion('vim.ls', 'checkhealth')[1]) 49 50 -- "test_plug/health/init.lua" should complete as "test_plug", not "test_plug.health". #30342 51 eq({ 52 'test_plug', 53 'test_plug.full_render', 54 'test_plug.submodule', 55 'test_plug.submodule_empty', 56 'test_plug.success1', 57 'test_plug.success2', 58 }, getcompletion('test_plug', 'checkhealth')) 59 end) 60 61 it('completion checks for vim.health._complete() return type #28456', function() 62 clear() 63 exec_lua([[vim.health._complete = function() return 1 end]]) 64 eq({}, getcompletion('', 'checkhealth')) 65 exec_lua([[vim.health._complete = function() return { 1 } end]]) 66 eq({}, getcompletion('', 'checkhealth')) 67 assert_alive() 68 end) 69 70 it('cmdline completion works with multiple args #35054', function() 71 clear() 72 n.feed(':checkhealth vim.ls<Tab>') 73 eq('checkhealth vim.lsp', fn.getcmdline()) 74 n.feed(' vim.prov<Tab>') 75 eq('checkhealth vim.lsp vim.provider', fn.getcmdline()) 76 end) 77 78 it('vim.g.health', function() 79 clear { 80 args_rm = { '-u' }, 81 args = { '--clean', '+set runtimepath+=test/functional/fixtures' }, 82 } 83 command("let g:health = {'style':'float'}") 84 command('checkhealth lsp') 85 eq( 86 'editor', 87 exec_lua([[ 88 return vim.api.nvim_win_get_config(0).relative 89 ]]) 90 ) 91 matches('health%.lua:%d+>$', fn.maparg('q', 'n', false, false)) 92 93 -- gO should not close the :checkhealth floating window. #34784 94 command('checkhealth full_render') 95 local win = api.nvim_get_current_win() 96 api.nvim_win_set_cursor(win, { 5, 1 }) 97 n.feed('gO') 98 eq(true, api.nvim_win_is_valid(win)) 99 eq('qf', api.nvim_get_option_value('filetype', { buf = 0 })) 100 end) 101 102 it("vim.provider works with a misconfigured 'shell'", function() 103 clear() 104 command([[set shell=echo\ WRONG!!!]]) 105 command('let g:loaded_perl_provider = 0') 106 command('let g:loaded_python3_provider = 0') 107 command('checkhealth vim.provider') 108 eq(nil, string.match(curbuf_contents(), 'WRONG!!!')) 109 end) 110 end) 111 112 describe('vim.health', function() 113 before_each(function() 114 clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } } 115 end) 116 117 describe(':checkhealth', function() 118 it('report_xx() renders correctly', function() 119 command('checkhealth full_render') 120 n.expect([[ 121 122 ============================================================================== 123 test_plug.full_render: 1 ⚠️ 1 ❌ 124 125 report 1 ~ 126 - ✅ OK life is fine 127 - ⚠️ WARNING no what installed 128 - ADVICE: 129 - pip what 130 - make what 131 132 report 2 ~ 133 - stuff is stable 134 - ❌ ERROR why no hardcopy 135 - ADVICE: 136 - :help |:hardcopy| 137 - :help |:TOhtml| 138 ]]) 139 end) 140 141 it('user FileType handler can modify report', function() 142 -- Define a FileType autocmd that removes emoji chars. 143 source [[ 144 autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g 145 checkhealth full_render 146 ]] 147 n.expect([[ 148 149 ============================================================================== 150 test_plug.full_render: 1 1 151 152 report 1 ~ 153 - OK life is fine 154 - WARNING no what installed 155 - ADVICE: 156 - pip what 157 - make what 158 159 report 2 ~ 160 - stuff is stable 161 - ERROR why no hardcopy 162 - ADVICE: 163 - :help |:hardcopy| 164 - :help |:TOhtml| 165 ]]) 166 end) 167 168 it('concatenates multiple reports', function() 169 command('checkhealth success1 success2 test_plug') 170 n.expect([[ 171 172 ============================================================================== 173 test_plug: ✅ 174 175 report 1 ~ 176 - ✅ OK everything is fine 177 178 report 2 ~ 179 - ✅ OK nothing to see here 180 181 ============================================================================== 182 test_plug.success1: ✅ 183 184 report 1 ~ 185 - ✅ OK everything is fine 186 187 report 2 ~ 188 - ✅ OK nothing to see here 189 190 ============================================================================== 191 test_plug.success2: ✅ 192 193 another 1 ~ 194 - ✅ OK ok 195 ]]) 196 end) 197 198 it('lua plugins submodules', function() 199 command('checkhealth test_plug.submodule') 200 n.expect([[ 201 202 ============================================================================== 203 test_plug.submodule: ✅ 204 205 report 1 ~ 206 - ✅ OK everything is fine 207 208 report 2 ~ 209 - ✅ OK nothing to see here 210 ]]) 211 end) 212 213 it('... including empty reports', function() 214 command('checkhealth test_plug.submodule_empty') 215 n.expect([[ 216 217 ============================================================================== 218 test_plug.submodule_empty: 1 ❌ 219 220 - ❌ ERROR The healthcheck report for "test_plug.submodule_empty" plugin is empty. 221 ]]) 222 end) 223 224 it('highlights OK, ERROR', function() 225 local screen = Screen.new(50, 12) 226 screen:set_default_attr_ids({ 227 h1 = { reverse = true }, 228 h2 = { foreground = tonumber('0x6a0dad') }, 229 Ok = { foreground = Screen.colors.LightGreen }, 230 Error = { foreground = Screen.colors.Red }, 231 Done = { foreground = Screen.colors.NvimDarkGreen }, 232 Bar = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGrey }, 233 }) 234 command('checkhealth foo success1') 235 command('set nofoldenable nowrap laststatus=0') 236 screen:expect { 237 grid = [[ 238 ^ | 239 {Bar: }| 240 {h1:foo: }| 241 | 242 - ❌ {Error:ERROR} No healthcheck found for "foo" plugin. | 243 | 244 {Bar: }| 245 {h1:test_plug.success1: }| 246 | 247 {h2:report 1} | 248 - ✅ {Ok:OK} everything is fine | 249 {Done:checkhealth}: checks done | 250 ]], 251 } 252 end) 253 254 it('gracefully handles invalid healthcheck', function() 255 command('checkhealth non_existent_healthcheck') 256 -- luacheck: ignore 613 257 n.expect([[ 258 259 ============================================================================== 260 non_existent_healthcheck: 1 ❌ 261 262 - ❌ ERROR No healthcheck found for "non_existent_healthcheck" plugin. 263 ]]) 264 end) 265 266 it('does not use vim.health as a healthcheck', function() 267 -- vim.health is not a healthcheck 268 command('checkhealth vim') 269 n.expect([[ 270 ERROR: No healthchecks found.]]) 271 end) 272 273 it('nested lua/ directory', function() 274 command('checkhealth lua') 275 n.expect([[ 276 277 ============================================================================== 278 test_plug.lua: ✅ 279 280 nested lua/ directory ~ 281 - ✅ OK everything is ok 282 ]]) 283 end) 284 285 it('&rtp can contain nested path (by packadd)', function() 286 -- re-add to ensure this appears before new nested rtp 287 command([[set runtimepath-=test/functional/fixtures]]) 288 command([[set runtimepath+=test/functional/fixtures]]) 289 command('set packpath+=test/functional/fixtures') 290 -- set rtp+=test/functional/fixtures/pack/foo/opt/healthy 291 command('packadd healthy') 292 command('checkhealth nest') 293 n.expect([[ 294 295 ============================================================================== 296 nest: ✅ 297 298 healthy pack ~ 299 - ✅ OK healthy ok 300 ]]) 301 end) 302 end) 303 end) 304 305 describe(':checkhealth window', function() 306 before_each(function() 307 clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } } 308 command('set nofoldenable nowrap laststatus=0') 309 end) 310 311 it('opens directly if no buffer created', function() 312 local screen = Screen.new(50, 12, { ext_multigrid = true }) 313 screen:set_default_attr_ids { 314 h1 = { reverse = true }, 315 h2 = { foreground = tonumber('0x6a0dad') }, 316 Done = { foreground = Screen.colors.NvimDarkGreen }, 317 [1] = { foreground = Screen.colors.Blue, bold = true }, 318 [14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray }, 319 [32] = { foreground = Screen.colors.PaleGreen2 }, 320 } 321 command('checkhealth success1') 322 screen:expect { 323 grid = [[ 324 ## grid 1 325 [2:--------------------------------------------------]|*11 326 [3:--------------------------------------------------]| 327 ## grid 2 328 ^ | 329 {14: }| 330 {14: } | 331 {h1:test_plug. }| 332 {h1:success1: }| 333 {h1: ✅} | 334 | 335 {h2:report 1} | 336 - ✅ {32:OK} everything is fine | 337 | 338 {h2:report 2} | 339 ## grid 3 340 {Done:checkhealth}: checks done | 341 ]], 342 } 343 end) 344 345 local function test_health_vsplit(left, emptybuf, mods) 346 local screen = Screen.new(50, 20, { ext_multigrid = true }) 347 screen:set_default_attr_ids { 348 h1 = { reverse = true }, 349 h2 = { foreground = tonumber('0x6a0dad') }, 350 Done = { foreground = Screen.colors.NvimDarkGreen }, 351 [1] = { foreground = Screen.colors.Blue, bold = true }, 352 [14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray }, 353 [32] = { foreground = Screen.colors.PaleGreen2 }, 354 } 355 if not emptybuf then 356 insert('hello') 357 end 358 command(mods .. ' checkhealth success1') 359 screen:expect( 360 ([[ 361 ## grid 1 362 %s 363 [3:--------------------------------------------------]| 364 ## grid 2 365 %s | 366 {1:~ }|*18 367 ## grid 3 368 {Done:checkhealth}: checks done | 369 ## grid 4 370 ^ | 371 {14: }|*3 372 {14: } | 373 {h1:test_plug. }| 374 {h1:success1: }| 375 {h1: }| 376 {h1: ✅} | 377 | 378 {h2:report 1} | 379 - ✅ {32:OK} everything is | 380 fine | 381 | 382 {h2:report 2} | 383 - ✅ {32:OK} nothing to see | 384 here | 385 | 386 {1:~ }| 387 ]]):format( 388 left and '[4:-------------------------]│[2:------------------------]|*19' 389 or '[2:------------------------]│[4:-------------------------]|*19', 390 emptybuf and ' ' or 'hello' 391 ) 392 ) 393 end 394 395 for _, mods in ipairs({ 'vertical', 'leftabove vertical', 'topleft vertical' }) do 396 it(('opens in left vsplit window with :%s and no buffer created'):format(mods), function() 397 test_health_vsplit(true, true, mods) 398 end) 399 it(('opens in left vsplit window with :%s and non-empty buffer'):format(mods), function() 400 test_health_vsplit(true, false, mods) 401 end) 402 end 403 404 for _, mods in ipairs({ 'rightbelow vertical', 'botright vertical' }) do 405 it(('opens in right vsplit window with :%s and no buffer created'):format(mods), function() 406 test_health_vsplit(false, true, mods) 407 end) 408 it(('opens in right vsplit window with :%s and non-empty buffer'):format(mods), function() 409 test_health_vsplit(false, false, mods) 410 end) 411 end 412 413 local function test_health_split(top, emptybuf, mods) 414 local screen = Screen.new(50, 25, { ext_multigrid = true }) 415 screen._default_attr_ids = nil 416 if not emptybuf then 417 insert('hello') 418 end 419 command(mods .. ' checkhealth success1') 420 screen:expect( 421 ([[ 422 ## grid 1 423 %s 424 [3:--------------------------------------------------]| 425 ## grid 2 426 %s | 427 ~ |*10 428 ## grid 3 429 checkhealth: checks done | 430 ## grid 4 431 ^ | 432 |*2 433 test_plug. | 434 success1: | 435 ✅ | 436 | 437 report 1 | 438 - ✅ OK everything is fine | 439 | 440 report 2 | 441 - ✅ OK nothing to see here | 442 ]]):format( 443 top 444 and [[ 445 [4:--------------------------------------------------]|*12 446 health:// [-] | 447 [2:--------------------------------------------------]|*11]] 448 or ([[ 449 [2:--------------------------------------------------]|*11 450 [No Name] %s | 451 [4:--------------------------------------------------]|*12]]):format( 452 emptybuf and ' ' or '[+]' 453 ), 454 emptybuf and ' ' or 'hello' 455 ) 456 ) 457 end 458 459 for _, mods in ipairs({ 'horizontal', 'leftabove', 'topleft' }) do 460 it(('opens in top split window with :%s and no buffer created'):format(mods), function() 461 test_health_split(true, true, mods) 462 end) 463 it(('opens in top split window with :%s and non-empty buffer'):format(mods), function() 464 test_health_split(true, false, mods) 465 end) 466 end 467 468 for _, mods in ipairs({ 'rightbelow', 'botright' }) do 469 it(('opens in bottom split window with :%s and no buffer created'):format(mods), function() 470 test_health_split(false, true, mods) 471 end) 472 it(('opens in bottom split window with :%s and non-empty buffer'):format(mods), function() 473 test_health_split(false, false, mods) 474 end) 475 end 476 477 it('opens in tab', function() 478 -- create an empty buffer called "my_buff" 479 api.nvim_create_buf(false, true) 480 command('file my_buff') 481 command('checkhealth success1') 482 -- define a function that collects all buffers in each tab 483 -- returns a dict like {tab1 = ["buf1", "buf2"], tab2 = ["buf3"]} 484 source([[ 485 function CollectBuffersPerTab() 486 let buffs = {} 487 for i in range(tabpagenr('$')) 488 let key = 'tab' . (i + 1) 489 let value = [] 490 for j in tabpagebuflist(i + 1) 491 call add(value, bufname(j)) 492 endfor 493 let buffs[key] = value 494 endfor 495 return buffs 496 endfunction 497 ]]) 498 local buffers_per_tab = fn.CollectBuffersPerTab() 499 eq(buffers_per_tab, { tab1 = { 'my_buff' }, tab2 = { 'health://' } }) 500 end) 501 end)