Merge pull request #1455 from atom/ns-workspace-specs-cleanup

Cleanup workspace and its specs
This commit is contained in:
Nathan Sobo
2014-01-28 17:38:58 -08:00
5 changed files with 251 additions and 275 deletions

195
spec/workspace-spec.coffee Normal file
View File

@@ -0,0 +1,195 @@
Workspace = require '../src/workspace'
describe "Workspace", ->
workspace = null
beforeEach ->
atom.project.setPath(atom.project.resolve('dir'))
workspace = new Workspace
describe "::open(uri)", ->
beforeEach ->
spyOn(workspace.activePane, 'activate')
describe "when called without a uri", ->
it "adds and activates an empty editor on the active pane", ->
editor = null
waitsForPromise ->
workspace.open().then (o) -> editor = o
runs ->
expect(editor.getPath()).toBeUndefined()
expect(workspace.activePane.items).toEqual [editor]
expect(workspace.activePaneItem).toBe editor
expect(workspace.activePane.activate).toHaveBeenCalled()
describe "when called with a uri", ->
describe "when the active pane already has an editor for the given uri", ->
it "activates the existing editor on the active pane", ->
editor1 = workspace.openSync('a')
editor2 = workspace.openSync('b')
editor = null
waitsForPromise ->
workspace.open('a').then (o) -> editor = o
runs ->
expect(editor).toBe editor1
expect(workspace.activePaneItem).toBe editor
expect(workspace.activePane.activate).toHaveBeenCalled()
describe "when the active pane does not have an editor for the given uri", ->
it "adds and activates a new editor for the given path on the active pane", ->
editor = null
waitsForPromise ->
workspace.open('a').then (o) -> editor = o
runs ->
expect(editor.getUri()).toBe 'a'
expect(workspace.activePaneItem).toBe editor
expect(workspace.activePane.items).toEqual [editor]
expect(workspace.activePane.activate).toHaveBeenCalled()
describe "::openSync(uri, options)", ->
[activePane, initialItemCount] = []
beforeEach ->
activePane = workspace.activePane
spyOn(activePane, 'activate')
initialItemCount = activePane.items.length
describe "when called without a uri", ->
it "adds and activates an empty editor on the active pane", ->
editor = workspace.openSync()
expect(activePane.items.length).toBe initialItemCount + 1
expect(activePane.activeItem).toBe editor
expect(editor.getPath()).toBeUndefined()
expect(activePane.activate).toHaveBeenCalled()
describe "when called with a uri", ->
describe "when the active pane already has an editor for the given uri", ->
it "activates the existing editor on the active pane", ->
editor1 = workspace.openSync('a')
editor2 = workspace.openSync('b')
expect(activePane.activeItem).toBe editor2
expect(activePane.items.length).toBe 2
editor = workspace.openSync(editor1.getPath())
expect(editor).toBe editor1
expect(activePane.activeItem).toBe editor
expect(activePane.activate).toHaveBeenCalled()
expect(activePane.items.length).toBe 2
describe "when the active pane does not have an editor for the given uri", ->
it "adds and activates a new editor for the given path on the active pane", ->
editor = workspace.openSync('a')
expect(activePane.items.length).toBe 1
expect(activePane.activeItem).toBe editor
expect(activePane.activate).toHaveBeenCalled()
describe "when the 'activatePane' option is false", ->
it "does not activate the active pane", ->
workspace.openSync('b', activatePane: false)
expect(activePane.activate).not.toHaveBeenCalled()
describe "::openSingletonSync(uri, options)", ->
describe "when an editor for the given uri is already open on the active pane", ->
it "activates the existing editor", ->
editor1 = workspace.openSync('a')
editor2 = workspace.openSync('b')
expect(workspace.activePaneItem).toBe editor2
workspace.openSingletonSync('a')
expect(workspace.activePaneItem).toBe editor1
describe "when an editor for the given uri is already open on an inactive pane", ->
it "activates the existing editor on the inactive pane, then activates that pane", ->
editor1 = workspace.openSync('a')
pane1 = workspace.activePane
pane2 = workspace.activePane.splitRight()
editor2 = workspace.openSync('b')
expect(workspace.activePaneItem).toBe editor2
workspace.openSingletonSync('a')
expect(workspace.activePane).toBe pane1
expect(workspace.activePaneItem).toBe editor1
describe "when no editor for the given uri is open in any pane", ->
it "opens an editor for the given uri in the active pane", ->
editor1 = workspace.openSingletonSync('a')
expect(workspace.activePaneItem).toBe editor1
describe "when the 'split' option is 'left'", ->
it "opens the editor in the leftmost pane of the current pane axis", ->
pane1 = workspace.activePane
pane2 = pane1.splitRight()
expect(workspace.activePane).toBe pane2
editor1 = workspace.openSingletonSync('a', split: 'left')
expect(workspace.activePane).toBe pane1
expect(pane1.items).toEqual [editor1]
expect(pane2.items).toEqual []
describe "when the 'split' option is 'right'", ->
describe "when the active pane is in a horizontal pane axis", ->
it "activates the editor on the rightmost pane of the current pane axis", ->
pane1 = workspace.activePane
pane2 = pane1.splitRight()
pane1.activate()
editor1 = workspace.openSingletonSync('a', split: 'right')
expect(workspace.activePane).toBe pane2
expect(pane2.items).toEqual [editor1]
expect(pane1.items).toEqual []
describe "when the active pane is not in a horizontal pane axis", ->
it "splits the current pane to the right, then activates the editor on the right pane", ->
pane1 = workspace.activePane
editor1 = workspace.openSingletonSync('a', split: 'right')
pane2 = workspace.activePane
expect(workspace.paneContainer.root.children).toEqual [pane1, pane2]
expect(pane2.items).toEqual [editor1]
expect(pane1.items).toEqual []
describe "::reopenItemSync()", ->
it "opens the uri associated with the last closed pane that isn't currently open", ->
pane = workspace.activePane
workspace.openSync('a')
workspace.openSync('b')
workspace.openSync('file1')
workspace.openSync()
# does not reopen items with no uri
expect(workspace.activePaneItem.getUri()).toBeUndefined()
pane.destroyActiveItem()
workspace.reopenItemSync()
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
# destroy all items
expect(workspace.activePaneItem.getUri()).toBe 'file1'
pane.destroyActiveItem()
expect(workspace.activePaneItem.getUri()).toBe 'b'
pane.destroyActiveItem()
expect(workspace.activePaneItem.getUri()).toBe 'a'
pane.destroyActiveItem()
# reopens items with uris
expect(workspace.activePaneItem).toBeUndefined()
workspace.reopenItemSync()
expect(workspace.activePaneItem.getUri()).toBe 'a'
# does not reopen items that are already open
workspace.openSync('b')
expect(workspace.activePaneItem.getUri()).toBe 'b'
workspace.reopenItemSync()
expect(workspace.activePaneItem.getUri()).toBe 'file1'
describe "::increase/decreaseFontSize()", ->
it "increases/decreases the font size without going below 1", ->
atom.config.set('editor.fontSize', 1)
workspace.increaseFontSize()
expect(atom.config.get('editor.fontSize')).toBe 2
workspace.increaseFontSize()
expect(atom.config.get('editor.fontSize')).toBe 3
workspace.decreaseFontSize()
expect(atom.config.get('editor.fontSize')).toBe 2
workspace.decreaseFontSize()
expect(atom.config.get('editor.fontSize')).toBe 1
workspace.decreaseFontSize()
expect(atom.config.get('editor.fontSize')).toBe 1

