Extensions have a src and specs directory now. Move existing extension specs.

Move the extensions spec code inside of the extension's spec directory. Move source code to the extension's src directory
This commit is contained in:
Corey Johnson
2012-10-25 11:45:58 -07:00
parent 6870f21ca5
commit 2af29c9934
68 changed files with 346 additions and 84 deletions

View File

@@ -1,442 +0,0 @@
$ = 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

View File

@@ -1,373 +0,0 @@
CommandInterpreter = require 'command-panel/command-interpreter'
Project = require 'project'
Buffer = require 'buffer'
EditSession = require 'edit-session'
describe "CommandInterpreter", ->
[project, interpreter, editSession, buffer, anchorCountBefore] = []
beforeEach ->
project = new Project(fixturesProject.resolve('dir/'))
interpreter = new CommandInterpreter(fixturesProject)
editSession = fixturesProject.buildEditSessionForPath('sample.js')
buffer = editSession.buffer
afterEach ->
editSession?.destroy()
expect(buffer.getAnchors().length).toBe 0
describe "addresses", ->
beforeEach ->
editSession.addSelectionForBufferRange([[7,0], [7,11]])
editSession.addSelectionForBufferRange([[8,0], [8,11]])
describe "a line address", ->
it "selects the specified line", ->
waitsForPromise -> interpreter.eval('4', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[3, 0], [4, 0]]
describe "0", ->
it "selects the zero-length string at the start of the file", ->
waitsForPromise -> interpreter.eval('0', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [0,0]]
interpreter.eval('0,1', editSession)
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [1,0]]
describe "$", ->
it "selects EOF", ->
waitsForPromise -> interpreter.eval('$', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[12,2], [12,2]]
waitsForPromise -> interpreter.eval('1,$', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [12,2]]
describe ".", ->
describe "when a single selection", ->
it 'maintains the current selection', ->
editSession.clearSelections()
waitsForPromise ->
editSession.setSelectedBufferRange([[1,1], [2,2]])
interpreter.eval('.', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[1,1], [2,2]]
waitsForPromise ->
editSession.setSelectedBufferRange([[1,1], [2,2]])
interpreter.eval('.,', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[1,1], [12,2]]
waitsForPromise ->
editSession.setSelectedBufferRange([[1,1], [2,2]])
interpreter.eval(',.', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [2,2]]
describe "with multiple selections", ->
it "maintains the current selections", ->
preSelections = editSession.getSelections()
expect(preSelections.length).toBe 3
[preRange1, preRange2, preRange3] = preSelections.map (s) -> s.getScreenRange()
waitsForPromise -> interpreter.eval('.', editSession)
runs ->
selections = editSession.getSelections()
expect(selections.length).toBe 3
[selection1, selection2, selection3] = selections
expect(selection1.getScreenRange()).toEqual preRange1
expect(selection2.getScreenRange()).toEqual preRange2
expect(selection3.getScreenRange()).toEqual preRange3
describe "/regex/", ->
beforeEach ->
editSession.clearSelections()
it 'selects text matching regex after current selection', ->
waitsForPromise ->
editSession.setSelectedBufferRange([[4,16], [4,20]])
interpreter.eval('/pivot/', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
it 'does not require the trailing slash', ->
waitsForPromise ->
editSession.setSelectedBufferRange([[4,16], [4,20]])
interpreter.eval('/pivot', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
it "searches from the end of each selection in the buffer", ->
waitsForPromise ->
editSession.clearSelections()
editSession.setSelectedBufferRange([[4,16], [4,20]])
editSession.addSelectionForBufferRange([[1,16], [2,20]])
expect(editSession.getSelections().length).toBe 2
interpreter.eval('/pivot', editSession)
runs ->
selections = editSession.getSelections()
expect(selections.length).toBe 2
expect(selections[0].getBufferRange()).toEqual [[3,8], [3,13]]
expect(selections[1].getBufferRange()).toEqual [[6,16], [6,21]]
it "wraps around to the beginning of the buffer, but doesn't infinitely loop if no matches are found", ->
waitsForPromise ->
editSession.setSelectedBufferRange([[10, 0], [10,3]])
interpreter.eval('/pivot', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[3,8], [3,13]]
waitsForPromise ->
interpreter.eval('/mike tyson', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[3,8], [3,13]]
it "searches in reverse when prefixed with a -", ->
waitsForPromise ->
editSession.setSelectedBufferRange([[6, 16], [6, 22]])
interpreter.eval('-/pivot', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[3,8], [3,13]]
it "removes folds that contain the selections", ->
waitsForPromise ->
editSession.createFold(5, 6)
editSession.createFold(10, 11)
editSession.setSelectedBufferRange([[4,16], [4,20]])
interpreter.eval('/pivot/', editSession)
runs ->
expect(editSession.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
expect(editSession.lineForScreenRow(5).fold).toBeUndefined()
expect(editSession.lineForScreenRow(10).fold).toBeDefined()
it "is case-insentive when the pattern contains no non-escaped uppercase letters (behavior copied from vim)", ->
waitsForPromise ->
interpreter.eval('/array', editSession)
runs ->
expect(interpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/array/i"
waitsForPromise ->
interpreter.eval('/a\\Sray', editSession)
runs ->
expect(interpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/a\\Sray/i"
it "is case-sentive when the pattern contains a non-escaped uppercase letters (behavior copied from vim)", ->
waitsForPromise ->
interpreter.eval('/arRay', editSession)
runs ->
expect(interpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/arRay/"
waitsForPromise ->
interpreter.eval('/Array', editSession)
runs ->
expect(interpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/Array/"
describe "address range", ->
describe "when two addresses are specified", ->
it "selects from the begining of the left address to the end of the right address", ->
waitsForPromise -> interpreter.eval('4,7', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[3, 0], [7, 0]]
describe "when the left address is unspecified", ->
it "selects from the begining of buffer to the end of the right address", ->
waitsForPromise -> interpreter.eval(',7', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[0, 0], [7, 0]]
describe "when the right address is unspecified", ->
it "selects from the begining of left address to the end file", ->
waitsForPromise -> interpreter.eval('4,', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[3, 0], [12, 2]]
describe "when the neither address is specified", ->
it "selects the entire file", ->
waitsForPromise -> interpreter.eval(',', editSession)
runs ->
expect(editSession.getSelections().length).toBe 1
expect(editSession.getSelection().getBufferRange()).toEqual [[0, 0], [12, 2]]
describe "x/regex/", ->
it "sets the current selection to every match of the regex in the current selection", ->
waitsForPromise -> interpreter.eval('6,7 x/current/', editSession)
runs ->
selections = editSession.getSelections()
expect(selections.length).toBe 4
expect(selections[0].getBufferRange()).toEqual [[5,6], [5,13]]
expect(selections[1].getBufferRange()).toEqual [[6,6], [6,13]]
expect(selections[2].getBufferRange()).toEqual [[6,34], [6,41]]
expect(selections[3].getBufferRange()).toEqual [[6,56], [6,63]]
describe "when matching /$/", ->
it "matches the end of each line in the selected region", ->
waitsForPromise -> interpreter.eval('6,8 x/$/', editSession)
runs ->
cursors = editSession.getCursors()
expect(cursors.length).toBe 3
expect(cursors[0].getBufferPosition()).toEqual [5, 30]
expect(cursors[1].getBufferPosition()).toEqual [6, 65]
expect(cursors[2].getBufferPosition()).toEqual [7, 5]
describe "when text is initially selected", ->
it "loops through current selections and selects text matching the regex", ->
waitsForPromise ->
editSession.setSelectedBufferRange [[3,0], [3,62]]
editSession.addSelectionForBufferRange [[6,0], [6,65]]
interpreter.eval('x/current', editSession)
runs ->
selections = editSession.getSelections()
expect(selections.length).toBe 4
expect(selections[0].getBufferRange()).toEqual [[3,31], [3,38]]
expect(selections[1].getBufferRange()).toEqual [[6,6], [6,13]]
expect(selections[2].getBufferRange()).toEqual [[6,34], [6,41]]
expect(selections[3].getBufferRange()).toEqual [[6,56], [6,63]]
describe "when nothing is matched", ->
it "preserves the existing selection", ->
previousSelections = null
waitsForPromise ->
previousSelections = editSession.getSelectedBufferRanges()
interpreter.eval(',x/this will match nothing', editSession)
runs ->
expect(editSession.getSelectedBufferRanges()).toEqual previousSelections
describe "substitution", ->
it "does nothing if there are no matches", ->
waitsForPromise ->
editSession.setSelectedBufferRange([[6, 0], [6, 44]])
interpreter.eval('s/not-in-text/foo/', editSession)
runs ->
expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(current) : right.push(current);'
describe "when not global", ->
describe "when there is a single selection", ->
it "performs a single substitution within the current selection", ->
waitsForPromise ->
editSession.setSelectedBufferRange([[6, 0], [6, 44]])
interpreter.eval('s/current/foo/', editSession)
runs ->
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(current) : right.push(current);'
describe "when there are multiple selections", ->
it "performs a single substitutions within each of the selections", ->
waitsForPromise ->
editSession.setSelectedBufferRange([[5, 0], [5, 20]])
editSession.addSelectionForBufferRange([[6, 0], [6, 44]])
interpreter.eval('s/current/foo/', editSession)
runs ->
expect(buffer.lineForRow(5)).toBe ' foo = items.shift();'
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(current) : right.push(current);'
describe "when global", ->
it "performs a multiple substitutions within the current selection", ->
waitsForPromise ->
editSession.setSelectedBufferRange([[6, 0], [6, 44]])
interpreter.eval('s/current/foo/g', editSession)
runs ->
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(foo) : right.push(current);'
describe "when prefixed with an address", ->
it "only makes substitutions within given lines", ->
waitsForPromise -> interpreter.eval('4,6s/ /!/g', editSession)
runs ->
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;'
expect(buffer.lineForRow(3)).toBe '!!!!var!pivot!=!items.shift(),!current,!left!=![],!right!=![];'
expect(buffer.lineForRow(4)).toBe '!!!!while(items.length!>!0)!{'
expect(buffer.lineForRow(5)).toBe '!!!!!!current!=!items.shift();'
expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(current) : right.push(current);'
describe "when matching $", ->
it "matches the end of each line and avoids infinitely looping on a zero-width match", ->
waitsForPromise -> interpreter.eval(',s/$/!!!/g', editSession)
runs ->
expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {!!!'
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;!!!'
expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(current) : right.push(current);!!!'
expect(buffer.lineForRow(12)).toBe '};!!!'
describe "when matching ^", ->
it "matches the beginning of each line and avoids infinitely looping on a zero-width match", ->
waitsForPromise -> interpreter.eval(',s/^/!!!/g', editSession)
runs ->
expect(buffer.lineForRow(0)).toBe '!!!var quicksort = function () {'
expect(buffer.lineForRow(2)).toBe '!!! if (items.length <= 1) return items;'
expect(buffer.lineForRow(6)).toBe '!!! current < pivot ? left.push(current) : right.push(current);'
expect(buffer.lineForRow(12)).toBe '!!!};'
describe "when there are multiple selections", ->
it "performs a multiple substitutions within each of the selections", ->
waitsForPromise ->
editSession.setSelectedBufferRange([[5, 0], [5, 20]])
editSession.addSelectionForBufferRange([[6, 0], [6, 44]])
interpreter.eval('s/current/foo/g', editSession)
runs ->
expect(buffer.lineForRow(5)).toBe ' foo = items.shift();'
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(foo) : right.push(current);'
describe "when prefixed with an address", ->
it "restores the original selections upon completion if it is the last command", ->
waitsForPromise ->
editSession.setSelectedBufferRanges([[[5, 0], [5, 20]], [[6, 0], [6, 44]]])
interpreter.eval(',s/current/foo/g', editSession)
runs ->
expect(editSession.getSelectedBufferRanges()).toEqual [[[5, 0], [5, 16]], [[6, 0], [6, 36]]]
describe "X x/regex/", ->
it "returns selection operations for all regex matches in all the project's files", ->
editSession.destroy()
project = new Project(fixturesProject.resolve('dir/'))
interpreter = new CommandInterpreter(project)
operations = null
waitsForPromise ->
interpreter.eval("X x/a+/").done (ops) -> operations = ops
runs ->
expect(operations.length).toBeGreaterThan 3
for operation in operations
editSession = project.buildEditSessionForPath(operation.getPath())
editSession.setSelectedBufferRange(operation.execute(editSession))
expect(editSession.getSelectedText()).toMatch /a+/
editSession.destroy()
operation.destroy()
editSession = null

View File

@@ -1,402 +0,0 @@
RootView = require 'root-view'
CommandPanel = require 'command-panel'
_ = require 'underscore'
describe "CommandPanel", ->
[rootView, editor, buffer, commandPanel, project] = []
beforeEach ->
rootView = new RootView
rootView.open(require.resolve 'fixtures/sample.js')
rootView.enableKeymap()
project = rootView.project
editor = rootView.getActiveEditor()
buffer = editor.activeEditSession.buffer
commandPanel = requireExtension('command-panel')
commandPanel.history = []
commandPanel.historyIndex = 0
afterEach ->
rootView.deactivate()
describe "serialization", ->
it "preserves the command panel's mini-editor text, visibility, focus, and history across reloads", ->
rootView.attachToDom()
rootView.trigger 'command-panel:toggle'
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
commandPanel.execute('/test')
expect(commandPanel.history.length).toBe(1)
expect(commandPanel.history[0]).toBe('/test')
expect(commandPanel.historyIndex).toBe(1)
rootView.trigger 'command-panel:toggle'
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
commandPanel.miniEditor.insertText 'abc'
rootView2 = RootView.deserialize(rootView.serialize())
rootView.deactivate()
rootView2.attachToDom()
commandPanel = rootView2.activateExtension(CommandPanel)
expect(rootView2.find('.command-panel')).toExist()
expect(commandPanel.miniEditor.getText()).toBe 'abc'
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
expect(commandPanel.history.length).toBe(1)
expect(commandPanel.history[0]).toBe('/test')
expect(commandPanel.historyIndex).toBe(1)
rootView2.focus()
expect(commandPanel.miniEditor.isFocused).toBeFalsy()
rootView3 = RootView.deserialize(rootView2.serialize())
rootView2.deactivate()
rootView3.attachToDom()
commandPanel = rootView3.activateExtension(CommandPanel)
expect(commandPanel.miniEditor.isFocused).toBeFalsy()
rootView3.deactivate()
it "only retains the configured max serialized history size", ->
rootView.attachToDom()
commandPanel.maxSerializedHistorySize = 2
commandPanel.execute('/test1')
commandPanel.execute('/test2')
commandPanel.execute('/test3')
expect(commandPanel.history.length).toBe(3)
expect(commandPanel.history[0]).toBe('/test1')
expect(commandPanel.history[1]).toBe('/test2')
expect(commandPanel.history[2]).toBe('/test3')
expect(commandPanel.historyIndex).toBe(3)
rootView2 = RootView.deserialize(rootView.serialize())
rootView.deactivate()
rootView2.attachToDom()
commandPanel = rootView2.activateExtension(CommandPanel)
expect(commandPanel.history.length).toBe(2)
expect(commandPanel.history[0]).toBe('/test2')
expect(commandPanel.history[1]).toBe('/test3')
expect(commandPanel.historyIndex).toBe(2)
rootView2.deactivate()
describe "when core:close is triggered on the command panel", ->
it "detaches the command panel", ->
commandPanel.attach()
commandPanel.trigger('core:close')
expect(commandPanel.hasParent()).toBeFalsy()
describe "when command-panel:toggle is triggered on the root view", ->
beforeEach ->
rootView.attachToDom()
describe "when the command panel is visible", ->
beforeEach ->
commandPanel.attach()
describe "when the mini editor is focused", ->
it "closes the command panel", ->
expect(commandPanel.miniEditor.hiddenInput).toMatchSelector ':focus'
rootView.trigger 'command-panel:toggle'
expect(commandPanel.hasParent()).toBeFalsy()
describe "when the mini editor is not focused", ->
it "focuses the mini editor", ->
rootView.focus()
expect(commandPanel.miniEditor.hiddenInput).not.toMatchSelector ':focus'
rootView.trigger 'command-panel:toggle'
expect(commandPanel.hasParent()).toBeTruthy()
expect(commandPanel.miniEditor.hiddenInput).toMatchSelector ':focus'
describe "when the command panel is not visible", ->
it "shows and focuses the command panel", ->
expect(commandPanel.hasParent()).toBeFalsy()
rootView.trigger 'command-panel:toggle'
expect(commandPanel.hasParent()).toBeTruthy()
describe "when command-panel:toggle-preview is triggered on the root view", ->
beforeEach ->
rootView.attachToDom()
describe "when the preview list is/was previously visible", ->
beforeEach ->
rootView.trigger 'command-panel:toggle'
waitsForPromise -> commandPanel.execute('X x/a+/')
describe "when the command panel is visible", ->
beforeEach ->
expect(commandPanel.hasParent()).toBeTruthy()
describe "when the preview list is visible", ->
beforeEach ->
expect(commandPanel.previewList).toBeVisible()
describe "when the preview list is focused", ->
it "hides the command panel", ->
expect(commandPanel.previewList).toMatchSelector(':focus')
rootView.trigger 'command-panel:toggle-preview'
expect(commandPanel.hasParent()).toBeFalsy()
describe "when the preview list is not focused", ->
it "focuses the preview list", ->
commandPanel.miniEditor.focus()
rootView.trigger 'command-panel:toggle-preview'
expect(commandPanel.previewList).toMatchSelector(':focus')
describe "when the preview list is not visible", ->
beforeEach ->
commandPanel.miniEditor.focus()
rootView.trigger 'command-panel:toggle'
rootView.trigger 'command-panel:toggle'
expect(commandPanel.hasParent()).toBeTruthy()
expect(commandPanel.previewList).toBeHidden()
it "shows and focuses the preview list", ->
rootView.trigger 'command-panel:toggle-preview'
expect(commandPanel.previewList).toBeVisible()
expect(commandPanel.previewList).toMatchSelector(':focus')
describe "when the command panel is not visible", ->
it "shows the command panel and the preview list, and focuses the preview list", ->
commandPanel.miniEditor.focus()
rootView.trigger 'command-panel:toggle'
expect(commandPanel.hasParent()).toBeFalsy()
rootView.trigger 'command-panel:toggle-preview'
expect(commandPanel.hasParent()).toBeTruthy()
expect(commandPanel.previewList).toBeVisible()
expect(commandPanel.previewList).toMatchSelector(':focus')
describe "when the preview list has never been opened", ->
describe "when the command panel is visible", ->
beforeEach ->
rootView.trigger 'command-panel:toggle'
expect(commandPanel.hasParent()).toBeTruthy()
describe "when the mini editor is focused", ->
it "retains focus on the mini editor and does not show the preview list", ->
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
rootView.trigger 'command-panel:toggle-preview'
expect(commandPanel.previewList).toBeHidden()
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
describe "when the mini editor is not focused", ->
it "focuses the mini editor and does not show the preview list", ->
rootView.focus()
rootView.trigger 'command-panel:toggle-preview'
expect(commandPanel.previewList).toBeHidden()
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
describe "when the command panel is not visible", ->
it "shows the command panel and focuses the mini editor, but does not show the preview list", ->
describe "when tool-pane:unfocus is triggered on the command panel", ->
it "returns focus to the root view but does not hide the command panel", ->
rootView.attachToDom()
commandPanel.attach()
expect(commandPanel.miniEditor.hiddenInput).toMatchSelector ':focus'
commandPanel.trigger 'tool-pane:unfocus'
expect(commandPanel.hasParent()).toBeTruthy()
expect(commandPanel.miniEditor.hiddenInput).not.toMatchSelector ':focus'
describe "when command-panel:repeat-relative-address is triggered on the root view", ->
it "repeats the last search command if there is one", ->
rootView.trigger 'command-panel:repeat-relative-address'
editor.setCursorScreenPosition([4, 0])
commandPanel.execute("/current")
expect(editor.getSelection().getBufferRange()).toEqual [[5,6], [5,13]]
rootView.trigger 'command-panel:repeat-relative-address'
expect(editor.getSelection().getBufferRange()).toEqual [[6,6], [6,13]]
commandPanel.execute('s/r/R/g')
rootView.trigger 'command-panel:repeat-relative-address'
expect(editor.getSelection().getBufferRange()).toEqual [[6,34], [6,41]]
commandPanel.execute('0')
commandPanel.execute('/sort/ s/r/R/') # this contains a substitution... won't be repeated
rootView.trigger 'command-panel:repeat-relative-address'
expect(editor.getSelection().getBufferRange()).toEqual [[3,31], [3,38]]
describe "when command-panel:repeat-relative-address-in-reverse is triggered on the root view", ->
it "it repeats the last relative address in the reverse direction", ->
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
editor.setCursorScreenPosition([6, 0])
commandPanel.execute("/current")
expect(editor.getSelection().getBufferRange()).toEqual [[6,6], [6,13]]
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
expect(editor.getSelection().getBufferRange()).toEqual [[5,6], [5,13]]
describe "when command-panel:set-selection-as-regex-address is triggered on the root view", ->
it "sets the @lastRelativeAddress to a RegexAddress of the current selection", ->
rootView.open(require.resolve('fixtures/sample.js'))
rootView.getActiveEditor().setSelectedBufferRange([[1,21],[1,28]])
commandInterpreter = commandPanel.commandInterpreter
expect(commandInterpreter.lastRelativeAddress).toBeUndefined()
rootView.trigger 'command-panel:set-selection-as-regex-address'
expect(commandInterpreter.lastRelativeAddress.subcommands.length).toBe 1
expect(commandInterpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/\\(items\\)/i"
describe "when command-panel:find-in-file is triggered on an editor", ->
it "pre-populates the command panel's editor with / and moves the cursor to the last column", ->
spyOn(commandPanel, 'attach').andCallThrough()
commandPanel.miniEditor.setText("foo")
commandPanel.miniEditor.setCursorBufferPosition([0, 0])
rootView.getActiveEditor().trigger "command-panel:find-in-file"
expect(commandPanel.attach).toHaveBeenCalled()
expect(commandPanel.parent).not.toBeEmpty()
expect(commandPanel.miniEditor.getText()).toBe "/"
expect(commandPanel.miniEditor.getCursorBufferPosition()).toEqual [0, 1]
describe "when command-panel:find-in-project is triggered on the root view", ->
it "pre-populates the command panel's editor with Xx/ and moves the cursor to the last column", ->
spyOn(commandPanel, 'attach').andCallThrough()
commandPanel.miniEditor.setText("foo")
commandPanel.miniEditor.setCursorBufferPosition([0, 0])
rootView.trigger "command-panel:find-in-project"
expect(commandPanel.attach).toHaveBeenCalled()
expect(commandPanel.parent).not.toBeEmpty()
expect(commandPanel.miniEditor.getText()).toBe "Xx/"
expect(commandPanel.miniEditor.getCursorBufferPosition()).toEqual [0, 3]
describe "when return is pressed on the panel's editor", ->
describe "if the command has an immediate effect", ->
it "executes it immediately on the current buffer", ->
rootView.trigger 'command-panel:toggle'
commandPanel.miniEditor.insertText ',s/sort/torta/g'
commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter')
expect(buffer.lineForRow(0)).toMatch /quicktorta/
expect(buffer.lineForRow(1)).toMatch /var torta/
describe "when the command returns operations to be previewed", ->
beforeEach ->
rootView.attachToDom()
editor.remove()
rootView.trigger 'command-panel:toggle'
waitsForPromise -> commandPanel.execute('X x/a+/')
it "displays and focuses the operation preview list", ->
expect(commandPanel).toBeVisible()
expect(commandPanel.previewList).toBeVisible()
expect(commandPanel.previewList).toMatchSelector ':focus'
previewItem = commandPanel.previewList.find("li:contains(dir/a):first")
expect(previewItem.find('.path').text()).toBe "dir/a"
expect(previewItem.find('.preview').text()).toBe "aaa bbb"
expect(previewItem.find('.preview > .match').text()).toBe "aaa"
rootView.trigger 'command-panel:toggle-preview' # ensure we can close panel without problems
expect(commandPanel).toBeHidden()
it "destroys previously previewed operations if there are any", ->
waitsForPromise -> commandPanel.execute('X x/b+/')
# there shouldn't be any dangling operations after this
describe "if the command is malformed", ->
it "adds and removes an error class to the command panel and does not close it", ->
rootView.trigger 'command-panel:toggle'
commandPanel.miniEditor.insertText 'garbage-command!!'
commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter')
expect(commandPanel.parent()).toExist()
expect(commandPanel).toHaveClass 'error'
advanceClock 400
expect(commandPanel).not.toHaveClass 'error'
describe "when move-up and move-down are triggerred on the editor", ->
it "navigates forward and backward through the command history", ->
commandPanel.execute 's/war/peace/g'
commandPanel.execute 's/twinkies/wheatgrass/g'
rootView.trigger 'command-panel:toggle'
commandPanel.miniEditor.trigger 'core:move-up'
expect(commandPanel.miniEditor.getText()).toBe 's/twinkies/wheatgrass/g'
commandPanel.miniEditor.trigger 'core:move-up'
expect(commandPanel.miniEditor.getText()).toBe 's/war/peace/g'
commandPanel.miniEditor.trigger 'core:move-up'
expect(commandPanel.miniEditor.getText()).toBe 's/war/peace/g'
commandPanel.miniEditor.trigger 'core:move-down'
expect(commandPanel.miniEditor.getText()).toBe 's/twinkies/wheatgrass/g'
commandPanel.miniEditor.trigger 'core:move-down'
expect(commandPanel.miniEditor.getText()).toBe ''
describe "when the preview list is focused with search operations", ->
previewList = null
beforeEach ->
previewList = commandPanel.previewList
rootView.trigger 'command-panel:toggle'
waitsForPromise -> commandPanel.execute('X x/a+/')
describe "when move-down and move-up are triggered on the preview list", ->
it "selects the next/previous operation (if there is one), and scrolls the list if needed", ->
rootView.attachToDom()
expect(previewList.find('li:eq(0)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
previewList.trigger 'core:move-up'
expect(previewList.find('li:eq(0)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
previewList.trigger 'core:move-down'
expect(previewList.find('li:eq(1)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[1]
previewList.trigger 'core:move-down'
expect(previewList.find('li:eq(2)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[2]
previewList.trigger 'core:move-up'
expect(previewList.find('li:eq(1)')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[1]
_.times previewList.getOperations().length, -> previewList.trigger 'core:move-down'
expect(previewList.find('li:last')).toHaveClass 'selected'
expect(previewList.getSelectedOperation()).toBe _.last(previewList.getOperations())
expect(previewList.scrollBottom()).toBeCloseTo previewList.prop('scrollHeight'), -1
_.times previewList.getOperations().length, -> previewList.trigger 'core:move-up'
describe "when core:confirm is triggered on the preview list", ->
it "opens the operation's buffer, selects the search result, and focuses the active editor", ->
spyOn(rootView, 'focus')
executeHandler = jasmine.createSpy('executeHandler')
commandPanel.on 'core:confirm', executeHandler
_.times 4, -> previewList.trigger 'core:move-down'
operation = previewList.getSelectedOperation()
previewList.trigger 'core:confirm'
editSession = rootView.getActiveEditSession()
expect(editSession.buffer.getPath()).toBe project.resolve(operation.getPath())
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
expect(rootView.focus).toHaveBeenCalled()
expect(executeHandler).not.toHaveBeenCalled()
describe "when an operation in the preview list is clicked", ->
it "opens the operation's buffer, selects the search result, and focuses the active editor", ->
spyOn(rootView, 'focus')
operation = previewList.getOperations()[4]
previewList.find('li:eq(4) span').mousedown()
expect(previewList.getSelectedOperation()).toBe operation
editSession = rootView.getActiveEditSession()
expect(editSession.buffer.getPath()).toBe project.resolve(operation.getPath())
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
expect(rootView.focus).toHaveBeenCalled()

View File

@@ -1,68 +0,0 @@
RootView = require 'root-view'
EventPalette = require 'event-palette'
$ = require 'jquery'
_ = require 'underscore'
describe "EventPalette", ->
[rootView, palette] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
rootView.activateExtension(EventPalette)
palette = EventPalette.instance
rootView.attachToDom().focus()
rootView.trigger 'event-palette:toggle'
afterEach ->
rootView.remove()
describe "when event-palette:toggle is triggered on the root view", ->
it "shows a list of all valid event descriptions, names, and keybindings for the previously focused element", ->
keyBindings = _.losslessInvert(keymap.bindingsForElement(rootView.getActiveEditor()))
for eventName, description of rootView.getActiveEditor().events()
eventLi = palette.list.children("[data-event-name='#{eventName}']")
if description
expect(eventLi).toExist()
expect(eventLi.find('.event-name')).toHaveText(eventName)
expect(eventLi.find('.event-description')).toHaveText(description)
for binding in keyBindings[eventName] ? []
expect(eventLi.find(".key-binding:contains(#{binding})")).toExist()
else
expect(eventLi).not.toExist()
it "focuses the mini-editor and selects the first event", ->
expect(palette.miniEditor.isFocused).toBeTruthy()
expect(palette.find('.event:first')).toHaveClass 'selected'
it "clears the previous mini editor text", ->
palette.miniEditor.setText('hello')
palette.trigger 'event-palette:toggle'
rootView.trigger 'event-palette:toggle'
expect(palette.miniEditor.getText()).toBe ''
describe "when event-palette:toggle is triggered on the open event palette", ->
it "focus the root view and detaches the event palette", ->
expect(palette.hasParent()).toBeTruthy()
palette.trigger 'event-palette:toggle'
expect(palette.hasParent()).toBeFalsy()
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "when the event palette is cancelled", ->
it "focuses the root view and detaches the event palette", ->
expect(palette.hasParent()).toBeTruthy()
palette.cancel()
expect(palette.hasParent()).toBeFalsy()
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "when an event selection is confirmed", ->
it "detaches the palette, then focuses the previously focused element and emits the selected event on it", ->
eventHandler = jasmine.createSpy 'eventHandler'
activeEditor = rootView.getActiveEditor()
{eventName} = palette.array[5]
activeEditor.preempt eventName, eventHandler
palette.confirmed(palette.array[5])
expect(activeEditor.isFocused).toBeTruthy()
expect(eventHandler).toHaveBeenCalled()
expect(palette.hasParent()).toBeFalsy()

View File

@@ -1,179 +0,0 @@
RootView = require 'root-view'
FuzzyFinder = require 'fuzzy-finder'
$ = require 'jquery'
{$$} = require 'space-pen'
describe 'FuzzyFinder', ->
[rootView, finder] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
rootView.enableKeymap()
rootView.activateExtension(FuzzyFinder)
finder = FuzzyFinder.instance
afterEach ->
rootView.remove()
describe "file-finder behavior", ->
describe "toggling", ->
describe "when the root view's project has a path", ->
it "shows the FuzzyFinder or hides it and returns focus to the active editor if it already showing", ->
rootView.attachToDom()
expect(rootView.find('.fuzzy-finder')).not.toExist()
rootView.find('.editor').trigger 'editor:split-right'
[editor1, editor2] = rootView.find('.editor').map -> $(this).view()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
expect(rootView.find('.fuzzy-finder')).toExist()
expect(finder.miniEditor.isFocused).toBeTruthy()
expect(editor1.isFocused).toBeFalsy()
expect(editor2.isFocused).toBeFalsy()
finder.miniEditor.insertText('this should not show up next time we toggle')
rootView.trigger 'fuzzy-finder:toggle-file-finder'
expect(editor1.isFocused).toBeFalsy()
expect(editor2.isFocused).toBeTruthy()
expect(rootView.find('.fuzzy-finder')).not.toExist()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
expect(finder.miniEditor.getText()).toBe ''
it "shows all relative file paths for the current project and selects the first", ->
finder.maxItems = Infinity
rootView.trigger 'fuzzy-finder:toggle-file-finder'
paths = rootView.project.getFilePaths()
expect(finder.list.children('li').length).toBe paths.length, finder.maxResults
for path in paths
expect(finder.list.find("li:contains(#{path})")).toExist()
expect(finder.list.children().first()).toHaveClass 'selected'
describe "when root view's project has no path", ->
beforeEach ->
rootView.project.setPath(null)
it "does not open the FuzzyFinder", ->
expect(rootView.find('.fuzzy-finder')).not.toExist()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
expect(rootView.find('.fuzzy-finder')).not.toExist()
describe "when a path selection is confirmed", ->
it "opens the file associated with that path in the editor", ->
rootView.attachToDom()
editor1 = rootView.getActiveEditor()
editor2 = editor1.splitRight()
expect(rootView.getActiveEditor()).toBe editor2
rootView.trigger 'fuzzy-finder:toggle-file-finder'
finder.confirmed('dir/a')
expectedPath = fixturesProject.resolve('dir/a')
expect(finder.hasParent()).toBeFalsy()
expect(editor1.getPath()).not.toBe expectedPath
expect(editor2.getPath()).toBe expectedPath
expect(editor2.isFocused).toBeTruthy()
describe "buffer-finder behavior", ->
describe "toggling", ->
describe "when the active editor contains edit sessions for buffers with paths", ->
beforeEach ->
rootView.open('sample.txt')
it "shows the FuzzyFinder or hides it, returning focus to the active editor if", ->
rootView.attachToDom()
expect(rootView.find('.fuzzy-finder')).not.toExist()
rootView.find('.editor').trigger 'editor:split-right'
[editor1, editor2] = rootView.find('.editor').map -> $(this).view()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(rootView.find('.fuzzy-finder')).toExist()
expect(rootView.find('.fuzzy-finder input:focus')).toExist()
finder.miniEditor.insertText('this should not show up next time we toggle')
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(editor1.isFocused).toBeFalsy()
expect(editor2.isFocused).toBeTruthy()
expect(rootView.find('.fuzzy-finder')).not.toExist()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(finder.miniEditor.getText()).toBe ''
it "lists the paths of the current open buffers", ->
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(finder.list.children('li').length).toBe 2
expect(finder.list.find("li:contains(sample.js)")).toExist()
expect(finder.list.find("li:contains(sample.txt)")).toExist()
expect(finder.list.children().first()).toHaveClass 'selected'
describe "when the active editor only contains edit sessions for anonymous buffers", ->
it "does not open", ->
editor = rootView.getActiveEditor()
editor.edit(rootView.project.buildEditSessionForPath())
editor.loadPreviousEditSession()
editor.destroyActiveEditSession()
expect(editor.getOpenBufferPaths().length).toBe 0
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(rootView.find('.fuzzy-finder')).not.toExist()
describe "when there is no active editor", ->
it "does not open", ->
rootView.getActiveEditor().destroyActiveEditSession()
expect(rootView.getActiveEditor()).toBeUndefined()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(rootView.find('.fuzzy-finder')).not.toExist()
describe "when a path selection is confirmed", ->
[editor1, editor2] = []
beforeEach ->
rootView.attachToDom()
editor1 = rootView.getActiveEditor()
editor2 = editor1.splitRight()
expect(rootView.getActiveEditor()).toBe editor2
rootView.open('sample.txt')
editor2.loadPreviousEditSession()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
describe "when there is an edit session for the confirmed path in the active editor", ->
it "switches the active editor to the edit session for the selected path", ->
expectedPath = fixturesProject.resolve('sample.txt')
finder.confirmed('sample.txt')
expect(finder.hasParent()).toBeFalsy()
expect(editor1.getPath()).not.toBe expectedPath
expect(editor2.getPath()).toBe expectedPath
expect(editor2.isFocused).toBeTruthy()
describe "when there is NO edit session for the confirmed path on the active editor, but there is one on another editor", ->
it "focuses the editor that contains an edit session for the selected path", ->
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
editor1.focus()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(rootView.getActiveEditor()).toBe editor1
expectedPath = fixturesProject.resolve('sample.txt')
finder.confirmed('sample.txt')
expect(finder.hasParent()).toBeFalsy()
expect(editor1.getPath()).not.toBe expectedPath
expect(editor2.getPath()).toBe expectedPath
expect(editor2.isFocused).toBeTruthy()
describe "common behavior between file and buffer finder", ->
describe "when the fuzzy finder is cancelled", ->
it "detaches the finder and focuses the previously focused element", ->
rootView.attachToDom()
activeEditor = rootView.getActiveEditor()
activeEditor.focus()
rootView.trigger 'fuzzy-finder:toggle-file-finder'
expect(finder.hasParent()).toBeTruthy()
expect(activeEditor.isFocused).toBeFalsy()
expect(finder.miniEditor.isFocused).toBeTruthy()
finder.cancel()
expect(finder.hasParent()).toBeFalsy()
expect(activeEditor.isFocused).toBeTruthy()
expect(finder.miniEditor.isFocused).toBeFalsy()

View File

@@ -1,60 +0,0 @@
$ = require 'jquery'
RootView = require 'root-view'
MarkdownPreview = require 'markdown-preview'
describe "MarkdownPreview", ->
[rootView, markdownPreview] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/markdown'))
rootView.activateExtension(MarkdownPreview)
markdownPreview = MarkdownPreview.instance
rootView.attachToDom()
afterEach ->
rootView.deactivate()
describe "markdown-preview:toggle event", ->
it "toggles on/off a preview for a .md file", ->
rootView.open('file.md')
editor = rootView.getActiveEditor()
expect(rootView.find('.markdown-preview')).not.toExist()
spyOn(markdownPreview, 'loadHtml')
editor.trigger('markdown-preview:toggle')
markdownPreviewView = rootView.find('.markdown-preview')?.view()
expect(rootView.find('.markdown-preview')).toExist()
expect(markdownPreview.loadHtml).toHaveBeenCalled();
markdownPreviewView.trigger('markdown-preview:toggle')
expect(rootView.find('.markdown-preview')).not.toExist()
it "displays a preview for a .markdown file", ->
rootView.open('file.markdown')
editor = rootView.getActiveEditor()
expect(rootView.find('.markdown-preview')).not.toExist()
spyOn(markdownPreview, 'loadHtml')
editor.trigger('markdown-preview:toggle')
expect(rootView.find('.markdown-preview')).toExist()
expect(markdownPreview.loadHtml).toHaveBeenCalled();
it "does not display a preview for non-markdown file", ->
rootView.open('file.js')
editor = rootView.getActiveEditor()
expect(rootView.find('.markdown-preview')).not.toExist()
spyOn(markdownPreview, 'loadHtml')
editor.trigger('markdown-preview:toggle')
expect(rootView.find('.markdown-preview')).not.toExist()
expect(markdownPreview.loadHtml).not.toHaveBeenCalled();
describe "core:cancel event", ->
it "removes markdown preview", ->
rootView.open('file.md')
editor = rootView.getActiveEditor()
expect(rootView.find('.markdown-preview')).not.toExist()
spyOn(markdownPreview, 'loadHtml')
editor.trigger('markdown-preview:toggle')
markdownPreviewView = rootView.find('.markdown-preview')?.view()
expect(markdownPreviewView).toExist()
markdownPreviewView.trigger('core:cancel')
expect(rootView.find('.markdown-preview')).not.toExist()

View File

@@ -1,123 +0,0 @@
SelectList = require 'select-list'
{$$} = require 'space-pen'
$ = require 'jquery'
describe "SelectList", ->
[selectList, array, list, miniEditor] = []
beforeEach ->
array = [
["A", "Alpha"], ["B", "Bravo"], ["C", "Charlie"],
["D", "Delta"], ["E", "Echo"], ["F", "Foxtrot"]
]
selectList = new SelectList
selectList.maxItems = 4
selectList.filterKey = 1
selectList.itemForElement = (element) ->
$$ -> @li element[1], class: element[0]
selectList.confirmed = jasmine.createSpy('confirmed hook')
selectList.cancelled = jasmine.createSpy('cancelled hook')
selectList.setArray(array)
{list, miniEditor} = selectList
describe "when an array is assigned", ->
it "populates the list with up to maxItems items, based on the liForElement function", ->
expect(list.find('li').length).toBe selectList.maxItems
expect(list.find('li:eq(0)')).toHaveText 'Alpha'
expect(list.find('li:eq(0)')).toHaveClass 'A'
describe "when the text of the mini editor changes", ->
it "filters the elements in the list based on the scoreElement function and selects the first item", ->
miniEditor.insertText('la')
expect(list.find('li').length).toBe 2
expect(list.find('li:contains(Alpha)')).toExist()
expect(list.find('li:contains(Delta)')).toExist()
expect(list.find('li:first')).toHaveClass 'selected'
describe "when core:move-up / core:move-down are triggered on the miniEditor", ->
it "selects the previous / next item in the list, or wraps around to the other side", ->
expect(list.find('li:first')).toHaveClass 'selected'
miniEditor.trigger 'core:move-up'
expect(list.find('li:first')).not.toHaveClass 'selected'
expect(list.find('li:last')).toHaveClass 'selected'
miniEditor.trigger 'core:move-down'
expect(list.find('li:first')).toHaveClass 'selected'
expect(list.find('li:last')).not.toHaveClass 'selected'
miniEditor.trigger 'core:move-down'
expect(list.find('li:eq(0)')).not.toHaveClass 'selected'
expect(list.find('li:eq(1)')).toHaveClass 'selected'
miniEditor.trigger 'core:move-down'
expect(list.find('li:eq(1)')).not.toHaveClass 'selected'
expect(list.find('li:eq(2)')).toHaveClass 'selected'
miniEditor.trigger 'core:move-up'
expect(list.find('li:eq(2)')).not.toHaveClass 'selected'
expect(list.find('li:eq(1)')).toHaveClass 'selected'
it "scrolls to keep the selected item in view", ->
selectList.attachToDom()
itemHeight = list.find('li').outerHeight()
list.height(itemHeight * 2)
miniEditor.trigger 'core:move-down'
miniEditor.trigger 'core:move-down'
expect(list.scrollBottom()).toBe itemHeight * 3
miniEditor.trigger 'core:move-down'
expect(list.scrollBottom()).toBe itemHeight * 4
miniEditor.trigger 'core:move-up'
miniEditor.trigger 'core:move-up'
expect(list.scrollBottom()).toBe itemHeight * 3
describe "the core:confirm event", ->
describe "when there is an item selected (because the list in not empty)", ->
it "triggers the selected hook with the selected array element", ->
miniEditor.trigger 'core:move-down'
miniEditor.trigger 'core:move-down'
miniEditor.trigger 'core:confirm'
expect(selectList.confirmed).toHaveBeenCalledWith(array[2])
describe "when there is no item selected (because the list is empty)", ->
it "does not trigger the confirmed hook", ->
miniEditor.insertText("i will never match anything")
expect(list.find('li')).not.toExist()
miniEditor.trigger 'core:confirm'
expect(selectList.confirmed).not.toHaveBeenCalled()
describe "when a list item is clicked", ->
it "selects the item on mousedown and confirms it on mouseup", ->
item = list.find('li:eq(1)')
item.mousedown()
expect(item).toHaveClass 'selected'
item.mouseup()
expect(selectList.confirmed).toHaveBeenCalledWith(array[1])
describe "the core:cancel event", ->
it "triggers the cancelled hook and detaches the select list", ->
spyOn(selectList, 'detach')
miniEditor.trigger 'core:cancel'
expect(selectList.cancelled).toHaveBeenCalled()
expect(selectList.detach).toHaveBeenCalled()
describe "when the mini editor loses focus", ->
it "triggers the cancelled hook and detaches the select list", ->
spyOn(selectList, 'detach')
miniEditor.trigger 'focusout'
expect(selectList.cancelled).toHaveBeenCalled()
expect(selectList.detach).toHaveBeenCalled()

View File

@@ -1,242 +0,0 @@
Snippets = require 'snippets'
RootView = require 'root-view'
Buffer = require 'buffer'
Editor = require 'editor'
_ = require 'underscore'
fs = require 'fs'
describe "Snippets extension", ->
[buffer, editor] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
requireExtension("snippets")
editor = rootView.getActiveEditor()
buffer = editor.getBuffer()
rootView.simulateDomAttachment()
rootView.enableKeymap()
afterEach ->
rootView.remove()
describe "when 'tab' is triggered on the editor", ->
beforeEach ->
Snippets.evalSnippets 'js', """
snippet t1 "Snippet without tab stops"
this is a test
endsnippet
snippet t2 "With tab stops"
go here next:($2) and finally go here:($3)
go here first:($1)
endsnippet
snippet t3 "With indented second line"
line 1
line 2$1
endsnippet
snippet t4 "With tab stop placeholders"
go here ${1:first} and then here ${2:second}
endsnippet
snippet t5 "Caused problems with undo"
first line$1
${2:placeholder ending second line}
endsnippet
"""
describe "when the letters preceding the cursor trigger a snippet", ->
describe "when the snippet contains no tab stops", ->
it "replaces the prefix with the snippet text and places the cursor at its end", ->
editor.insertText("t1")
expect(editor.getCursorScreenPosition()).toEqual [0, 2]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe "this is a testvar quicksort = function () {"
expect(editor.getCursorScreenPosition()).toEqual [0, 14]
describe "when the snippet contains tab stops", ->
it "places the cursor at the first tab-stop, and moves the cursor in response to 'next-tab-stop' events", ->
anchorCountBefore = editor.activeEditSession.getAnchors().length
editor.setCursorScreenPosition([2, 0])
editor.insertText('t2')
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(2)).toBe "go here next:() and finally go here:()"
expect(buffer.lineForRow(3)).toBe "go here first:()"
expect(buffer.lineForRow(4)).toBe " if (items.length <= 1) return items;"
expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]]
editor.trigger keydownEvent('tab', target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 14]]
editor.insertText 'abc'
editor.trigger keydownEvent('tab', target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[2, 40], [2, 40]]
# tab backwards
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 17]] # should highlight text typed at tab stop
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]]
# shift-tab on first tab-stop does nothing
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
expect(editor.getCursorScreenPosition()).toEqual [3, 15]
# tab through all tab stops, then tab on last stop to terminate snippet
editor.trigger keydownEvent('tab', target: editor[0])
editor.trigger keydownEvent('tab', target: editor[0])
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(2)).toBe "go here next:(abc) and finally go here:( )"
expect(editor.activeEditSession.getAnchors().length).toBe anchorCountBefore
describe "when the tab stops have placeholder text", ->
it "auto-fills the placeholder text and highlights it when navigating to that tab stop", ->
editor.insertText 't4'
editor.trigger 'snippets:expand'
expect(buffer.lineForRow(0)).toBe 'go here first and then here second'
expect(editor.getSelectedBufferRange()).toEqual [[0, 8], [0, 13]]
describe "when the cursor is moved beyond the bounds of a tab stop", ->
it "terminates the snippet on the next tab", ->
editor.setCursorScreenPosition([2, 0])
editor.insertText('t2')
editor.trigger keydownEvent('tab', target: editor[0])
editor.moveCursorRight()
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(3)).toBe "go here first:() "
expect(editor.getCursorBufferPosition()).toEqual [3, 18]
# test we can terminate with shift-tab
editor.setCursorScreenPosition([4, 0])
editor.insertText('t2')
editor.trigger keydownEvent('tab', target: editor[0])
editor.trigger keydownEvent('tab', target: editor[0])
editor.moveCursorRight()
editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0])
expect(editor.getCursorBufferPosition()).toEqual [4, 15]
describe "when a the start of the snippet is indented", ->
describe "when the snippet spans a single line", ->
it "does not indent the next line", ->
editor.setCursorScreenPosition([2, Infinity])
editor.insertText ' t1'
editor.trigger 'snippets:expand'
expect(buffer.lineForRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
describe "when the snippet spans multiple lines", ->
it "indents the subsequent lines of the snippet to be even with the start of the first line", ->
editor.setCursorScreenPosition([2, Infinity])
editor.insertText ' t3'
editor.trigger 'snippets:expand'
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items; line 1"
expect(buffer.lineForRow(3)).toBe " line 2"
expect(editor.getCursorBufferPosition()).toEqual [3, 12]
describe "when the letters preceding the cursor don't match a snippet", ->
it "inserts a tab as normal", ->
editor.insertText("xte")
expect(editor.getCursorScreenPosition()).toEqual [0, 3]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe "xte var quicksort = function () {"
expect(editor.getCursorScreenPosition()).toEqual [0, 5]
describe "when a previous snippet expansion has just been undone", ->
it "expands the snippet based on the current prefix rather than jumping to the old snippet's tab stop", ->
editor.insertText 't5\n'
editor.setCursorBufferPosition [0, 2]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe "first line"
editor.undo()
expect(buffer.lineForRow(0)).toBe "t5"
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe "first line"
describe "when a snippet expansion is undone and redone", ->
it "recreates the snippet's tab stops", ->
editor.insertText ' t5\n'
editor.setCursorBufferPosition [0, 6]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe " first line"
editor.undo()
editor.redo()
expect(editor.getCursorBufferPosition()).toEqual [0, 14]
editor.trigger keydownEvent('tab', target: editor[0])
expect(editor.getSelectedBufferRange()).toEqual [[1, 6], [1, 36]]
it "restores tabs stops in active edit session even when the initial expansion was in a different edit session", ->
anotherEditor = editor.splitRight()
editor.insertText ' t5\n'
editor.setCursorBufferPosition [0, 6]
editor.trigger keydownEvent('tab', target: editor[0])
expect(buffer.lineForRow(0)).toBe " first line"
editor.undo()
anotherEditor.redo()
expect(anotherEditor.getCursorBufferPosition()).toEqual [0, 14]
anotherEditor.trigger keydownEvent('tab', target: anotherEditor[0])
expect(anotherEditor.getSelectedBufferRange()).toEqual [[1, 6], [1, 36]]
describe ".loadSnippetsFile(path)", ->
it "loads the snippets in the given file", ->
spyOn(fs, 'read').andReturn """
snippet t1 "Test snippet 1"
this is a test 1
endsnippet
"""
Snippets.loadSnippetsFile('/tmp/foo/js.snippets')
expect(fs.read).toHaveBeenCalledWith('/tmp/foo/js.snippets')
editor.insertText("t1")
editor.trigger 'snippets:expand'
expect(buffer.lineForRow(0)).toBe "this is a test 1var quicksort = function () {"
describe "Snippets parser", ->
it "can parse multiple snippets", ->
snippets = Snippets.snippetsParser.parse """
snippet t1 "Test snippet 1"
this is a test 1
endsnippet
snippet t2 "Test snippet 2"
this is a test 2
endsnippet
"""
expect(_.keys(snippets).length).toBe 2
snippet = snippets['t1']
expect(snippet.prefix).toBe 't1'
expect(snippet.description).toBe "Test snippet 1"
expect(snippet.body).toBe "this is a test 1"
snippet = snippets['t2']
expect(snippet.prefix).toBe 't2'
expect(snippet.description).toBe "Test snippet 2"
expect(snippet.body).toBe "this is a test 2"
it "can parse snippets with tabstops", ->
snippets = Snippets.snippetsParser.parse """
# this line intentially left blank.
snippet t1 "Snippet with tab stops"
go here next:($2) and finally go here:($3)
go here first:($1)
endsnippet
"""
snippet = snippets['t1']
expect(snippet.body).toBe """
go here next:() and finally go here:()
go here first:()
"""
expect(snippet.tabStops).toEqual [[[1, 15], [1, 15]], [[0, 14], [0, 14]], [[0, 37], [0, 37]]]

View File

@@ -1,35 +0,0 @@
StripTrailingWhitespace = require 'strip-trailing-whitespace'
RootView = require 'root-view'
fs = require 'fs'
describe "StripTrailingWhitespace", ->
[rootView, editor, path] = []
beforeEach ->
path = "/tmp/atom-whitespace.txt"
fs.write(path, "")
rootView = new RootView(path)
StripTrailingWhitespace.activate(rootView)
rootView.focus()
editor = rootView.getActiveEditor()
afterEach ->
fs.remove(path) if fs.exists(path)
rootView.remove()
it "strips trailing whitespace before an editor saves a buffer", ->
spyOn(fs, 'write')
# works for buffers that are already open when extension is initialized
editor.insertText("foo \nbar\t \n\nbaz")
editor.save()
expect(editor.getText()).toBe "foo\nbar\n\nbaz"
# works for buffers that are opened after extension is initialized
rootView.open(require.resolve('fixtures/sample.txt'))
editor.moveCursorToEndOfLine()
editor.insertText(" ")
editor.getBuffer().save()
expect(editor.getText()).toBe 'Some text.\n'

View File

@@ -1,835 +0,0 @@
$ = require 'jquery'
_ = require 'underscore'
TreeView = require 'tree-view'
RootView = require 'root-view'
Directory = require 'directory'
Native = require 'native'
fs = require 'fs'
describe "TreeView", ->
[rootView, project, treeView, sampleJs, sampleTxt] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/tree-view'))
project = rootView.project
rootView.activateExtension(TreeView)
treeView = rootView.find(".tree-view").view()
treeView.root = treeView.find('> li:first').view()
sampleJs = treeView.find('.file:contains(tree-view.js)')
sampleTxt = treeView.find('.file:contains(tree-view.txt)')
expect(treeView.root.directory.subscriptionCount()).toBeGreaterThan 0
afterEach ->
rootView.deactivate()
describe ".initialize(project)", ->
it "renders the root of the project and its contents alphabetically with subdirectories first in a collapsed state", ->
expect(treeView.root.find('> .header .disclosure-arrow')).toHaveText('')
expect(treeView.root.find('> .header .name')).toHaveText('tree-view/')
rootEntries = treeView.root.find('.entries')
subdir0 = rootEntries.find('> li:eq(0)')
expect(subdir0.find('.disclosure-arrow')).toHaveText('')
expect(subdir0.find('.name')).toHaveText('dir1/')
expect(subdir0.find('.entries')).not.toExist()
subdir2 = rootEntries.find('> li:eq(1)')
expect(subdir2.find('.disclosure-arrow')).toHaveText('')
expect(subdir2.find('.name')).toHaveText('dir2/')
expect(subdir2.find('.entries')).not.toExist()
expect(rootEntries.find('> .file:contains(tree-view.js)')).toExist()
expect(rootEntries.find('> .file:contains(tree-view.txt)')).toExist()
it "selects the rootview", ->
expect(treeView.selectedEntry()).toEqual treeView.root
describe "when the project has no path", ->
beforeEach ->
rootView.deactivate()
rootView = new RootView
rootView.activateExtension(TreeView)
treeView = rootView.find(".tree-view").view()
it "does not create a root node", ->
expect(treeView.root).not.toExist()
it "serializes without throwing an exception", ->
expect(-> treeView.serialize()).not.toThrow()
it "creates a root view when the project path is created", ->
rootView.open(require.resolve('fixtures/sample.js'))
expect(treeView.root.getPath()).toBe require.resolve('fixtures')
expect(treeView.root.parent()).toMatchSelector(".tree-view")
oldRoot = treeView.root
rootView.project.setPath('/tmp')
expect(treeView.root).not.toEqual oldRoot
expect(oldRoot.hasParent()).toBeFalsy()
describe "when the prototypes deactivate method is called", ->
it "calls the deactivate on tree view instance", ->
spyOn(treeView, "deactivate").andCallThrough()
rootView.deactivateExtension(TreeView)
expect(treeView.deactivate).toHaveBeenCalled()
describe "serialization", ->
[newRootView, newTreeView] = []
afterEach ->
newRootView?.deactivate()
it "restores expanded directories and selected file when deserialized", ->
treeView.find('.directory:contains(dir1)').click()
sampleJs.click()
newRootView = RootView.deserialize(rootView.serialize())
rootView.deactivate() # Deactivates previous TreeView
newRootView.activateExtension(TreeView)
newTreeView = newRootView.find(".tree-view").view()
expect(newTreeView).toExist()
expect(newTreeView.selectedEntry()).toMatchSelector(".file:contains(tree-view.js)")
expect(newTreeView.find(".directory:contains(dir1)")).toHaveClass("expanded")
it "restores the focus state of the tree view", ->
rootView.attachToDom()
treeView.focus()
expect(treeView).toMatchSelector ':focus'
newRootView = RootView.deserialize(rootView.serialize())
rootView.deactivate() # Deactivates previous TreeView
newRootView.attachToDom()
newRootView.activateExtension(TreeView)
newTreeView = newRootView.find(".tree-view").view()
expect(newTreeView).toMatchSelector ':focus'
it "restores the scroll top when toggled", ->
rootView.height(5)
rootView.attachToDom()
expect(treeView).toBeVisible()
treeView.focus()
treeView.scrollTop(10)
expect(treeView.scrollTop()).toBe(10)
rootView.trigger 'tree-view:toggle'
expect(treeView).toBeHidden()
rootView.trigger 'tree-view:toggle'
expect(treeView).toBeVisible()
expect(treeView.scrollTop()).toBe(10)
describe "when tree-view:toggle is triggered on the root view", ->
beforeEach ->
rootView.attachToDom()
describe "when the tree view is visible", ->
beforeEach ->
expect(treeView).toBeVisible()
describe "when the tree view is focused", ->
it "hides the tree view", ->
treeView.focus()
rootView.trigger 'tree-view:toggle'
expect(treeView).toBeHidden()
describe "when the tree view is not focused", ->
it "shifts focus to the tree view", ->
rootView.open() # When we call focus below, we want an editor to become focused
rootView.focus()
rootView.trigger 'tree-view:toggle'
expect(treeView).toBeVisible()
expect(treeView).toMatchSelector(':focus')
describe "when the tree view is hidden", ->
it "shows and focuses the tree view", ->
treeView.detach()
rootView.trigger 'tree-view:toggle'
expect(treeView.hasParent()).toBeTruthy()
expect(treeView).toMatchSelector(':focus')
describe "when tree-view:reveal-current-file is triggered on the root view", ->
beforeEach ->
treeView.detach()
spyOn(treeView, 'focus')
describe "if the current file has a path", ->
it "shows and focuses the tree view and selects the file", ->
rootView.open('dir1/file1')
rootView.trigger 'tree-view:reveal-active-file'
expect(treeView.hasParent()).toBeTruthy()
expect(treeView.focus).toHaveBeenCalled()
expect(treeView.selectedEntry().getPath()).toMatch /dir1\/file1$/
describe "if the current file has no path", ->
it "shows and focuses the tree view, but does not attempt to select a specific file", ->
rootView.open()
expect(rootView.getActiveEditSession().getPath()).toBeUndefined()
rootView.trigger 'tree-view:reveal-active-file'
expect(treeView.hasParent()).toBeTruthy()
expect(treeView.focus).toHaveBeenCalled()
describe "if there is no editor open", ->
it "shows and focuses the tree view, but does not attempt to select a specific file", ->
expect(rootView.getActiveEditSession()).toBeUndefined()
rootView.trigger 'tree-view:reveal-active-file'
expect(treeView.hasParent()).toBeTruthy()
expect(treeView.focus).toHaveBeenCalled()
describe "when tool-panel:unfocus is triggered on the tree view", ->
it "surrenders focus to the root view but remains open", ->
rootView.open() # When we trigger 'tool-panel:unfocus' below, we want an editor to become focused
rootView.attachToDom()
treeView.focus()
expect(treeView).toMatchSelector(':focus')
treeView.trigger 'tool-panel:unfocus'
expect(treeView).toBeVisible()
expect(treeView).not.toMatchSelector(':focus')
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "when a directory's disclosure arrow is clicked", ->
it "expands / collapses the associated directory", ->
subdir = treeView.root.find('.entries > li:contains(dir1/)').view()
expect(subdir.disclosureArrow).toHaveText('')
expect(subdir.find('.entries')).not.toExist()
subdir.disclosureArrow.click()
expect(subdir.disclosureArrow).toHaveText('')
expect(subdir.find('.entries')).toExist()
subdir.disclosureArrow.click()
expect(subdir.disclosureArrow).toHaveText('')
expect(subdir.find('.entries')).not.toExist()
it "restores the expansion state of descendant directories", ->
child = treeView.root.find('.entries > li:contains(dir1/)').view()
child.disclosureArrow.click()
grandchild = child.find('.entries > li:contains(sub-dir1/)').view()
grandchild.disclosureArrow.click()
treeView.root.disclosureArrow.click()
expect(treeView.root.find('.entries')).not.toExist()
treeView.root.disclosureArrow.click()
# previously expanded descendants remain expanded
expect(treeView.root.find('> .entries > li:contains(dir1/) > .entries > li:contains(sub-dir1/) > .entries').length).toBe 1
# collapsed descendants remain collapsed
expect(treeView.root.find('> .entries > li.contains(dir2/) > .entries')).not.toExist()
it "when collapsing a directory, removes change subscriptions from the collapsed directory and its descendants", ->
child = treeView.root.entries.find('li:contains(dir1/)').view()
child.disclosureArrow.click()
grandchild = child.entries.find('li:contains(sub-dir1/)').view()
grandchild.disclosureArrow.click()
expect(treeView.root.directory.subscriptionCount()).toBe 1
expect(child.directory.subscriptionCount()).toBe 1
expect(grandchild.directory.subscriptionCount()).toBe 1
treeView.root.disclosureArrow.click()
expect(treeView.root.directory.subscriptionCount()).toBe 0
expect(child.directory.subscriptionCount()).toBe 0
expect(grandchild.directory.subscriptionCount()).toBe 0
describe "when a file is single-clicked", ->
it "selects the files and opens it in the active editor, without changing focus", ->
expect(rootView.getActiveEditor()).toBeUndefined()
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
expect(sampleJs).toHaveClass 'selected'
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
sampleTxt.trigger clickEvent(originalEvent: { detail: 1 })
expect(sampleTxt).toHaveClass 'selected'
expect(treeView.find('.selected').length).toBe 1
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.txt')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
describe "when a file is double-clicked", ->
it "selects the file and opens it in the active editor on the first click, then changes focus to the active editor on the second", ->
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
expect(sampleJs).toHaveClass 'selected'
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
sampleJs.trigger clickEvent(originalEvent: { detail: 2 })
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "when a directory is single-clicked", ->
it "is selected", ->
subdir = treeView.root.find('.directory:first').view()
subdir.trigger clickEvent(originalEvent: { detail: 1 })
expect(subdir).toHaveClass 'selected'
describe "when a directory is double-clicked", ->
it "toggles the directory expansion state and does not change the focus to the editor", ->
sampleJs.trigger clickEvent(originalEvent: { detail: 1 })
subdir = treeView.root.find('.directory:first').view()
subdir.trigger clickEvent(originalEvent: { detail: 1 })
expect(subdir).toHaveClass 'selected'
subdir.trigger clickEvent(originalEvent: { detail: 2 })
expect(subdir).toHaveClass 'expanded'
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
describe "when a new file is opened in the active editor", ->
it "is selected in the tree view if the file's entry visible", ->
sampleJs.click()
rootView.open(require.resolve('fixtures/tree-view/tree-view.txt'))
expect(sampleTxt).toHaveClass 'selected'
expect(treeView.find('.selected').length).toBe 1
it "selected a file's parent dir if the file's entry is not visible", ->
rootView.open(require.resolve('fixtures/tree-view/dir1/sub-dir1/sub-file1'))
dirView = treeView.root.find('.directory:contains(dir1)').view()
expect(dirView).toHaveClass 'selected'
describe "when a different editor becomes active", ->
it "selects the file in that is open in that editor", ->
sampleJs.click()
leftEditor = rootView.getActiveEditor()
rightEditor = leftEditor.splitRight()
sampleTxt.click()
expect(sampleTxt).toHaveClass('selected')
leftEditor.focus()
expect(sampleJs).toHaveClass('selected')
describe "keyboard navigation", ->
afterEach ->
expect(treeView.find('.selected').length).toBeLessThan 2
describe "core:move-down", ->
describe "when a collapsed directory is selected", ->
it "skips to the next directory", ->
treeView.root.find('.directory:eq(0)').click()
treeView.trigger 'core:move-down'
expect(treeView.root.find('.directory:eq(1)')).toHaveClass 'selected'
describe "when an expanded directory is selected", ->
it "selects the first entry of the directory", ->
subdir = treeView.root.find('.directory:eq(1)').view()
subdir.expand()
subdir.click()
treeView.trigger 'core:move-down'
expect(subdir.entries.find('.entry:first')).toHaveClass 'selected'
describe "when the last entry of an expanded directory is selected", ->
it "selects the entry after its parent directory", ->
subdir1 = treeView.root.find('.directory:eq(1)').view()
subdir1.expand()
subdir1.entries.find('.entry:last').click()
treeView.trigger 'core:move-down'
expect(treeView.root.find('.entries > .entry:eq(2)')).toHaveClass 'selected'
describe "when the last directory of another last directory is selected", ->
[nested, nested2] = []
beforeEach ->
nested = treeView.root.find('.directory:eq(2)').view()
expect(nested.find('.header').text()).toContain 'nested/'
nested.expand()
nested2 = nested.entries.find('.entry:last').view()
nested2.click()
describe "when the directory is collapsed", ->
it "selects the entry after its grandparent directory", ->
treeView.trigger 'core:move-down'
expect(nested.next()).toHaveClass 'selected'
describe "when the directory is expanded", ->
it "selects the entry after its grandparent directory", ->
nested2.expand()
nested2.find('.file').remove() # kill the .gitkeep file, which has to be there but screws the test
treeView.trigger 'core:move-down'
expect(nested.next()).toHaveClass 'selected'
describe "when the last entry of the last directory is selected", ->
it "does not change the selection", ->
lastEntry = treeView.root.find('> .entries .entry:last')
lastEntry.click()
treeView.trigger 'core:move-down'
expect(lastEntry).toHaveClass 'selected'
describe "core:move-up", ->
describe "when there is an expanded directory before the currently selected entry", ->
it "selects the last entry in the expanded directory", ->
lastDir = treeView.root.find('.directory:last').view()
fileAfterDir = lastDir.next().view()
lastDir.expand()
fileAfterDir.click()
treeView.trigger 'core:move-up'
expect(lastDir.find('.entry:last')).toHaveClass 'selected'
describe "when there is an entry before the currently selected entry", ->
it "selects the previous entry", ->
lastEntry = treeView.root.find('.entry:last')
lastEntry.click()
treeView.trigger 'core:move-up'
expect(lastEntry.prev()).toHaveClass 'selected'
describe "when there is no entry before the currently selected entry, but there is a parent directory", ->
it "selects the parent directory", ->
subdir = treeView.root.find('.directory:first').view()
subdir.expand()
subdir.find('> .entries > .entry:first').click()
treeView.trigger 'core:move-up'
expect(subdir).toHaveClass 'selected'
describe "when there is no parent directory or previous entry", ->
it "does not change the selection", ->
treeView.root.click()
treeView.trigger 'core:move-up'
expect(treeView.root).toHaveClass 'selected'
describe "core:move-to-top", ->
it "scrolls to the top", ->
treeView.height(100)
treeView.attachToDom()
$(element).view().expand() for element in treeView.find('.directory')
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
expect(treeView.scrollTop()).toBe 0
entryCount = treeView.find(".entry").length
_.times entryCount, -> treeView.moveDown()
expect(treeView.scrollTop()).toBeGreaterThan 0
treeView.trigger 'core:move-to-top'
expect(treeView.scrollTop()).toBe 0
describe "core:move-to-bottom", ->
it "scrolls to the bottom", ->
treeView.height(100)
treeView.attachToDom()
$(element).view().expand() for element in treeView.find('.directory')
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
expect(treeView.scrollTop()).toBe 0
treeView.trigger 'core:move-to-bottom'
expect(treeView.scrollBottom()).toBe treeView.prop('scrollHeight')
describe "core:page-up", ->
it "scrolls up a page", ->
treeView.height(5)
treeView.attachToDom()
$(element).view().expand() for element in treeView.find('.directory')
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
expect(treeView.scrollTop()).toBe 0
treeView.scrollToBottom()
scrollTop = treeView.scrollTop()
expect(scrollTop).toBeGreaterThan 0
treeView.trigger 'core:page-up'
expect(treeView.scrollTop()).toBe scrollTop - treeView.height()
describe "core:page-down", ->
it "scrolls down a page", ->
treeView.height(5)
treeView.attachToDom()
$(element).view().expand() for element in treeView.find('.directory')
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
expect(treeView.scrollTop()).toBe 0
treeView.trigger 'core:page-down'
expect(treeView.scrollTop()).toBe treeView.height()
describe "movement outside of viewable region", ->
it "scrolls the tree view to the selected item", ->
treeView.height(100)
treeView.attachToDom()
$(element).view().expand() for element in treeView.find('.directory')
expect(treeView.prop('scrollHeight')).toBeGreaterThan treeView.outerHeight()
treeView.moveDown()
expect(treeView.scrollTop()).toBe 0
entryCount = treeView.find(".entry").length
_.times entryCount, -> treeView.moveDown()
expect(treeView.scrollBottom()).toBe treeView.prop('scrollHeight')
_.times entryCount, -> treeView.moveUp()
expect(treeView.scrollTop()).toBe 0
describe "tree-view:expand-directory", ->
describe "when a directory entry is selected", ->
it "expands the current directory", ->
subdir = treeView.root.find('.directory:first')
subdir.click()
expect(subdir).not.toHaveClass 'expanded'
treeView.trigger 'tree-view:expand-directory'
expect(subdir).toHaveClass 'expanded'
describe "when a file entry is selected", ->
it "does nothing", ->
treeView.root.find('.file').click()
treeView.trigger 'tree-view:expand-directory'
describe "tree-view:collapse-directory", ->
subdir = null
beforeEach ->
subdir = treeView.root.find('> .entries > .directory').eq(0).view()
subdir.expand()
describe "when an expanded directory is selected", ->
it "collapses the selected directory", ->
expect(subdir).toHaveClass 'expanded'
subdir.click()
treeView.trigger 'tree-view:collapse-directory'
expect(subdir).not.toHaveClass 'expanded'
expect(treeView.root).toHaveClass 'expanded'
describe "when a collapsed directory is selected", ->
it "collapses and selects the selected directory's parent directory", ->
subdir.find('.directory').click()
treeView.trigger 'tree-view:collapse-directory'
expect(subdir).not.toHaveClass 'expanded'
expect(subdir).toHaveClass 'selected'
expect(treeView.root).toHaveClass 'expanded'
describe "when collapsed root directory is selected", ->
it "does not raise an error", ->
treeView.root.collapse()
treeView.selectEntry(treeView.root)
treeView.trigger 'tree-view:collapse-directory'
describe "when a file is selected", ->
it "collapses and selects the selected file's parent directory", ->
subdir.find('.file').click()
treeView.trigger 'tree-view:collapse-directory'
expect(subdir).not.toHaveClass 'expanded'
expect(subdir).toHaveClass 'selected'
expect(treeView.root).toHaveClass 'expanded'
describe "tree-view:open-selected-entry", ->
describe "when a file is selected", ->
it "opens the file in the editor and focuses it", ->
treeView.root.find('.file:contains(tree-view.js)').click()
treeView.root.trigger 'tree-view:open-selected-entry'
expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/tree-view/tree-view.js')
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "when a directory is selected", ->
it "expands or collapses the directory", ->
subdir = treeView.root.find('.directory').first()
subdir.click()
expect(subdir).not.toHaveClass 'expanded'
treeView.root.trigger 'tree-view:open-selected-entry'
expect(subdir).toHaveClass 'expanded'
treeView.root.trigger 'tree-view:open-selected-entry'
expect(subdir).not.toHaveClass 'expanded'
describe "when nothing is selected", ->
it "does nothing", ->
treeView.root.trigger 'tree-view:open-selected-entry'
expect(rootView.getActiveEditor()).toBeUndefined()
describe "file modification", ->
[dirView, fileView, rootDirPath, dirPath, filePath] = []
beforeEach ->
rootView.deactivate()
rootDirPath = "/tmp/atom-tests"
fs.remove(rootDirPath) if fs.exists(rootDirPath)
dirPath = fs.join(rootDirPath, "test-dir")
filePath = fs.join(dirPath, "test-file.txt")
fs.makeDirectory(rootDirPath)
fs.makeDirectory(dirPath)
fs.write(filePath, "doesn't matter")
rootView = new RootView(rootDirPath)
project = rootView.project
rootView.activateExtension(TreeView)
treeView = rootView.find(".tree-view").view()
dirView = treeView.root.entries.find('.directory:contains(test-dir)').view()
dirView.expand()
fileView = treeView.find('.file:contains(test-file.txt)').view()
afterEach ->
fs.remove(rootDirPath) if fs.exists(rootDirPath)
describe "tree-view:add", ->
addDialog = null
beforeEach ->
fileView.click()
treeView.trigger "tree-view:add"
addDialog = rootView.find(".tree-view-dialog").view()
describe "when a file is selected", ->
it "opens an add dialog with the file's current directory path populated", ->
expect(addDialog).toExist()
expect(addDialog.prompt.text()).toBeTruthy()
expect(project.relativize(dirPath)).toMatch(/[^\/]$/)
expect(addDialog.miniEditor.getText()).toBe(project.relativize(dirPath) + "/")
expect(addDialog.miniEditor.getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length
expect(addDialog.miniEditor.isFocused).toBeTruthy()
describe "when parent directory of the selected file changes", ->
it "active file is still shown as selected in the tree view", ->
directoryChangeHandler = jasmine.createSpy("directory-change")
dirView.on "tree-view:directory-modified", directoryChangeHandler
dirView.directory.trigger 'contents-change'
expect(directoryChangeHandler).toHaveBeenCalled()
expect(treeView.find('.selected').text()).toBe fs.base(filePath)
describe "when the path without a trailing '/' is changed and confirmed", ->
describe "when no file exists at that location", ->
it "add a file, closes the dialog and selects the file in the tree-view", ->
newPath = fs.join(dirPath, "new-test-file.txt")
addDialog.miniEditor.insertText(fs.base(newPath))
addDialog.trigger 'core:confirm'
expect(fs.exists(newPath)).toBeTruthy()
expect(fs.isFile(newPath)).toBeTruthy()
expect(addDialog.parent()).not.toExist()
expect(rootView.getActiveEditor().getPath()).toBe newPath
waitsFor "tree view to be updated", ->
dirView.entries.find("> .file").length > 1
runs ->
expect(treeView.find('.selected').text()).toBe fs.base(newPath)
describe "when a file already exists at that location", ->
it "shows an error message and does not close the dialog", ->
newPath = fs.join(dirPath, "new-test-file.txt")
fs.write(newPath, '')
addDialog.miniEditor.insertText(fs.base(newPath))
addDialog.trigger 'core:confirm'
expect(addDialog.prompt.text()).toContain 'Error'
expect(addDialog.prompt.text()).toContain 'already exists'
expect(addDialog.prompt).toHaveClass('error')
expect(addDialog.hasParent()).toBeTruthy()
describe "when the path with a trailing '/' is changed and confirmed", ->
describe "when no file or directory exists at the given path", ->
it "adds a directory and closes the dialog", ->
treeView.attachToDom()
newPath = fs.join(dirPath, "new/dir")
addDialog.miniEditor.insertText("new/dir/")
addDialog.trigger 'core:confirm'
expect(fs.exists(newPath)).toBeTruthy()
expect(fs.isDirectory(newPath)).toBeTruthy()
expect(addDialog.parent()).not.toExist()
expect(rootView.getActiveEditor().getPath()).not.toBe newPath
expect(treeView).toMatchSelector(':focus')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
expect(dirView.find('.directory.selected:contains(new/)').length).toBe(1)
it "selects the created directory", ->
treeView.attachToDom()
newPath = fs.join(dirPath, "new2/")
addDialog.miniEditor.insertText("new2/")
addDialog.trigger 'core:confirm'
expect(fs.exists(newPath)).toBeTruthy()
expect(fs.isDirectory(newPath)).toBeTruthy()
expect(addDialog.parent()).not.toExist()
expect(rootView.getActiveEditor().getPath()).not.toBe newPath
expect(treeView).toMatchSelector(':focus')
expect(rootView.getActiveEditor().isFocused).toBeFalsy()
expect(dirView.find('.directory.selected:contains(new2/)').length).toBe(1)
describe "when a file or directory already exists at the given path", ->
it "shows an error message and does not close the dialog", ->
newPath = fs.join(dirPath, "new-dir")
fs.makeDirectory(newPath)
addDialog.miniEditor.insertText("new-dir/")
addDialog.trigger 'core:confirm'
expect(addDialog.prompt.text()).toContain 'Error'
expect(addDialog.prompt.text()).toContain 'already exists'
expect(addDialog.prompt).toHaveClass('error')
expect(addDialog.hasParent()).toBeTruthy()
describe "when 'core:cancel' is triggered on the add dialog", ->
it "removes the dialog and focuses the tree view", ->
treeView.attachToDom()
addDialog.trigger 'core:cancel'
expect(addDialog.parent()).not.toExist()
expect(treeView).toMatchSelector(':focus')
describe "when the add dialog's editor loses focus", ->
it "removes the dialog and focuses root view", ->
rootView.attachToDom()
rootView.focus()
expect(addDialog.parent()).not.toExist()
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "when a directory is selected", ->
it "opens an add dialog with the directory's path populated", ->
addDialog.cancel()
dirView.click()
treeView.trigger "tree-view:add"
addDialog = rootView.find(".tree-view-dialog").view()
expect(addDialog).toExist()
expect(addDialog.prompt.text()).toBeTruthy()
expect(project.relativize(dirPath)).toMatch(/[^\/]$/)
expect(addDialog.miniEditor.getText()).toBe(project.relativize(dirPath) + "/")
expect(addDialog.miniEditor.getCursorBufferPosition().column).toBe addDialog.miniEditor.getText().length
expect(addDialog.miniEditor.isFocused).toBeTruthy()
describe "when the root directory is selected", ->
it "opens an add dialog with no path populated", ->
addDialog.cancel()
treeView.root.click()
treeView.trigger "tree-view:add"
addDialog = rootView.find(".tree-view-dialog").view()
expect(addDialog.miniEditor.getText().length).toBe 0
describe "tree-view:move", ->
describe "when a file is selected", ->
moveDialog = null
beforeEach ->
fileView.click()
treeView.trigger "tree-view:move"
moveDialog = rootView.find(".tree-view-dialog").view()
it "opens a move dialog with the file's current path (excluding extension) populated", ->
extension = fs.extension(filePath)
fileNameWithoutExtension = fs.base(filePath, extension)
expect(moveDialog).toExist()
expect(moveDialog.prompt.text()).toBe "Enter the new path for the file:"
expect(moveDialog.miniEditor.getText()).toBe(project.relativize(filePath))
expect(moveDialog.miniEditor.getSelectedText()).toBe fs.base(fileNameWithoutExtension)
expect(moveDialog.miniEditor.isFocused).toBeTruthy()
describe "when the path is changed and confirmed", ->
describe "when all the directories along the new path exist", ->
it "moves the file, updates the tree view, and closes the dialog", ->
newPath = fs.join(rootDirPath, 'renamed-test-file.txt')
moveDialog.miniEditor.setText(newPath)
moveDialog.trigger 'core:confirm'
expect(fs.exists(newPath)).toBeTruthy()
expect(fs.exists(filePath)).toBeFalsy()
expect(moveDialog.parent()).not.toExist()
waitsFor "tree view to update", ->
treeView.root.find('> .entries > .file:contains(renamed-test-file.txt)').length > 0
runs ->
dirView = treeView.root.entries.find('.directory:contains(test-dir)').view()
dirView.expand()
expect(dirView.entries.children().length).toBe 0
describe "when the directories along the new path don't exist", ->
it "creates the target directory before moving the file", ->
newPath = fs.join(rootDirPath, 'new/directory', 'renamed-test-file.txt')
moveDialog.miniEditor.setText(newPath)
moveDialog.trigger 'core:confirm'
expect(fs.exists(newPath)).toBeTruthy()
expect(fs.exists(filePath)).toBeFalsy()
waits 50 # TODO: remove this workaround once we fix the race condition in fs events
describe "when a file or directory already exists at the target path", ->
it "shows an error message and does not close the dialog", ->
runs ->
fs.write(fs.join(rootDirPath, 'target.txt'), '')
newPath = fs.join(rootDirPath, 'target.txt')
moveDialog.miniEditor.setText(newPath)
moveDialog.trigger 'core:confirm'
expect(moveDialog.prompt.text()).toContain 'Error'
expect(moveDialog.prompt.text()).toContain 'already exists'
expect(moveDialog.prompt).toHaveClass('error')
expect(moveDialog.hasParent()).toBeTruthy()
describe "when 'core:cancel' is triggered on the move dialog", ->
it "removes the dialog and focuses the tree view", ->
treeView.attachToDom()
moveDialog.trigger 'core:cancel'
expect(moveDialog.parent()).not.toExist()
expect(treeView).toMatchSelector(':focus')
describe "when the move dialog's editor loses focus", ->
it "removes the dialog and focuses root view", ->
rootView.attachToDom()
rootView.focus()
expect(moveDialog.parent()).not.toExist()
expect(rootView.getActiveEditor().isFocused).toBeTruthy()
describe "tree-view:remove", ->
it "shows the native alert dialog", ->
fileView.click()
spyOn(atom, 'confirm')
treeView.trigger 'tree-view:remove'
expect(atom.confirm).toHaveBeenCalled()
describe "file system events", ->
temporaryFilePath = null
beforeEach ->
temporaryFilePath = fs.join(require.resolve('fixtures/tree-view'), 'temporary')
if fs.exists(temporaryFilePath)
fs.remove(temporaryFilePath)
waits(20)
afterEach ->
fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath)
describe "when a file is added or removed in an expanded directory", ->
it "updates the directory view to display the directory's new contents", ->
entriesCountBefore = null
runs ->
expect(fs.exists(temporaryFilePath)).toBeFalsy()
entriesCountBefore = treeView.root.entries.find('.entry').length
fs.write temporaryFilePath, 'hi'
waitsFor "directory view contens to refresh", ->
treeView.root.entries.find('.entry').length == entriesCountBefore + 1
runs ->
expect(treeView.root.entries.find('.entry').length).toBe entriesCountBefore + 1
expect(treeView.root.entries.find('.file:contains(temporary)')).toExist()
fs.remove(temporaryFilePath)
waitsFor "directory view contens to refresh", ->
treeView.root.entries.find('.entry').length == entriesCountBefore

View File

@@ -1,324 +0,0 @@
Editor = require 'editor'
VimMode = require 'vim-mode'
xdescribe "VimMode", ->
[editor, vimMode] = []
beforeEach ->
editor = new Editor
editor.enableKeymap()
editor.isFocused = true
vimMode = new VimMode(editor)
describe "initialize", ->
it "puts the editor in command-mode initially", ->
expect(editor).toHaveClass 'command-mode'
describe "command-mode", ->
it "stops propagation on key events would otherwise insert a character, but allows unhandled non-insertions through", ->
event = keydownEvent('\\')
spyOn(event, 'stopPropagation')
editor.trigger event
expect(event.stopPropagation).toHaveBeenCalled()
event = keydownEvent('/', metaKey: true)
spyOn(event, 'stopPropagation')
editor.trigger event
expect(event.stopPropagation).not.toHaveBeenCalled()
it "does not allow the cursor to be placed on the \n character, unless the line is empty", ->
editor.setText("012345\n\nabcdef")
editor.setCursorScreenPosition([0, 5])
expect(editor.getCursorScreenPosition()).toEqual [0,5]
editor.setCursorScreenPosition([0, 6])
expect(editor.getCursorScreenPosition()).toEqual [0,5]
editor.setCursorScreenPosition([1, 0])
expect(editor.getCursorScreenPosition()).toEqual [1,0]
it "clears the operator stack when commands can't be composed", ->
editor.trigger keydownEvent('d')
expect(vimMode.opStack.length).toBe 1
editor.trigger keydownEvent('x')
expect(vimMode.opStack.length).toBe 0
editor.trigger keydownEvent('d')
expect(vimMode.opStack.length).toBe 1
editor.trigger keydownEvent('\\') # \ is an unused key in vim
expect(vimMode.opStack.length).toBe 0
describe "the escape keybinding", ->
it "clears the operator stack", ->
editor.trigger keydownEvent('d')
expect(vimMode.opStack.length).toBe 1
editor.trigger keydownEvent('escape')
expect(vimMode.opStack.length).toBe 0
describe "the i keybinding", ->
it "puts the editor into insert mode", ->
expect(editor).not.toHaveClass 'insert-mode'
editor.trigger keydownEvent('i')
expect(editor).toHaveClass 'insert-mode'
expect(editor).not.toHaveClass 'command-mode'
describe "the x keybinding", ->
it "deletes a charachter", ->
editor.setText("012345")
editor.setCursorScreenPosition([0, 4])
editor.trigger keydownEvent('x')
expect(editor.getText()).toBe '01235'
expect(editor.getCursorScreenPosition()).toEqual([0, 4])
editor.trigger keydownEvent('x')
expect(editor.getText()).toBe '0123'
expect(editor.getCursorScreenPosition()).toEqual([0, 3])
editor.trigger keydownEvent('x')
expect(editor.getText()).toBe '012'
expect(editor.getCursorScreenPosition()).toEqual([0, 2])
it "deletes nothing when cursor is on empty line", ->
editor.getBuffer().setText "012345\n\nabcdef"
editor.setCursorScreenPosition [1, 0]
editor.trigger keydownEvent 'x'
expect(editor.getText()).toBe "012345\n\nabcdef"
describe "the d keybinding", ->
describe "when followed by a d", ->
it "deletes the current line", ->
editor.setText("12345\nabcde\nABCDE")
editor.setCursorScreenPosition([1,1])
editor.trigger keydownEvent('d')
editor.trigger keydownEvent('d')
expect(editor.getText()).toBe "12345\nABCDE"
expect(editor.getCursorScreenPosition()).toEqual([1,0])
it "deletes the last line", ->
editor.setText("12345\nabcde\nABCDE")
editor.setCursorScreenPosition([2,1])
editor.trigger keydownEvent('d')
editor.trigger keydownEvent('d')
expect(editor.getText()).toBe "12345\nabcde"
expect(editor.getCursorScreenPosition()).toEqual([1,0])
xdescribe "when the second d is prefixed by a count", ->
it "deletes n lines, starting from the current", ->
editor.setText("12345\nabcde\nABCDE\nQWERT")
editor.setCursorScreenPosition([1,1])
editor.trigger keydownEvent('d')
editor.trigger keydownEvent('2')
editor.trigger keydownEvent('d')
expect(editor.getText()).toBe "12345\nQWERT"
expect(editor.getCursorScreenPosition()).toEqual([1,0])
describe "when followed by an h", ->
it "deletes the previous letter on the current line", ->
editor.setText("abcd\n01234")
editor.setCursorScreenPosition([1,1])
editor.trigger keydownEvent 'd'
editor.trigger keydownEvent 'h'
expect(editor.getText()).toBe "abcd\n1234"
expect(editor.getCursorScreenPosition()).toEqual([1,0])
editor.trigger keydownEvent 'd'
editor.trigger keydownEvent 'h'
expect(editor.getText()).toBe "abcd\n1234"
expect(editor.getCursorScreenPosition()).toEqual([1,0])
describe "when followed by a w", ->
it "deletes to the beginning of the next word", ->
editor.setText("abcd efg")
editor.setCursorScreenPosition([0,2])
editor.trigger keydownEvent('d')
editor.trigger keydownEvent('w')
expect(editor.getText()).toBe "abefg"
expect(editor.getCursorScreenPosition()).toEqual([0,2])
editor.setText("one two three four")
editor.setCursorScreenPosition([0,0])
editor.trigger keydownEvent('d')
editor.trigger keydownEvent('3')
editor.trigger keydownEvent('w')
expect(editor.getText()).toBe "four"
expect(editor.getCursorScreenPosition()).toEqual([0,0])
describe "when followed by a b", ->
it "deletes to the beginning of the previous word", ->
editor.setText("abcd efg")
editor.setCursorScreenPosition([0,2])
editor.trigger keydownEvent('d')
editor.trigger keydownEvent('b')
expect(editor.getText()).toBe "cd efg"
expect(editor.getCursorScreenPosition()).toEqual([0,0])
editor.setText("one two three four")
editor.setCursorScreenPosition([0,11])
editor.trigger keydownEvent('d')
editor.trigger keydownEvent('3')
editor.trigger keydownEvent('b')
expect(editor.getText()).toBe "ee four"
expect(editor.getCursorScreenPosition()).toEqual([0,0])
describe "basic motion bindings", ->
beforeEach ->
editor.setText("12345\nabcde\nABCDE")
editor.setCursorScreenPosition([1,1])
describe "the h keybinding", ->
it "moves the cursor left, but not to the previous line", ->
editor.trigger keydownEvent('h')
expect(editor.getCursorScreenPosition()).toEqual([1,0])
editor.trigger keydownEvent('h')
expect(editor.getCursorScreenPosition()).toEqual([1,0])
describe "the j keybinding", ->
it "moves the cursor down, but not to the end of the last line", ->
editor.trigger keydownEvent 'j'
expect(editor.getCursorScreenPosition()).toEqual([2,1])
editor.trigger keydownEvent 'j'
expect(editor.getCursorScreenPosition()).toEqual([2,1])
describe "the k keybinding", ->
it "moves the cursor up, but not to the beginning of the first line", ->
editor.trigger keydownEvent('k')
expect(editor.getCursorScreenPosition()).toEqual([0,1])
editor.trigger keydownEvent('k')
expect(editor.getCursorScreenPosition()).toEqual([0,1])
describe "the l keybinding", ->
it "moves the cursor right, but not to the next line", ->
editor.setCursorScreenPosition([1,3])
editor.trigger keydownEvent('l')
expect(editor.getCursorScreenPosition()).toEqual([1,4])
editor.trigger keydownEvent('l')
expect(editor.getCursorScreenPosition()).toEqual([1,4])
describe "the w keybinding", ->
it "moves the cursor to the beginning of the next word", ->
editor.setText("ab cde1+- \n xyz\n\nzip")
editor.setCursorScreenPosition([0,0])
editor.trigger keydownEvent('w')
expect(editor.getCursorScreenPosition()).toEqual([0,3])
editor.trigger keydownEvent('w')
expect(editor.getCursorScreenPosition()).toEqual([0,7])
editor.trigger keydownEvent('w')
expect(editor.getCursorScreenPosition()).toEqual([1,1])
editor.trigger keydownEvent('w')
expect(editor.getCursorScreenPosition()).toEqual([2,0])
editor.trigger keydownEvent('w')
expect(editor.getCursorScreenPosition()).toEqual([3,0])
editor.trigger keydownEvent('w')
expect(editor.getCursorScreenPosition()).toEqual([3,2])
describe "the { keybinding", ->
it "moves the cursor to the beginning of the paragraph", ->
editor.setText("abcde\n\nfghij\nhijk\n xyz \n\nzip\n\n \nthe end")
editor.setCursorScreenPosition([0,0])
editor.trigger keydownEvent('}')
expect(editor.getCursorScreenPosition()).toEqual [1,0]
editor.trigger keydownEvent('}')
expect(editor.getCursorScreenPosition()).toEqual [5,0]
editor.trigger keydownEvent('}')
expect(editor.getCursorScreenPosition()).toEqual [7,0]
editor.trigger keydownEvent('}')
expect(editor.getCursorScreenPosition()).toEqual [9,6]
describe "the b keybinding", ->
it "moves the cursor to the beginning of the previous word", ->
editor.setText(" ab cde1+- \n xyz\n\nzip }\n last")
editor.setCursorScreenPosition [4,1]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [3,4]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [3,0]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [2,0]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [1,1]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [0,8]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [0,4]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [0,1]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [0,0]
editor.trigger keydownEvent('b')
expect(editor.getCursorScreenPosition()).toEqual [0,0]
describe "numeric prefix bindings", ->
it "repeats the following operation N times", ->
editor.setText("12345")
editor.setCursorScreenPosition([0,1])
editor.trigger keydownEvent('3')
editor.trigger keydownEvent('x')
expect(editor.getText()).toBe '15'
editor.setText("123456789abc")
editor.setCursorScreenPosition([0,0])
editor.trigger keydownEvent('1')
editor.trigger keydownEvent('0')
editor.trigger keydownEvent('x')
expect(editor.getText()).toBe 'bc'
describe "insert-mode", ->
beforeEach ->
editor.trigger keydownEvent('i')
it "allows the cursor to reach the end of the line", ->
editor.setText("012345\n\nabcdef")
editor.setCursorScreenPosition([0, 5])
expect(editor.getCursorScreenPosition()).toEqual [0,5]
editor.setCursorScreenPosition([0, 6])
expect(editor.getCursorScreenPosition()).toEqual [0,6]
it "puts the editor into command mode when <escape> is pressed", ->
expect(editor).not.toHaveClass 'command-mode'
editor.trigger keydownEvent('escape')
expect(editor).toHaveClass 'command-mode'
expect(editor).not.toHaveClass 'insert-mode'

View File

@@ -1,70 +0,0 @@
$ = require 'jquery'
RootView = require 'root-view'
describe "WrapGuide", ->
[rootView, editor, wrapGuide] = []
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
requireExtension('wrap-guide')
rootView.attachToDom()
editor = rootView.getActiveEditor()
wrapGuide = rootView.find('.wrap-guide').view()
afterEach ->
rootView.deactivate()
describe "@initialize", ->
it "appends a wrap guide to all existing and new editors", ->
expect(rootView.panes.find('.pane').length).toBe 1
expect(rootView.panes.find('.lines > .wrap-guide').length).toBe 1
editor.splitRight()
expect(rootView.find('.pane').length).toBe 2
expect(rootView.panes.find('.lines > .wrap-guide').length).toBe 2
describe "@updateGuide", ->
it "positions the guide at the configured column", ->
width = editor.charWidth * wrapGuide.defaultColumn
expect(width).toBeGreaterThan(0)
expect(wrapGuide.position().left).toBe(width)
describe "font-size-change", ->
it "updates the wrap guide position", ->
initial = wrapGuide.position().left
expect(initial).toBeGreaterThan(0)
rootView.trigger('window:increase-font-size')
expect(wrapGuide.position().left).toBeGreaterThan(initial)
describe "overriding getGuideColumn", ->
it "invokes the callback with the editor path", ->
editorPath = null
wrapGuide.getGuideColumn = (path) ->
editorPath = path
80
wrapGuide.updateGuide(editor)
expect(editorPath).toBe(require.resolve('fixtures/sample.js'))
it "invokes the callback with a default value", ->
column = null
wrapGuide.getGuideColumn = (path, defaultColumn) ->
editorPath = path
column = defaultColumn
defaultColumn
wrapGuide.updateGuide(editor)
expect(column).toBeGreaterThan(0)
it "uses the function from the config data", ->
rootView.find('.wrap-guide').remove()
config =
getGuideColumn: ->
1
requireExtension('wrap-guide', config)
wrapGuide = rootView.find('.wrap-guide').view()
expect(wrapGuide.getGuideColumn).toBe(config.getGuideColumn)
it "hides the guide when the column is less than 1", ->
wrapGuide.getGuideColumn = (path) ->
-1
wrapGuide.updateGuide(editor)
expect(wrapGuide).toBeHidden()