Before:
  call ale#test#SetDirectory('/testplugin/test')
  call ale#test#SetFilename('dummy.txt')

  let g:Callback = 0
  let g:message_list = []
  let g:item_list = []
  let g:show_message_arg_list = []

  let g:ale_floating_preview = 0
  let g:ale_hover_to_floating_preview = 0
  let g:ale_detail_to_floating_preview = 0

  runtime autoload/ale/linter.vim
  runtime autoload/ale/lsp.vim
  runtime autoload/ale/lsp_linter.vim
  runtime autoload/ale/util.vim
  runtime autoload/ale/floating_preview.vim
  runtime autoload/ale/hover.vim

  let g:floated_lines = []
  let g:floating_preview_show_called = 0

  " Stub out so we can track the call
  function! ale#floating_preview#Show(lines, ...) abort
    let g:floating_preview_show_called = 1
    let g:floated_lines = a:lines
    return win_getid()
  endfunction

  function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
    let g:Callback = a:callback

    return {
    \ 'command': 'foobar',
    \ 'connection_id': 347,
    \ 'project_root': '/foo/bar',
    \}
  endfunction

  function! ale#lsp#Send(conn_id, message, root) abort
    call add(g:message_list, a:message)

    return 42
  endfunction

  function! ale#util#ShowMessage(string, ...) abort
      call add(g:show_message_arg_list, [a:string] + a:000)
  endfunction

  function! HandleValidLSPResult(result) abort
    " The cursor is beyond the length of the line.
    " We will clamp the cursor position with the line length.
    call setpos('.', [bufnr(''), 1, 5, 0])

    call ale#hover#SetMap({3: {
    \ 'buffer': bufnr(''),
    \ 'line': 1,
    \ 'column': 5,
    \}})
    call ale#hover#HandleLSPResponse(
    \ 1,
    \ {
    \   'id': 3,
    \   'result': a:result,
    \ }
    \)
  endfunction


After:
  call ale#hover#SetMap({})
  call ale#test#RestoreDirectory()
  call ale#linter#Reset()

  unlet! g:Callback
  unlet! g:message_list
  unlet! b:ale_linters
  unlet! g:show_message_arg_list

  delfunction HandleValidLSPResult

  runtime autoload/ale/lsp_linter.vim
  runtime autoload/ale/lsp.vim
  runtime autoload/ale/util.vim
  runtime autoload/ale/floating_preview.vim

Given python(Some Python file):
  foo
  somelongerline
  bazxyzxyzxyz

Execute(Other messages for the tsserver handler should be ignored):
  call ale#hover#HandleTSServerResponse(1, {'command': 'foo'})

Execute(Failed hover responses should be handled correctly):
  call ale#hover#SetMap({3: {}})
  call ale#hover#HandleTSServerResponse(
  \ 1,
  \ {'command': 'quickinfo', 'request_seq': 3}
  \)
  AssertEqual {}, ale#hover#GetMap()

Given typescript(Some typescript file):
  foo
  somelongerline
  bazxyzxyzxyz

Execute(tsserver quickinfo responses will null missing bodies should be handled):
  call ale#hover#SetMap({3: {}})
  call ale#hover#HandleTSServerResponse(
  \ 1,
  \ {
  \   'command': 'quickinfo',
  \   'request_seq': 3,
  \   'success': v:true,
  \ }
  \)

  AssertEqual {}, ale#hover#GetMap()

Execute(tsserver quickinfo displayString values should be displayed):
  call ale#hover#SetMap({3: {'buffer': bufnr('')}})
  call ale#hover#HandleTSServerResponse(
  \ 1,
  \ {
  \   'command': 'quickinfo',
  \   'request_seq': 3,
  \   'success': v:true,
  \   'body': {'displayString': 'foo bar'},
  \ }
  \)

  AssertEqual [['foo bar']], g:show_message_arg_list
  AssertEqual {}, ale#hover#GetMap()

Execute(LSP hover responses with just a string should be handled):
  call HandleValidLSPResult({'contents': 'foobar'})

  AssertEqual [['foobar', {'commands': []}]], g:show_message_arg_list
  AssertEqual {}, ale#hover#GetMap()

