mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-21 16:08:24 +03:00
bb8b3782b9
Previously, package specs needed to deactivate the root view to test their package serialization. Now, specs can just deactivate and then reactivate the package, relying on serialization infrastructure that's independent of the lifecycle of the RootView.
357 lines
14 KiB
CoffeeScript
357 lines
14 KiB
CoffeeScript
$ = require 'jquery'
|
|
fs = require 'fs-utils'
|
|
Project = require 'project'
|
|
RootView = require 'root-view'
|
|
Buffer = require 'text-buffer'
|
|
Editor = require 'editor'
|
|
Pane = require 'pane'
|
|
{View, $$} = require 'space-pen'
|
|
|
|
describe "RootView", ->
|
|
pathToOpen = null
|
|
|
|
beforeEach ->
|
|
project.setPath(project.resolve('dir'))
|
|
pathToOpen = project.resolve('a')
|
|
window.rootView = new RootView
|
|
rootView.enableKeymap()
|
|
rootView.open(pathToOpen)
|
|
rootView.focus()
|
|
|
|
describe "@deserialize()", ->
|
|
viewState = null
|
|
|
|
describe "when the serialized RootView has an unsaved buffer", ->
|
|
it "constructs the view with the same panes", ->
|
|
rootView.attachToDom()
|
|
rootView.open()
|
|
editor1 = rootView.getActiveView()
|
|
buffer = editor1.getBuffer()
|
|
editor1.splitRight()
|
|
viewState = rootView.serialize()
|
|
rootView.remove()
|
|
|
|
window.rootView = deserialize(viewState)
|
|
rootView.attachToDom()
|
|
|
|
expect(rootView.getEditors().length).toBe 2
|
|
expect(rootView.getActiveView().getText()).toBe buffer.getText()
|
|
expect(rootView.title).toBe "untitled - #{project.getPath()}"
|
|
|
|
describe "when the serialized RootView has a project", ->
|
|
describe "when there are open editors", ->
|
|
it "constructs the view with the same panes", ->
|
|
rootView.attachToDom()
|
|
pane1 = rootView.getActivePane()
|
|
pane2 = pane1.splitRight()
|
|
pane3 = pane2.splitRight()
|
|
pane4 = pane2.splitDown()
|
|
pane2.showItem(project.buildEditSession('b'))
|
|
pane3.showItem(project.buildEditSession('../sample.js'))
|
|
pane3.activeItem.setCursorScreenPosition([2, 4])
|
|
pane4.showItem(project.buildEditSession('../sample.txt'))
|
|
pane4.activeItem.setCursorScreenPosition([0, 2])
|
|
pane2.focus()
|
|
|
|
viewState = rootView.serialize()
|
|
rootView.remove()
|
|
window.rootView = deserialize(viewState)
|
|
rootView.attachToDom()
|
|
|
|
expect(rootView.getEditors().length).toBe 4
|
|
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
|
editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
|
editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
|
editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
|
|
|
expect(editor1.getPath()).toBe project.resolve('a')
|
|
expect(editor2.getPath()).toBe project.resolve('b')
|
|
expect(editor3.getPath()).toBe project.resolve('../sample.js')
|
|
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
|
expect(editor4.getPath()).toBe project.resolve('../sample.txt')
|
|
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
|
|
|
# ensure adjust pane dimensions is called
|
|
expect(editor1.width()).toBeGreaterThan 0
|
|
expect(editor2.width()).toBeGreaterThan 0
|
|
expect(editor3.width()).toBeGreaterThan 0
|
|
expect(editor4.width()).toBeGreaterThan 0
|
|
|
|
# ensure correct editor is focused again
|
|
expect(editor2.isFocused).toBeTruthy()
|
|
expect(editor1.isFocused).toBeFalsy()
|
|
expect(editor3.isFocused).toBeFalsy()
|
|
expect(editor4.isFocused).toBeFalsy()
|
|
|
|
expect(rootView.title).toBe "#{fs.base(editor2.getPath())} - #{project.getPath()}"
|
|
|
|
describe "where there are no open editors", ->
|
|
it "constructs the view with no open editors", ->
|
|
rootView.getActivePane().remove()
|
|
expect(rootView.getEditors().length).toBe 0
|
|
|
|
viewState = rootView.serialize()
|
|
rootView.remove()
|
|
window.rootView = deserialize(viewState)
|
|
|
|
rootView.attachToDom()
|
|
expect(rootView.getEditors().length).toBe 0
|
|
|
|
describe "focus", ->
|
|
describe "when there is an active view", ->
|
|
it "hands off focus to the active view", ->
|
|
editor = rootView.getActiveView()
|
|
editor.isFocused = false
|
|
rootView.focus()
|
|
expect(editor.isFocused).toBeTruthy()
|
|
|
|
describe "when there is no active view", ->
|
|
beforeEach ->
|
|
rootView.getActivePane().remove()
|
|
expect(rootView.getActiveView()).toBeUndefined()
|
|
rootView.attachToDom()
|
|
expect(document.activeElement).toBe document.body
|
|
|
|
describe "when are visible focusable elements (with a -1 tabindex)", ->
|
|
it "passes focus to the first focusable element", ->
|
|
focusable1 = $$ -> @div "One", id: 'one', tabindex: -1
|
|
focusable2 = $$ -> @div "Two", id: 'two', tabindex: -1
|
|
rootView.horizontal.append(focusable1, focusable2)
|
|
expect(document.activeElement).toBe document.body
|
|
|
|
rootView.focus()
|
|
expect(document.activeElement).toBe focusable1[0]
|
|
|
|
describe "when there are no visible focusable elements", ->
|
|
it "surrenders focus to the body", ->
|
|
focusable = $$ -> @div "One", id: 'one', tabindex: -1
|
|
rootView.horizontal.append(focusable)
|
|
focusable.hide()
|
|
expect(document.activeElement).toBe document.body
|
|
|
|
rootView.focus()
|
|
expect(document.activeElement).toBe document.body
|
|
|
|
describe "keymap wiring", ->
|
|
commandHandler = null
|
|
beforeEach ->
|
|
commandHandler = jasmine.createSpy('commandHandler')
|
|
rootView.on('foo-command', commandHandler)
|
|
|
|
window.keymap.bindKeys('*', 'x': 'foo-command')
|
|
|
|
describe "when a keydown event is triggered on the RootView", ->
|
|
it "triggers matching keybindings for that event", ->
|
|
event = keydownEvent 'x', target: rootView[0]
|
|
|
|
rootView.trigger(event)
|
|
expect(commandHandler).toHaveBeenCalled()
|
|
|
|
describe "window title", ->
|
|
describe "when the project has no path", ->
|
|
it "sets the title to 'untitled'", ->
|
|
project.setPath(undefined)
|
|
expect(rootView.title).toBe 'untitled'
|
|
|
|
describe "when the project has a path", ->
|
|
beforeEach ->
|
|
rootView.open('b')
|
|
|
|
describe "when there is an active pane item", ->
|
|
it "sets the title to the pane item's title plus the project path", ->
|
|
item = rootView.getActivePaneItem()
|
|
expect(rootView.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
|
|
|
describe "when the title of the active pane item changes", ->
|
|
it "updates the window title based on the item's new title", ->
|
|
editSession = rootView.getActivePaneItem()
|
|
editSession.buffer.setPath('/tmp/hi')
|
|
expect(rootView.title).toBe "#{editSession.getTitle()} - #{project.getPath()}"
|
|
|
|
describe "when the active pane's item changes", ->
|
|
it "updates the title to the new item's title plus the project path", ->
|
|
rootView.getActivePane().showNextItem()
|
|
item = rootView.getActivePaneItem()
|
|
expect(rootView.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
|
|
|
describe "when the last pane item is removed", ->
|
|
it "sets the title to the project's path", ->
|
|
rootView.getActivePane().remove()
|
|
expect(rootView.getActivePaneItem()).toBeUndefined()
|
|
expect(rootView.title).toBe project.getPath()
|
|
|
|
describe "when an inactive pane's item changes", ->
|
|
it "does not update the title", ->
|
|
pane = rootView.getActivePane()
|
|
pane.splitRight()
|
|
initialTitle = rootView.title
|
|
pane.showNextItem()
|
|
expect(rootView.title).toBe initialTitle
|
|
|
|
describe "font size adjustment", ->
|
|
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
|
|
fontSizeBefore = config.get('editor.fontSize')
|
|
rootView.trigger 'window:increase-font-size'
|
|
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
|
rootView.trigger 'window:increase-font-size'
|
|
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
|
rootView.trigger 'window:decrease-font-size'
|
|
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
|
rootView.trigger 'window:decrease-font-size'
|
|
expect(config.get('editor.fontSize')).toBe fontSizeBefore
|
|
|
|
it "does not allow the font size to be less than 1", ->
|
|
config.set("editor.fontSize", 1)
|
|
rootView.trigger 'window:decrease-font-size'
|
|
expect(config.get('editor.fontSize')).toBe 1
|
|
|
|
describe ".open(path, options)", ->
|
|
describe "when there is no active pane", ->
|
|
beforeEach ->
|
|
spyOn(Pane.prototype, 'focus')
|
|
rootView.getActivePane().remove()
|
|
expect(rootView.getActivePane()).toBeUndefined()
|
|
|
|
describe "when called with no path", ->
|
|
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
|
editSession = rootView.open()
|
|
expect(rootView.getActivePane().activeItem).toBe editSession
|
|
expect(editSession.getPath()).toBeUndefined()
|
|
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
|
|
|
describe "when called with a path", ->
|
|
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
|
editSession = rootView.open('b')
|
|
expect(rootView.getActivePane().activeItem).toBe editSession
|
|
expect(editSession.getPath()).toBe fs.resolveOnLoadPath('fixtures/dir/b')
|
|
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
|
|
|
describe "when the changeFocus option is false", ->
|
|
it "does not focus the new pane", ->
|
|
editSession = rootView.open('b', changeFocus: false)
|
|
expect(rootView.getActivePane().focus).not.toHaveBeenCalled()
|
|
|
|
describe "when there is an active pane", ->
|
|
[activePane, initialItemCount] = []
|
|
beforeEach ->
|
|
activePane = rootView.getActivePane()
|
|
spyOn(activePane, 'focus')
|
|
initialItemCount = activePane.getItems().length
|
|
|
|
describe "when called with no path", ->
|
|
it "opens an edit session with an empty buffer as an item on the active pane and focuses it", ->
|
|
editSession = rootView.open()
|
|
expect(activePane.getItems().length).toBe initialItemCount + 1
|
|
expect(activePane.activeItem).toBe editSession
|
|
expect(editSession.getPath()).toBeUndefined()
|
|
expect(activePane.focus).toHaveBeenCalled()
|
|
|
|
describe "when called with a path", ->
|
|
describe "when the active pane already has an edit session item for the path being opened", ->
|
|
it "shows the existing edit session on the pane", ->
|
|
previousEditSession = activePane.activeItem
|
|
|
|
editSession = rootView.open('b')
|
|
expect(activePane.activeItem).toBe editSession
|
|
expect(editSession).not.toBe previousEditSession
|
|
|
|
editSession = rootView.open(previousEditSession.getPath())
|
|
expect(editSession).toBe previousEditSession
|
|
expect(activePane.activeItem).toBe editSession
|
|
|
|
expect(activePane.focus).toHaveBeenCalled()
|
|
|
|
describe "when the active pane does not have an edit session item for the path being opened", ->
|
|
it "creates a new edit session for the given path in the active editor", ->
|
|
editSession = rootView.open('b')
|
|
expect(activePane.items.length).toBe 2
|
|
expect(activePane.activeItem).toBe editSession
|
|
expect(activePane.focus).toHaveBeenCalled()
|
|
|
|
describe "when the changeFocus option is false", ->
|
|
it "does not focus the active pane", ->
|
|
editSession = rootView.open('b', changeFocus: false)
|
|
expect(activePane.focus).not.toHaveBeenCalled()
|
|
|
|
describe "window:toggle-invisibles event", ->
|
|
it "shows/hides invisibles in all open and future editors", ->
|
|
rootView.height(200)
|
|
rootView.attachToDom()
|
|
rightEditor = rootView.getActiveView()
|
|
rightEditor.setText(" \t ")
|
|
leftEditor = rightEditor.splitLeft()
|
|
expect(rightEditor.find(".line:first").text()).toBe " "
|
|
expect(leftEditor.find(".line:first").text()).toBe " "
|
|
|
|
withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"
|
|
|
|
rootView.trigger "window:toggle-invisibles"
|
|
expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
|
expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
|
|
|
lowerLeftEditor = leftEditor.splitDown()
|
|
expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
|
|
|
rootView.trigger "window:toggle-invisibles"
|
|
expect(rightEditor.find(".line:first").text()).toBe " "
|
|
expect(leftEditor.find(".line:first").text()).toBe " "
|
|
|
|
lowerRightEditor = rightEditor.splitDown()
|
|
expect(lowerRightEditor.find(".line:first").text()).toBe " "
|
|
|
|
describe ".eachEditor(callback)", ->
|
|
beforeEach ->
|
|
rootView.attachToDom()
|
|
|
|
it "invokes the callback for existing editor", ->
|
|
count = 0
|
|
callbackEditor = null
|
|
callback = (editor) ->
|
|
callbackEditor = editor
|
|
count++
|
|
rootView.eachEditor(callback)
|
|
expect(count).toBe 1
|
|
expect(callbackEditor).toBe rootView.getActiveView()
|
|
|
|
it "invokes the callback for new editor", ->
|
|
count = 0
|
|
callbackEditor = null
|
|
callback = (editor) ->
|
|
callbackEditor = editor
|
|
count++
|
|
|
|
rootView.eachEditor(callback)
|
|
count = 0
|
|
callbackEditor = null
|
|
rootView.getActiveView().splitRight()
|
|
expect(count).toBe 1
|
|
expect(callbackEditor).toBe rootView.getActiveView()
|
|
|
|
describe ".eachBuffer(callback)", ->
|
|
beforeEach ->
|
|
rootView.attachToDom()
|
|
|
|
it "invokes the callback for existing buffer", ->
|
|
count = 0
|
|
callbackBuffer = null
|
|
callback = (buffer) ->
|
|
callbackBuffer = buffer
|
|
count++
|
|
rootView.eachBuffer(callback)
|
|
expect(count).toBe 1
|
|
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
|
|
|
|
it "invokes the callback for new buffer", ->
|
|
count = 0
|
|
callbackBuffer = null
|
|
callback = (buffer) ->
|
|
callbackBuffer = buffer
|
|
count++
|
|
|
|
rootView.eachBuffer(callback)
|
|
count = 0
|
|
callbackBuffer = null
|
|
rootView.open(require.resolve('fixtures/sample.txt'))
|
|
expect(count).toBe 1
|
|
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
|