View File

@@ -168,186 +168,6 @@ describe "WorkspaceView", ->
expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
workspaceView2.remove()
describe "font size adjustment", ->
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
fontSizeBefore = atom.config.get('editor.fontSize')
atom.workspaceView.trigger 'window:increase-font-size'
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
atom.workspaceView.trigger 'window:increase-font-size'
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 2
atom.workspaceView.trigger 'window:decrease-font-size'
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
atom.workspaceView.trigger 'window:decrease-font-size'
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore
it "does not allow the font size to be less than 1", ->
atom.config.set("editor.fontSize", 1)
atom.workspaceView.trigger 'window:decrease-font-size'
expect(atom.config.get('editor.fontSize')).toBe 1
describe ".openSync(filePath, options)", ->
[activePane, initialItemCount] = []
beforeEach ->
activePane = atom.workspaceView.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 in the active pane and focuses it", ->
editor = atom.workspaceView.openSync()
expect(activePane.getItems().length).toBe initialItemCount + 1
expect(activePane.activeItem).toBe editor
expect(editor.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 in the pane", ->
previousEditor = activePane.activeItem
editor = atom.workspaceView.openSync('b')
expect(activePane.activeItem).toBe editor
expect(editor).not.toBe previousEditor
editor = atom.workspaceView.openSync(previousEditor.getPath())
expect(editor).toBe previousEditor
expect(activePane.activeItem).toBe editor
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", ->
editor = atom.workspaceView.openSync('b')
expect(activePane.items.length).toBe 2
expect(activePane.activeItem).toBe editor
expect(activePane.focus).toHaveBeenCalled()
describe "when the changeFocus option is false", ->
it "does not focus the active pane", ->
editor = atom.workspaceView.openSync('b', changeFocus: false)
expect(activePane.focus).not.toHaveBeenCalled()
describe "when the split option is 'right'", ->
it "creates a new pane and opens the file in said pane", ->
pane1 = atom.workspaceView.getActivePane()
editor = atom.workspaceView.openSync('b', split: 'right')
pane2 = atom.workspaceView.getActivePane()
expect(pane2[0]).not.toBe pane1[0]
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
editor = atom.workspaceView.openSync('file1', split: 'right')
pane3 = atom.workspaceView.getActivePane()
expect(pane3[0]).toBe pane2[0]
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/file1')
expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
describe ".openSingletonSync(filePath, options)", ->
[pane1] = []
beforeEach ->
pane1 = atom.workspaceView.getActivePane()
it "creates a new pane and reuses the file when already open", ->
atom.workspaceView.openSingletonSync('b', split: 'right')
pane2 = atom.workspaceView.getActivePane()
expect(pane2[0]).not.toBe pane1[0]
expect(pane1.itemForUri('b')).toBeFalsy()
expect(pane2.itemForUri('b')).not.toBeFalsy()
expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
pane1.activate()
expect(atom.workspaceView.getActivePane()[0]).toBe pane1[0]
atom.workspaceView.openSingletonSync('b', split: 'right')
pane3 = atom.workspaceView.getActivePane()
expect(pane3[0]).toBe pane2[0]
expect(pane1.itemForUri('b')).toBeFalsy()
expect(pane2.itemForUri('b')).not.toBeFalsy()
expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
it "handles split: left by opening to the left pane when necessary", ->
atom.workspaceView.openSingletonSync('b', split: 'right')
pane2 = atom.workspaceView.getActivePane()
expect(pane2[0]).not.toBe pane1[0]
atom.workspaceView.openSingletonSync('file1', split: 'left')
activePane = atom.workspaceView.getActivePane()
expect(activePane[0]).toBe pane1[0]
expect(pane1.itemForUri('file1')).toBeTruthy()
expect(pane2.itemForUri('file1')).toBeFalsy()
expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
pane2.activate()
expect(atom.workspaceView.getActivePane()[0]).toBe pane2[0]
atom.workspaceView.openSingletonSync('file1', split: 'left')
activePane = atom.workspaceView.getActivePane()
expect(activePane[0]).toBe pane1[0]
expect(atom.workspaceView.panes.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
it "reuses the file when already open", ->
atom.workspaceView.openSync('b')
atom.workspaceView.openSingletonSync('b', split: 'right')
expect(atom.workspaceView.panes.find('.pane').toArray()).toEqual [pane1[0]]
describe ".open(filePath)", ->
[activePane] = []
beforeEach ->
spyOn(PaneView.prototype, 'focus')
activePane = atom.workspaceView.getActivePane()
describe "when called with no path", ->
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
editor = null
waitsForPromise ->
atom.workspaceView.open().then (o) -> editor = o
runs ->
expect(activePane.getItems().length).toBe 2
expect(activePane.activeItem).toBe editor
expect(editor.getPath()).toBeUndefined()
expect(activePane.focus).toHaveBeenCalled()
describe "when called with a path", ->
describe "when the active pane already has an item for the given path", ->
it "shows the existing edit session in the pane", ->
previousEditor = activePane.activeItem
editor = null
waitsForPromise ->
atom.workspaceView.open('b').then (o) -> editor = o
runs ->
expect(activePane.activeItem).toBe editor
expect(editor).not.toBe previousEditor
waitsForPromise ->
atom.workspaceView.open(previousEditor.getPath()).then (o) -> editor = o
runs ->
expect(editor).toBe previousEditor
expect(activePane.activeItem).toBe editor
expect(activePane.focus).toHaveBeenCalled()
describe "when the active pane does not have an existing item for the given path", ->
it "creates a new edit session for the given path in the active pane", ->
editor = null
waitsForPromise ->
atom.workspaceView.open('b').then (o) -> editor = o
runs ->
expect(activePane.activeItem).toBe editor
expect(activePane.getItems().length).toBe 2
expect(activePane.focus).toHaveBeenCalled()
describe "window:toggle-invisibles event", ->
it "shows/hides invisibles in all open and future editors", ->
atom.workspaceView.height(200)
@@ -414,39 +234,6 @@ describe "WorkspaceView", ->
atom.workspaceView.getActiveView().splitRight()
expect(count).toBe 2
describe ".reopenItemSync()", ->
it "opens the uri associated with the last closed pane that isn't currently open", ->
workspace = atom.workspaceView
pane = workspace.getActivePane()
workspace.openSync('b')
workspace.openSync('file1')
workspace.openSync()
# does not reopen items with no uri
expect(workspace.getActivePaneItem().getUri()).toBeUndefined()
pane.destroyActiveItem()
workspace.reopenItemSync()
expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined()
# destroy all items
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
pane.destroyActiveItem()
expect(workspace.getActivePaneItem().getUri()).toBe 'b'
pane.destroyActiveItem()
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
pane.destroyActiveItem()
# reopens items with uris
expect(workspace.getActivePaneItem()).toBeUndefined()
workspace.reopenItemSync()
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
# does not reopen items that are already open
workspace.openSync('b')
expect(workspace.getActivePaneItem().getUri()).toBe 'b'
workspace.reopenItemSync()
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
describe "core:close", ->
it "closes the active pane item until all that remains is a single empty pane", ->
atom.config.set('core.destroyEmptyPanes', true)
@@ -454,22 +241,3 @@ describe "WorkspaceView", ->
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 1
atom.workspaceView.trigger('core:close')
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 0
describe "core:save", ->
it "saves active editor until there are none", ->
editor = atom.project.openSync('../sample.txt')
spyOn(editor, 'save')
atom.workspaceView.getActivePane().activateItem(editor)
atom.workspaceView.trigger('core:save')
expect(editor.save).toHaveBeenCalled()
describe "core:save-as", ->
beforeEach ->
spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path')
it "saves active editor until there are none", ->
editor = atom.project.openSync('../sample.txt')
spyOn(editor, 'saveAs')
atom.workspaceView.getActivePane().activateItem(editor)
atom.workspaceView.trigger('core:save-as')
expect(editor.saveAs).toHaveBeenCalled()

View File

@@ -1,4 +1,4 @@
{find, compact, extend} = require 'underscore-plus'
{find, compact, extend, last} = require 'underscore-plus'
{dirname} = require 'path'
{Model, Sequence} = require 'theorist'
Serializable = require 'serializable'
@@ -332,3 +332,19 @@ class Pane extends Model
newPane.activate()
newPane
# Private: If the parent is a horizontal axis, returns its first child;
# otherwise this pane.
findLeftmostSibling: ->
if @parent.orientation is 'horizontal'
@parent.children[0]
else
this
# Private: If the parent is a horizontal axis, returns its last child;
# otherwise returns a new pane created by splitting this pane rightward.
findOrCreateRightmostSibling: ->
if @parent.orientation is 'horizontal'
last(@parent.children)
else
@splitRight()

View File

@@ -50,7 +50,7 @@ class WorkspaceView extends View
@delegatesProperty 'fullScreen', 'destroyedItemUris', toProperty: 'model'
@delegatesMethods 'open', 'openSync', 'openSingletonSync', 'reopenItemSync',
'saveActivePaneItem', 'saveActivePaneItemAs', 'saveAll', 'destroyActivePaneItem',
'destroyActivePane', toProperty: 'model'
'destroyActivePane', 'increaseFontSize', 'decreaseFontSize', toProperty: 'model'
@version: 4
@@ -113,12 +113,8 @@ class WorkspaceView extends View
@command 'window:install-shell-commands', => @installShellCommands()
@command 'window:run-package-specs', => ipc.sendChannel('run-package-specs', path.join(atom.project.getPath(), 'spec'))
@command 'window:increase-font-size', =>
atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1)
@command 'window:decrease-font-size', =>
fontSize = atom.config.get "editor.fontSize"
atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1
@command 'window:increase-font-size', => @increaseFontSize()
@command 'window:decrease-font-size', => @decreaseFontSize()
@command 'window:focus-next-pane', => @focusNextPane()
@command 'window:focus-previous-pane', => @focusPreviousPane()

View File

@@ -77,51 +77,45 @@ class Workspace extends Model
console.error(error.stack ? error)
# Private: Only used in specs
openSync: (uri, {changeFocus, initialLine, pane, split}={}) ->
changeFocus ?= true
pane ?= @activePane
openSync: (uri, options={}) ->
{initialLine} = options
# TODO: Remove deprecated changeFocus option
activatePane = options.activatePane ? options.changeFocus ? true
uri = atom.project.relativize(uri)
if pane
if uri
paneItem = pane.itemForUri(uri) ? atom.project.openSync(uri, {initialLine})
else
paneItem = atom.project.openSync()
if split == 'right'
panes = @getPanes()
if panes.length == 1
pane = panes[0].splitRight()
else
pane = last(panes)
else if split == 'left'
pane = @getPanes()[0]
pane.activateItem(paneItem)
if uri?
editor = @activePane.itemForUri(uri) ? atom.project.openSync(uri, {initialLine})
else
paneItem = atom.project.openSync(uri, {initialLine})
pane = new Pane(items: [paneItem])
@paneContainer.root = pane
editor = atom.project.openSync()
@itemOpened(paneItem)
pane.activate() if changeFocus
paneItem
@activePane.activateItem(editor)
@itemOpened(editor)
@activePane.activate() if activatePane
editor
# Public: Synchronously open an editor for the given URI or activate an existing
# editor in any pane if one already exists.
openSingletonSync: (uri, {changeFocus, initialLine, split}={}) ->
changeFocus ?= true
openSingletonSync: (uri, options={}) ->
{initialLine, split} = options
# TODO: Remove deprecated changeFocus option
activatePane = options.activatePane ? options.changeFocus ? true
uri = atom.project.relativize(uri)
pane = @paneContainer.paneForUri(uri)
if pane
paneItem = pane.itemForUri(uri)
pane.activateItem(paneItem)
pane.activate() if changeFocus
paneItem
if pane = @paneContainer.paneForUri(uri)
editor = pane.itemForUri(uri)
else
@openSync(uri, {changeFocus, initialLine, split})
pane = switch split
when 'left'
@activePane.findLeftmostSibling()
when 'right'
@activePane.findOrCreateRightmostSibling()
else
@activePane
editor = atom.project.openSync(uri, {initialLine})
pane.activateItem(editor)
pane.activate() if activatePane
editor
# Public: Reopens the last-closed item uri if it hasn't already been reopened.
reopenItemSync: ->
@@ -144,6 +138,13 @@ class Workspace extends Model
destroyActivePane: ->
@activePane?.destroy()
increaseFontSize: ->
atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1)
decreaseFontSize: ->
fontSize = atom.config.get("editor.fontSize")
atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1
# Private: Removes the item's uri from the list of potential items to reopen.
itemOpened: (item) ->
if uri = item.getUri?()