Files
atom/spec/extensions/autocomplete-spec.coffee
2012-10-23 13:21:35 -07:00

443 lines
18 KiB
CoffeeScript

$ = require 'jquery'
Autocomplete = require 'autocomplete'
Buffer = require 'buffer'
Editor = require 'editor'
RootView = require 'root-view'
describe "Autocomplete", ->
autocomplete = null
editor = null
miniEditor = null
beforeEach ->
editor = new Editor(editSession: fixturesProject.buildEditSessionForPath('sample.js'))
autocomplete = new Autocomplete(editor)
miniEditor = autocomplete.miniEditor
afterEach ->
editor?.remove()
describe "@activate(rootView)", ->
it "activates autocomplete on all existing and future editors (but not on autocomplete's own mini editor)", ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
rootView.simulateDomAttachment()
Autocomplete.activate(rootView)
leftEditor = rootView.getActiveEditor()
rightEditor = rootView.getActiveEditor().splitRight()
spyOn(Autocomplete.prototype, 'initialize')
leftEditor.trigger 'autocomplete:attach'
expect(leftEditor.find('.autocomplete')).toExist()
expect(rightEditor.find('.autocomplete')).not.toExist()
autoCompleteView = leftEditor.find('.autocomplete').view()
autoCompleteView.trigger 'core:cancel'
expect(leftEditor.find('.autocomplete')).not.toExist()
rightEditor.trigger 'autocomplete:attach'
expect(rightEditor.find('.autocomplete')).toExist()
expect(Autocomplete.prototype.initialize).not.toHaveBeenCalled()
rootView.deactivate()
describe 'autocomplete:attach event', ->
it "shows autocomplete view and focuses its mini-editor", ->
expect(editor.find('.autocomplete')).not.toExist()
editor.trigger "autocomplete:attach"
expect(editor.find('.autocomplete')).toExist()
expect(autocomplete.editor.isFocused).toBeFalsy()
expect(autocomplete.miniEditor.isFocused).toBeTruthy()
describe "when no text is selected", ->
it 'autocompletes word when there is only a prefix', ->
editor.getBuffer().insert([10,0] ,"extra:s:extra")
editor.setCursorBufferPosition([10,7])
autocomplete.attach()
expect(editor.lineForBufferRow(10)).toBe "extra:sort:extra"
expect(editor.getCursorBufferPosition()).toEqual [10,10]
expect(editor.getSelection().getBufferRange()).toEqual [[10,7], [10,10]]
expect(autocomplete.matchesList.find('li').length).toBe 2
expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('sort')
expect(autocomplete.matchesList.find('li:eq(1)')).toHaveText('shift')
it 'autocompletes word when there is only a suffix', ->
editor.getBuffer().insert([10,0] ,"extra:n:extra")
editor.setCursorBufferPosition([10,6])
autocomplete.attach()
expect(editor.lineForBufferRow(10)).toBe "extra:function:extra"
expect(editor.getCursorBufferPosition()).toEqual [10,13]
expect(editor.getSelection().getBufferRange()).toEqual [[10,6], [10,13]]
expect(autocomplete.matchesList.find('li').length).toBe 2
expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('function')
expect(autocomplete.matchesList.find('li:eq(1)')).toHaveText('return')
it 'autocompletes word when there is a single prefix and suffix match', ->
editor.getBuffer().insert([8,43] ,"q")
editor.setCursorBufferPosition([8,44])
autocomplete.attach()
expect(editor.lineForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(quicksort(right));"
expect(editor.getCursorBufferPosition()).toEqual [8,52]
expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
expect(autocomplete.matchesList.find('li').length).toBe 0
it "show's that there are no matches found when there is no prefix or suffix", ->
editor.setCursorBufferPosition([10, 0])
autocomplete.attach()
expect(autocomplete.matchesList.find('li').length).toBe 1
expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText "No matches found"
describe "when text is selected", ->
it 'autocompletes word when there is only a prefix', ->
editor.getBuffer().insert([10,0] ,"extra:sort:extra")
editor.setSelectedBufferRange [[10,7], [10,10]]
autocomplete.attach()
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
expect(editor.getCursorBufferPosition()).toEqual [10,11]
expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
expect(autocomplete.matchesList.find('li').length).toBe 0
it 'autocompletes word when there is only a suffix', ->
editor.getBuffer().insert([10,0] ,"extra:current:extra")
editor.setSelectedBufferRange [[10,6],[10,12]]
autocomplete.attach()
expect(editor.lineForBufferRow(10)).toBe "extra:quicksort:extra"
expect(editor.getCursorBufferPosition()).toEqual [10,14]
expect(editor.getSelection().getBufferRange()).toEqual [[10,6],[10,14]]
expect(autocomplete.matchesList.find('li').length).toBe 7
expect(autocomplete.matchesList.find('li:contains(current)')).not.toExist()
it 'autocompletes word when there is a prefix and suffix', ->
editor.setSelectedBufferRange [[5,7],[5,12]]
autocomplete.attach()
expect(editor.lineForBufferRow(5)).toBe " concat = items.shift();"
expect(editor.getCursorBufferPosition()).toEqual [5,12]
expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
expect(autocomplete.matchesList.find('li').length).toBe 0
it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete menu', ->
editor.getBuffer().insert([10,0] ,"extra:sort:extra")
editor.setSelectedBufferRange [[10,7], [10,9]]
autocomplete.attach()
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
expect(editor.getCursorBufferPosition()).toEqual [10,11]
expect(editor.getSelection().isEmpty()).toBeTruthy()
expect(editor.find('.autocomplete')).not.toExist()
describe 'core:confirm event', ->
describe "where there are matches", ->
describe "where there is no selection", ->
it "closes the menu and moves the cursor to the end", ->
editor.getBuffer().insert([10,0] ,"extra:sh:extra")
editor.setCursorBufferPosition([10,8])
autocomplete.attach()
miniEditor.trigger "core:confirm"
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
expect(editor.getCursorBufferPosition()).toEqual [10,11]
expect(editor.getSelection().isEmpty()).toBeTruthy()
expect(editor.find('.autocomplete')).not.toExist()
describe "when there are no matches", ->
it "closes the menu without changing the buffer", ->
editor.getBuffer().insert([10,0] ,"xxx")
editor.setCursorBufferPosition [10, 3]
autocomplete.attach()
expect(autocomplete.matchesList.find('li').length).toBe 1
expect(autocomplete.matchesList.find('li')).toHaveText ('No matches found')
miniEditor.trigger "core:confirm"
expect(editor.lineForBufferRow(10)).toBe "xxx"
expect(editor.getCursorBufferPosition()).toEqual [10,3]
expect(editor.getSelection().isEmpty()).toBeTruthy()
expect(editor.find('.autocomplete')).not.toExist()
describe 'core:cancel event', ->
it 'does not replace selection, removes autocomplete view and returns focus to editor', ->
editor.getBuffer().insert([10,0] ,"extra:so:extra")
editor.setSelectedBufferRange [[10,7], [10,8]]
originalSelectionBufferRange = editor.getSelection().getBufferRange()
autocomplete.attach()
editor.setCursorBufferPosition [0, 0] # even if selection changes before cancel, it should work
miniEditor.trigger "core:cancel"
expect(editor.lineForBufferRow(10)).toBe "extra:so:extra"
expect(editor.getSelection().getBufferRange()).toEqual originalSelectionBufferRange
expect(editor.find('.autocomplete')).not.toExist()
it "does not clear out a previously confirmed selection when canceling with an empty list", ->
editor.getBuffer().insert([10, 0], "sort\n")
editor.setCursorBufferPosition([10, 0])
autocomplete.attach()
miniEditor.trigger 'core:confirm'
expect(editor.lineForBufferRow(10)).toBe 'quicksort'
editor.setCursorBufferPosition([11, 0])
autocomplete.attach()
miniEditor.trigger 'core:cancel'
expect(editor.lineForBufferRow(10)).toBe 'quicksort'
describe 'move-up event', ->
it "highlights the previous match and replaces the selection with it", ->
editor.getBuffer().insert([10,0] ,"extra:t:extra")
editor.setCursorBufferPosition([10,6])
autocomplete.attach()
miniEditor.trigger "core:move-up"
expect(editor.lineForBufferRow(10)).toBe "extra:concat:extra"
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(1)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(7)')).toHaveClass('selected')
miniEditor.trigger "core:move-up"
expect(editor.lineForBufferRow(10)).toBe "extra:right:extra"
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(7)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(6)')).toHaveClass('selected')
it "scrolls to the selected match if it is out of view", ->
editor.getBuffer().insert([10,0] ,"t")
editor.setCursorBufferPosition([10, 0])
editor.attachToDom()
autocomplete.attach()
matchesList = autocomplete.matchesList
matchesList.height(100)
expect(matchesList.height()).toBeLessThan matchesList[0].scrollHeight
matchCount = matchesList.find('li').length
miniEditor.trigger 'core:move-up'
expect(matchesList.scrollBottom()).toBe matchesList[0].scrollHeight
miniEditor.trigger 'core:move-up' for i in [1...matchCount]
expect(matchesList.scrollTop()).toBe 0
describe 'move-down event', ->
it "highlights the next match and replaces the selection with it", ->
editor.getBuffer().insert([10,0] ,"extra:s:extra")
editor.setCursorBufferPosition([10,7])
autocomplete.attach()
miniEditor.trigger "core:move-down"
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
expect(autocomplete.find('li:eq(1)')).toHaveClass('selected')
miniEditor.trigger "core:move-down"
expect(editor.lineForBufferRow(10)).toBe "extra:sort:extra"
expect(autocomplete.find('li:eq(0)')).toHaveClass('selected')
expect(autocomplete.find('li:eq(1)')).not.toHaveClass('selected')
it "scrolls to the selected match if it is out of view", ->
editor.getBuffer().insert([10,0] ,"t")
editor.setCursorBufferPosition([10, 0])
editor.attachToDom()
autocomplete.attach()
matchesList = autocomplete.matchesList
matchesList.height(100)
expect(matchesList.height()).toBeLessThan matchesList[0].scrollHeight
matchCount = matchesList.find('li').length
miniEditor.trigger 'core:move-down' for i in [1...matchCount]
expect(matchesList.scrollBottom()).toBe matchesList[0].scrollHeight
miniEditor.trigger 'core:move-down'
expect(matchesList.scrollTop()).toBe 0
describe "when a match is clicked in the match list", ->
it "selects and confirms the match", ->
editor.getBuffer().insert([10,0] ,"t")
editor.setCursorBufferPosition([10, 0])
autocomplete.attach()
matchToSelect = autocomplete.matchesList.find('li:eq(1)')
matchToSelect.mousedown()
expect(matchToSelect).toMatchSelector('.selected')
matchToSelect.mouseup()
expect(autocomplete.parent()).not.toExist()
expect(editor.lineForBufferRow(10)).toBe matchToSelect.text()
it "cancels the autocomplete when clicking on the 'No matches found' li", ->
editor.getBuffer().insert([10,0] ,"t")
editor.setCursorBufferPosition([10, 0])
autocomplete.attach()
miniEditor.insertText('xxx')
autocomplete.matchesList.find('li').mousedown().mouseup()
expect(autocomplete.parent()).not.toExist()
expect(editor.lineForBufferRow(10)).toBe "t"
describe "when the mini-editor receives keyboard input", ->
describe "when text is removed from the mini-editor", ->
it "reloads the match list based on the mini-editor's text", ->
editor.getBuffer().insert([10,0] ,"t")
editor.setCursorBufferPosition([10,0])
autocomplete.attach()
expect(autocomplete.matchesList.find('li').length).toBe 8
miniEditor.textInput('c')
expect(autocomplete.matchesList.find('li').length).toBe 3
miniEditor.backspace()
expect(autocomplete.matchesList.find('li').length).toBe 8
describe "when the text contains only word characters", ->
it "narrows the list of completions with the fuzzy match algorithm", ->
editor.getBuffer().insert([10,0] ,"t")
editor.setCursorBufferPosition([10,0])
autocomplete.attach()
expect(autocomplete.matchesList.find('li').length).toBe 8
miniEditor.textInput('i')
expect(autocomplete.matchesList.find('li').length).toBe 4
expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText 'pivot'
expect(autocomplete.matchesList.find('li:eq(0)')).toHaveClass 'selected'
expect(autocomplete.matchesList.find('li:eq(1)')).toHaveText 'shift'
expect(autocomplete.matchesList.find('li:eq(2)')).toHaveText 'right'
expect(autocomplete.matchesList.find('li:eq(3)')).toHaveText 'quicksort'
expect(editor.lineForBufferRow(10)).toEqual 'pivot'
miniEditor.textInput('o')
expect(autocomplete.matchesList.find('li').length).toBe 2
expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText 'pivot'
expect(autocomplete.matchesList.find('li:eq(1)')).toHaveText 'quicksort'
describe "when a non-word character is typed in the mini-editor", ->
it "immediately confirms the current completion choice and inserts that character into the buffer", ->
editor.getBuffer().insert([10,0] ,"t")
editor.setCursorBufferPosition([10,0])
autocomplete.attach()
miniEditor.textInput('iv')
expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText 'pivot'
miniEditor.textInput(' ')
expect(autocomplete.parent()).not.toExist()
expect(editor.lineForBufferRow(10)).toEqual 'pivot '
describe 'when the mini-editor loses focus before the selection is confirmed', ->
it "cancels the autocomplete", ->
editor.attachToDom()
autocomplete.attach()
spyOn(autocomplete, "cancel")
editor.focus()
expect(autocomplete.cancel).toHaveBeenCalled()
describe 'when changes are made to the buffer', ->
describe "when the autocomplete menu is detached", ->
it 'updates word list', ->
spyOn(autocomplete, 'buildWordList')
editor.getBuffer().change([[0,4],[0,13]], "sauron")
expect(autocomplete.buildWordList).toHaveBeenCalled()
describe "when the autocomplete menu is attached and the change was caused by autocomplete itself", ->
it 'does not rebuild the word list', ->
editor.getBuffer().insert([10,0] ,"extra:s:extra")
spyOn(autocomplete, 'buildWordList')
editor.setCursorBufferPosition([10,7])
autocomplete.attach()
expect(autocomplete.buildWordList).not.toHaveBeenCalled()
describe "when a new edit session is assigned on editor", ->
it 'creates and uses a new word list based on new buffer', ->
wordList = autocomplete.wordList
expect(wordList).toContain "quicksort"
expect(wordList).not.toContain "Some"
editor.edit(fixturesProject.buildEditSessionForPath('sample.txt'))
wordList = autocomplete.wordList
expect(wordList).not.toContain "quicksort"
expect(wordList).toContain "Some"
it 'stops listening to previous buffers change events', ->
previousBuffer = editor.getBuffer()
editor.edit(fixturesProject.buildEditSessionForPath('sample.txt'))
spyOn(autocomplete, "buildWordList")
previousBuffer.change([[0,0],[0,1]], "sauron")
expect(autocomplete.buildWordList).not.toHaveBeenCalled()
describe 'when the editor is removed', ->
it 'removes event listeners from its buffer', ->
spyOn(autocomplete, 'buildWordList').andCallThrough()
editor.getBuffer().insert([0,0], "s")
expect(autocomplete.buildWordList).toHaveBeenCalled()
autocomplete.buildWordList.reset()
editor.remove()
editor.getBuffer().insert([0,0], "s")
expect(autocomplete.buildWordList).not.toHaveBeenCalled()
editor = null
describe ".attach()", ->
beforeEach ->
editor.attachToDom()
setEditorHeightInLines(editor, 13)
editor.renderLines() # Ensures the editor only has 13 lines visible
editor.setCursorBufferPosition [1, 1]
describe "when the autocomplete view fits below the cursor", ->
it "adds the autocomplete view to the editor below the cursor", ->
cursorPixelPosition = editor.pixelPositionForScreenPosition(editor.getCursorScreenPosition())
autocomplete.attach()
expect(editor.find('.autocomplete')).toExist()
expect(autocomplete.position().top).toBe cursorPixelPosition.top + editor.lineHeight
expect(autocomplete.position().left).toBe cursorPixelPosition.left
describe "when the autocomplete view does not fit below the cursor", ->
it "adds the autocomplete view to the editor above the cursor", ->
editor.setCursorScreenPosition([11, 0])
editor.insertText('t ')
editor.setCursorScreenPosition([11, 0])
cursorPixelPosition = editor.pixelPositionForScreenPosition(editor.getCursorScreenPosition())
autocomplete.attach()
expect(autocomplete.parent()).toExist()
autocompleteBottom = autocomplete.position().top + autocomplete.outerHeight()
expect(autocompleteBottom).toBe cursorPixelPosition.top
expect(autocomplete.position().left).toBe cursorPixelPosition.left
describe ".detach()", ->
it "clears the mini-editor and unbinds autocomplete event handlers for move-up and move-down", ->
autocomplete.attach()
miniEditor.setText('foo')
autocomplete.detach()
expect(miniEditor.getText()).toBe ''
editor.trigger 'core:move-down'
expect(editor.getCursorBufferPosition().row).toBe 1
editor.trigger 'core:move-up'
expect(editor.getCursorBufferPosition().row).toBe 0