Execute(LSP hover null responses should be handled):
  call HandleValidLSPResult(v:null)

  AssertEqual [], g:show_message_arg_list
  AssertEqual {}, ale#hover#GetMap()

Execute(LSP hover responses with markup content should be handled):
  call HandleValidLSPResult({'contents': {'kind': 'markdown', 'value': 'markup'}})

  AssertEqual [['markup', {'commands': []}]], g:show_message_arg_list
  AssertEqual {}, ale#hover#GetMap()

Execute(LSP hover responses with markup content missing values should be handled):
  call HandleValidLSPResult({'contents': {'kind': 'markdown'}})

  AssertEqual [], g:show_message_arg_list
  AssertEqual {}, ale#hover#GetMap()

Execute(LSP hover response with lists of strings should be handled):
  call HandleValidLSPResult({'contents': [
  \  "foo\n",
  \  "bar\n",
  \]})

  AssertEqual [["foo\n\nbar", {'commands': []}]], g:show_message_arg_list
  AssertEqual {}, ale#hover#GetMap()

Execute(LSP hover response with lists of strings and marked strings should be handled):
  call HandleValidLSPResult({'contents': [
  \  {'language': 'python', 'value': 'foo'},
  \  "bar\n",
  \]})

  AssertEqual [
  \ [
  \   "foo\n\nbar",
  \   {
  \     'commands': [
  \       'unlet! b:current_syntax',
  \       'syntax include @ALE_hover_python syntax/python.vim',
  \       'syntax region ALE_hover_1 start=/\%1l/ end=/\%2l/ contains=@ALE_hover_python',
  \     ],
  \   },
  \ ],
  \], g:show_message_arg_list
  AssertEqual {}, ale#hover#GetMap()

Execute(LSP hover with ale_floating_preview should float):
  let g:ale_floating_preview = 1

  call HandleValidLSPResult({'contents': "the message\ncontinuing"})

  AssertEqual 1, g:floating_preview_show_called
  AssertEqual ["the message", "continuing"], g:floated_lines

Execute(LSP hover ale_hover_to_floating_preview should float):
  let g:ale_hover_to_floating_preview = 1

  call HandleValidLSPResult({'contents': "the message\ncontinuing"})

  AssertEqual 1, g:floating_preview_show_called
  AssertEqual ["the message", "continuing"], g:floated_lines


Execute(LSP hover by default should not float):
  call HandleValidLSPResult({'contents': "the message\ncontinuing"})

  AssertEqual 0, g:floating_preview_show_called

Execute(tsserver responses for documentation requests should be handled):
  call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}})

  call ale#hover#HandleTSServerResponse(
  \ 1,
  \ {
  \   'command': 'quickinfo',
  \   'request_seq': 3,
  \   'success': v:true,
  \   'body': {
  \     'documentation': 'foo is a very good method',
  \     'displayString': 'foo bar',
  \   },
  \ }
  \)

  " The preview window should show the text.
  AssertEqual ['foo is a very good method'], ale#test#GetPreviewWindowText()
  silent! pclose

Execute(hover with show_documentation should be in the preview window, not floating):
  let g:ale_hover_to_floating_preview = 1
  let g:ale_floating_preview = 1

  call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}})

  call ale#hover#HandleTSServerResponse(
  \ 1,
  \ {
  \   'command': 'quickinfo',
  \   'request_seq': 3,
  \   'success': v:true,
  \   'body': {
  \     'documentation': 'foo is a very good method',
  \     'displayString': 'foo bar ',
  \   },
  \ }
  \)

  let expected = ["Every statement should end with a semicolon", "second line"]

  AssertEqual 0, g:floating_preview_show_called

Execute(TSServer hover without show_documentation and ale_floating_preview should float):
  let g:ale_floating_preview = 1

  call ale#hover#SetMap({3: {'buffer': bufnr('')}})

  call ale#hover#HandleTSServerResponse(
  \ 1,
  \ {
  \   'command': 'quickinfo',
  \   'request_seq': 3,
  \   'success': v:true,
  \   'body': {
  \     'displayString': "the message\ncontinuing",
  \   },
  \ }
  \)

  AssertEqual 1, g:floating_preview_show_called
  AssertEqual ["the message", "continuing"], g:floated_lines
