Before:
  Save g:ale_echo_msg_format
  Save g:ale_echo_cursor
  Save b:ale_lint_on_insert_leave

  let g:ale_echo_msg_format = '%code: %%s'
  let b:ale_lint_on_insert_leave = 0

  " We should prefer the error message at column 10 instead of the warning.
  let g:ale_buffer_info = {
  \ bufnr('%'): {
  \   'loclist': [
  \     {
  \       'lnum': 1,
  \       'col': 10,
  \       'bufnr': bufnr('%'),
  \       'vcol': 0,
  \       'linter_name': 'bettercode',
  \       'nr': -1,
  \       'type': 'W',
  \       'code': 'semi',
  \       'text': 'Ignore me.',
  \     },
  \     {
  \       'lnum': 1,
  \       'col': 10,
  \       'bufnr': bufnr('%'),
  \       'vcol': 0,
  \       'linter_name': 'bettercode',
  \       'nr': -1,
  \       'type': 'E',
  \       'code': 'semi',
  \       'text': "Missing semicolon.\r",
  \       'detail': "Every statement should end with a semicolon\nsecond line",
  \     },
  \     {
  \       'lnum': 1,
  \       'col': 14,
  \       'bufnr': bufnr('%'),
  \       'vcol': 0,
  \       'linter_name': 'bettercode',
  \       'nr': -1,
  \       'type': 'I',
  \       'text': 'Some information',
  \     },
  \     {
  \       'lnum': 2,
  \       'col': 10,
  \       'bufnr': bufnr('%'),
  \       'vcol': 0,
  \       'linter_name': 'bettercode',
  \       'nr': -1,
  \       'type': 'W',
  \       'code': 'space-infix-ops',
  \       'text': 'Infix operators must be spaced.',
  \     },
  \     {
  \       'lnum': 2,
  \       'col': 15,
  \       'bufnr': bufnr('%'),
  \       'vcol': 0,
  \       'linter_name': 'bettercode',
  \       'nr': -1,
  \       'type': 'E',
  \       'code': 'radix',
  \       'text': 'Missing radix parameter',
  \     },
  \     {
  \       'lnum': 3,
  \       'col': 1,
  \       'bufnr': bufnr('%'),
  \       'vcol': 0,
  \       'linter_name': 'bettercode',
  \       'nr': -1,
  \       'type': 'E',
  \       'text': 'lowercase error',
  \     },
  \   ],
  \ },
  \}

  " Turn off other features, we only care about this one feature in this test.
  let g:ale_set_loclist = 0
  let g:ale_set_signs = 0
  let g:ale_set_highlights = 0
  let g:ale_echo_cursor = 1

  function GetLastMessage()
    redir => l:output
      silent mess
    redir END

    let l:lines = split(l:output, "\n")

    return empty(l:lines) ? '' : l:lines[-1]
  endfunction

  call ale#linter#Reset()
  call ale#linter#PreventLoading('javascript')

After:
  Restore

  call cursor(1, 1)

  let g:ale_set_loclist = 1
  let g:ale_set_signs = 1
  let g:ale_set_highlights = 1

  let g:ale_buffer_info = {}

  unlet! g:output
  unlet! b:ale_loclist_msg_format

  delfunction GetLastMessage

  " Clearing the messages breaks tests on NeoVim for some reason, but all
  " we need to do for these tests is just make it so the last message isn't
  " carried over between test cases.
  echomsg ''

  " Close the preview window if it's open.
  if &filetype is# 'ale-preview'
    noautocmd :q!
  endif

  call ale#linter#Reset()

Given javascript(A Javscript file with warnings/errors):
  var x = 3 + 12345678
  var x = 5*2 + parseInt("10");
  // comment

Execute(Messages should be shown for the correct lines):
  call cursor(1, 1)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'semi: Missing semicolon.', GetLastMessage()

Execute(Messages should be shown for earlier columns):
  call cursor(2, 1)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'space-infix-ops: Infix operators must be spaced.', GetLastMessage()

Execute(Messages should be shown for later columns):
  call cursor(2, 16)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'radix: Missing radix parameter', GetLastMessage()

Execute(The message at the cursor should be shown when linting ends):
  call cursor(1, 1)
  call ale#engine#SetResults(
  \ bufnr('%'),
  \ g:ale_buffer_info[bufnr('%')].loclist,
  \)

  AssertEqual 'semi: Missing semicolon.', GetLastMessage()

Execute(The message at the cursor should be shown on InsertLeave):
  call cursor(2, 9)
  doautocmd InsertLeave

  AssertEqual 'space-infix-ops: Infix operators must be spaced.', GetLastMessage()

Execute(ALEDetail should print 'detail' attributes):
  call cursor(1, 1)

  ALEDetail

  AssertEqual
  \ ['Every statement should end with a semicolon', 'second line'],
  \ getline(1, '$')

Execute(ALEDetail should print regular 'text' attributes):
  call cursor(2, 10)

  ALEDetail

  " ALEDetail opens a window, so check the text in it.
  AssertEqual
  \ ['Infix operators must be spaced.'],
  \ getline(1, '$')

Execute(ALEDetail should not capitlise cursor messages):
  call cursor(3, 1)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'lowercase error', GetLastMessage()

Execute(The linter name should be formatted into the message correctly):
  let g:ale_echo_msg_format = '%linter%: %s'

  call cursor(2, 9)
  call ale#cursor#EchoCursorWarning()

  AssertEqual
  \ 'bettercode: Infix operators must be spaced.',
  \ GetLastMessage()

Execute(The severity should be formatted into the message correctly):
  let g:ale_echo_msg_format = '%severity%: %s'

  call cursor(2, 9)
  call ale#cursor#EchoCursorWarning()

  AssertEqual
  \ 'Warning: Infix operators must be spaced.',
  \ GetLastMessage()

  call cursor(1, 10)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'Error: Missing semicolon.', GetLastMessage()

  call cursor(1, 14)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'Info: Some information', GetLastMessage()

Execute(The %code% and %ifcode% should show the code and some text):
  let g:ale_echo_msg_format = '%(code) %%s'

  call cursor(2, 9)
  call ale#cursor#EchoCursorWarning()

  AssertEqual
  \ '(space-infix-ops) Infix operators must be spaced.',
  \ GetLastMessage()

Execute(The %code% and %ifcode% should be removed when there's no code):
  let g:ale_echo_msg_format = '%(code) %%s'

  call cursor(1, 14)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'Some information', GetLastMessage()

Execute(The buffer message format option should take precedence):
  let g:ale_echo_msg_format = '%(code) %%s'
  let b:ale_echo_msg_format = 'FOO %s'

  call cursor(1, 14)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'FOO Some information', GetLastMessage()

Execute(The cursor message shouldn't be echoed if the option is off):
  let g:ale_echo_cursor = 0
  echom 'foo'

  call cursor(1, 1)
  call ale#cursor#EchoCursorWarning()

  AssertEqual 'foo', GetLastMessage()