From c331723c55899528494f31de47c416a706b9724c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 17:00:32 -0700 Subject: [PATCH 01/18] Move ::activateItem specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 25 ++++++++ spec/pane-view-spec.coffee | 125 +++++++++++++------------------------ 2 files changed, 69 insertions(+), 81 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 0c15683a4..ad0798ebc 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -4,6 +4,31 @@ PaneAxis = require '../src/pane-axis' PaneContainer = require '../src/pane-container' describe "Pane", -> + class Item + constructor: (@name) -> + + describe "construction", -> + it "sets the active item to the first item", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + expect(pane.activeItem).toBe pane.items[0] + + describe "::activateItem(item)", -> + pane = null + + beforeEach -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + + it "changes the active item to the current item", -> + expect(pane.activeItem).toBe pane.items[0] + pane.activateItem(pane.items[1]) + expect(pane.activeItem).toBe pane.items[1] + + it "adds the given item if it isn't present in ::items", -> + item = new Item("C") + pane.activateItem(item) + expect(item in pane.items).toBe true + expect(pane.activeItem).toBe item + describe "split methods", -> [pane1, container] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index acbc7323a..8db8580e5 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -5,7 +5,7 @@ path = require 'path' temp = require 'temp' describe "PaneView", -> - [container, view1, view2, editor1, editor2, pane] = [] + [container, view1, view2, editor1, editor2, pane, paneModel] = [] class TestView extends View @deserialize: ({id, text}) -> new TestView({id, text}) @@ -23,120 +23,83 @@ describe "PaneView", -> editor1 = atom.project.openSync('sample.js') editor2 = atom.project.openSync('sample.txt') pane = new PaneView(view1, editor1, view2, editor2) + paneModel = pane.model container.setRoot(pane) afterEach -> atom.deserializers.remove(TestView) - describe "::initialize(items...)", -> - it "displays the first item in the pane", -> - expect(pane.itemViews.find('#view-1')).toExist() - - describe "::activateItem(item)", -> + describe "when the active pane item changes", -> it "hides all item views except the one being shown and sets the activeItem", -> expect(pane.activeItem).toBe view1 + expect(view1.css('display')).not.toBe 'none' + pane.activateItem(view2) expect(view1.css('display')).toBe 'none' expect(view2.css('display')).not.toBe 'none' - expect(pane.activeItem).toBe view2 - it "triggers 'pane:active-item-changed' if the item isn't already the activeItem", -> - pane.activate() + it "triggers 'pane:active-item-changed'", -> itemChangedHandler = jasmine.createSpy("itemChangedHandler") container.on 'pane:active-item-changed', itemChangedHandler expect(pane.activeItem).toBe view1 - pane.activateItem(view2) - pane.activateItem(view2) + paneModel.activateItem(view2) + paneModel.activateItem(view2) + expect(itemChangedHandler.callCount).toBe 1 expect(itemChangedHandler.argsForCall[0][1]).toBe view2 itemChangedHandler.reset() - pane.activateItem(editor1) + paneModel.activateItem(editor1) expect(itemChangedHandler).toHaveBeenCalled() expect(itemChangedHandler.argsForCall[0][1]).toBe editor1 itemChangedHandler.reset() - describe "if the pane's active view is focused before calling activateItem", -> - it "focuses the new active view", -> - container.attachToDom() - pane.focus() - expect(pane.activeView).not.toBe view2 - expect(pane.activeView).toMatchSelector ':focus' - pane.activateItem(view2) - expect(view2).toMatchSelector ':focus' + it "transfers focus to the new active view if the previous view was focused", -> + container.attachToDom() + pane.focus() + expect(pane.activeView).not.toBe view2 + expect(pane.activeView).toMatchSelector ':focus' + paneModel.activateItem(view2) + expect(view2).toMatchSelector ':focus' - describe "when the given item isn't yet in the items list on the pane", -> - view3 = null - beforeEach -> - view3 = new TestView(id: 'view-3', text: "View 3") - pane.activateItem(editor1) - expect(pane.getActiveItemIndex()).toBe 1 + describe "when the new activeItem is a model", -> + it "shows the item's view or creates and shows a new view for the item if none exists", -> + initialViewCount = pane.itemViews.find('.test-view').length - it "adds it to the items list after the active item", -> - pane.activateItem(view3) - expect(pane.getItems()).toEqual [view1, editor1, view3, view2, editor2] - expect(pane.activeItem).toBe view3 - expect(pane.getActiveItemIndex()).toBe 2 + model1 = + id: 'test-model-1' + text: 'Test Model 1' + serialize: -> {@id, @text} + getViewClass: -> TestView - it "triggers the 'item-added' event with the item and its index before the 'active-item-changed' event", -> - events = [] - container.on 'pane:item-added', (e, item, index) -> events.push(['pane:item-added', item, index]) - container.on 'pane:active-item-changed', (e, item) -> events.push(['pane:active-item-changed', item]) - pane.activateItem(view3) - expect(events).toEqual [['pane:item-added', view3, 2], ['pane:active-item-changed', view3]] + model2 = + id: 'test-model-2' + text: 'Test Model 2' + serialize: -> {@id, @text} + getViewClass: -> TestView - describe "when showing a model item", -> - describe "when no view has yet been appended for that item", -> - it "appends and shows a view to display the item based on its `.getViewClass` method", -> - pane.activateItem(editor1) - editorView = pane.activeView - expect(editorView.css('display')).not.toBe 'none' - expect(editorView.editor).toBe editor1 + paneModel.activateItem(model1) + paneModel.activateItem(model2) + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - describe "when a valid view has already been appended for another item", -> - it "multiple views are created for multiple items", -> - pane.activateItem(editor1) - pane.activateItem(editor2) - expect(pane.itemViews.find('.editor').length).toBe 2 - editorView = pane.activeView - expect(editorView.css('display')).not.toBe 'none' - expect(editorView.editor).toBe editor2 + paneModel.activatePreviousItem() + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - it "creates a new view with the item", -> - initialViewCount = pane.itemViews.find('.test-view').length + paneModel.destroyItem(model2) + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1 - model1 = - id: 'test-model-1' - text: 'Test Model 1' - serialize: -> {@id, @text} - getViewClass: -> TestView + paneModel.destroyItem(model1) + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount - model2 = - id: 'test-model-2' - text: 'Test Model 2' - serialize: -> {@id, @text} - getViewClass: -> TestView - - pane.activateItem(model1) - pane.activateItem(model2) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - - pane.activatePreviousItem() - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - - pane.destroyItem(model2) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1 - - pane.destroyItem(model1) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount - - describe "when showing a view item", -> + describe "when the new activeItem is a view", -> it "appends it to the itemViews div if it hasn't already been appended and shows it", -> expect(pane.itemViews.find('#view-2')).not.toExist() - pane.activateItem(view2) + paneModel.activateItem(view2) expect(pane.itemViews.find('#view-2')).toExist() - expect(pane.activeView).toBe view2 + paneModel.activateItem(view1) + paneModel.activateItem(view2) + expect(pane.itemViews.find('#view-2').length).toBe 1 describe "::destroyItem(item)", -> describe "if the item is not modified", -> From 9977884a2c73e34a89dbda3fce89c605342ff084 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 17:30:38 -0700 Subject: [PATCH 02/18] Move ::destroyItem specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 102 ++++++++++++++++++++++++++++------ spec/pane-view-spec.coffee | 109 ++++--------------------------------- 2 files changed, 96 insertions(+), 115 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index ad0798ebc..9441ef976 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -4,7 +4,7 @@ PaneAxis = require '../src/pane-axis' PaneContainer = require '../src/pane-container' describe "Pane", -> - class Item + class Item extends Model constructor: (@name) -> describe "construction", -> @@ -29,6 +29,91 @@ describe "Pane", -> expect(item in pane.items).toBe true expect(pane.activeItem).toBe item + describe "::destroyItem(item)", -> + [pane, item1, item2, item3] = [] + + beforeEach -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + + it "removes the item from the items list and activates the next item if it was the active item", -> + expect(pane.activeItem).toBe item1 + pane.destroyItem(item2) + expect(item2 in pane.items).toBe false + expect(pane.activeItem).toBe item1 + + pane.destroyItem(item1) + expect(item1 in pane.items).toBe false + expect(pane.activeItem).toBe item3 + + it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", -> + pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") + pane.destroyItem(item2) + expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true) + + describe "if the item is modified", -> + itemUri = null + + beforeEach -> + item1.shouldPromptToSave = -> true + item1.save = jasmine.createSpy("save") + item1.saveAs = jasmine.createSpy("saveAs") + item1.getUri = -> itemUri + + describe "if the [Save] option is selected", -> + describe "when the item has a uri", -> + it "saves the item before destroying it", -> + itemUri = "test" + spyOn(atom, 'confirm').andReturn(0) + pane.destroyItem(item1) + + expect(item1.save).toHaveBeenCalled() + expect(item1 in pane.items).toBe false + expect(item1.isDestroyed()).toBe true + + describe "when the item has no uri", -> + it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", -> + itemUri = null + + spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path") + spyOn(atom, 'confirm').andReturn(0) + pane.destroyItem(item1) + + expect(atom.showSaveDialogSync).toHaveBeenCalled() + expect(item1.saveAs).toHaveBeenCalledWith("/selected/path") + expect(item1 in pane.items).toBe false + expect(item1.isDestroyed()).toBe true + + describe "if the [Don't Save] option is selected", -> + it "removes and destroys the item without saving it", -> + spyOn(atom, 'confirm').andReturn(2) + pane.destroyItem(item1) + + expect(item1.save).not.toHaveBeenCalled() + expect(item1 in pane.items).toBe false + expect(item1.isDestroyed()).toBe true + + describe "if the [Cancel] option is selected", -> + it "does not save, remove, or destroy the item", -> + spyOn(atom, 'confirm').andReturn(1) + pane.destroyItem(item1) + + expect(item1.save).not.toHaveBeenCalled() + expect(item1 in pane.items).toBe true + expect(item1.isDestroyed()).toBe false + + describe "when the last item is destroyed", -> + it "destroys the pane", -> + pane.destroyItem(item) for item in pane.getItems() + expect(pane.isDestroyed()).toBe true + + describe "when an item emits a destroyed event", -> + it "removes it from the list of items", -> + pane = new Pane(items: [new Model, new Model, new Model]) + [item1, item2, item3] = pane.items + pane.items[1].destroy() + expect(pane.items).toEqual [item1, item3] + describe "split methods", -> [pane1, container] = [] @@ -109,21 +194,6 @@ describe "Pane", -> pane2 = pane1.splitRight() expect(pane2.focused).toBe true - describe "::destroyItem(item)", -> - describe "when the last item is destroyed", -> - it "destroys the pane", -> - pane = new Pane(items: ["A", "B"]) - pane.destroyItem("A") - pane.destroyItem("B") - expect(pane.isDestroyed()).toBe true - - describe "when an item emits a destroyed event", -> - it "removes it from the list of items", -> - pane = new Pane(items: [new Model, new Model, new Model]) - [item1, item2, item3] = pane.items - pane.items[1].destroy() - expect(pane.items).toEqual [item1, item3] - describe "::destroy()", -> [pane1, container] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 8db8580e5..86076bb83 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -101,115 +101,26 @@ describe "PaneView", -> paneModel.activateItem(view2) expect(pane.itemViews.find('#view-2').length).toBe 1 - describe "::destroyItem(item)", -> - describe "if the item is not modified", -> - it "removes the item and tries to call destroy on it", -> - pane.destroyItem(editor2) - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "if the item is modified", -> - beforeEach -> - jasmine.unspy(editor2, 'shouldPromptToSave') - spyOn(editor2, 'save') - spyOn(editor2, 'saveAs') - - editor2.insertText('a') - expect(editor2.isModified()).toBeTruthy() - - describe "if the [Save] option is selected", -> - describe "when the item has a uri", -> - it "saves the item before removing and destroying it", -> - spyOn(atom, 'confirm').andReturn(0) - pane.destroyItem(editor2) - - expect(editor2.save).toHaveBeenCalled() - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "when the item has no uri", -> - it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", -> - editor2.buffer.setPath(undefined) - - spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path") - spyOn(atom, 'confirm').andReturn(0) - pane.destroyItem(editor2) - - expect(atom.showSaveDialogSync).toHaveBeenCalled() - - expect(editor2.saveAs).toHaveBeenCalledWith("/selected/path") - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "if the [Don't Save] option is selected", -> - it "removes and destroys the item without saving it", -> - spyOn(atom, 'confirm').andReturn(2) - pane.destroyItem(editor2) - - expect(editor2.save).not.toHaveBeenCalled() - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "if the [Cancel] option is selected", -> - it "does not save, remove, or destroy the item", -> - spyOn(atom, 'confirm').andReturn(1) - pane.destroyItem(editor2) - - expect(editor2.save).not.toHaveBeenCalled() - expect(pane.getItems().indexOf(editor2)).not.toBe -1 - expect(editor2.isDestroyed()).toBe false - - it "removes the item's associated view", -> - view1.remove = (selector, keepData) -> @wasRemoved = not keepData - pane.destroyItem(view1) - expect(view1.wasRemoved).toBe true - - it "removes the item from the items list and shows the next item if it was showing", -> - pane.destroyItem(view1) - expect(pane.getItems()).toEqual [editor1, view2, editor2] - expect(pane.activeItem).toBe editor1 - - pane.activateItem(editor2) - pane.destroyItem(editor2) - expect(pane.getItems()).toEqual [editor1, view2] - expect(pane.activeItem).toBe editor1 - - it "triggers 'pane:item-removed' with the item and its former index", -> + describe "when an item is destroyed", -> + it "triggers the 'pane:item-removed' event with the item and its former index", -> itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") pane.on 'pane:item-removed', itemRemovedHandler - pane.destroyItem(editor1) + paneModel.destroyItem(editor1) expect(itemRemovedHandler).toHaveBeenCalled() expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1] - describe "when removing the last item", -> - it "removes the pane", -> - pane.destroyItem(item) for item in pane.getItems() - expect(pane.hasParent()).toBeFalsy() - - describe "when the pane is focused", -> - it "shifts focus to the next pane", -> - expect(container.getRoot()).toBe pane - container.attachToDom() - pane2 = pane.splitRight(new TestView(id: 'view-3', text: 'View 3')) - pane.focus() - expect(pane).toMatchSelector(':has(:focus)') - pane.destroyItem(item) for item in pane.getItems() - expect(pane2).toMatchSelector ':has(:focus)' - - describe "when the item is a view", -> + describe "when the destroyed item is a view", -> it "removes the item from the 'item-views' div", -> expect(view1.parent()).toMatchSelector pane.itemViews - pane.destroyItem(view1) + paneModel.destroyItem(view1) expect(view1.parent()).not.toMatchSelector pane.itemViews - describe "when the item is a model", -> - it "removes the associated view only when all items that require it have been removed", -> - pane.activateItem(editor1) - pane.activateItem(editor2) - pane.destroyItem(editor2) - expect(pane.itemViews.find('.editor')).toExist() + describe "when the destroyed item is a model", -> + it "removes the associated view", -> + paneModel.activateItem(editor1) + expect(pane.itemViews.find('.editor').length).toBe 1 pane.destroyItem(editor1) - expect(pane.itemViews.find('.editor')).not.toExist() + expect(pane.itemViews.find('.editor').length).toBe 0 describe "::moveItem(item, index)", -> it "moves the item to the given index and emits a 'pane:item-moved' event with the item and the new index", -> From 2ee6469b1715669cddccfa58b6d806cd7db78c16 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:15:11 -0700 Subject: [PATCH 03/18] Move ::moveItem specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 20 ++++++++++++++++++++ spec/pane-view-spec.coffee | 24 ++++-------------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 9441ef976..8ab7d30e3 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -114,6 +114,26 @@ describe "Pane", -> pane.items[1].destroy() expect(pane.items).toEqual [item1, item3] + describe "::moveItem(item, index)", -> + it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) + [item1, item2, item3, item4] = pane.items + pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") + + pane.moveItem(item1, 2) + expect(pane.getItems()).toEqual [item2, item3, item1, item4] + expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2) + itemMovedHandler.reset() + + pane.moveItem(item2, 3) + expect(pane.getItems()).toEqual [item3, item1, item4, item2] + expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3) + itemMovedHandler.reset() + + pane.moveItem(item2, 1) + expect(pane.getItems()).toEqual [item3, item2, item1, item4] + expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1) + describe "split methods", -> [pane1, container] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 86076bb83..2584081c5 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -122,28 +122,12 @@ describe "PaneView", -> pane.destroyItem(editor1) expect(pane.itemViews.find('.editor').length).toBe 0 - describe "::moveItem(item, index)", -> - it "moves the item to the given index and emits a 'pane:item-moved' event with the item and the new index", -> - itemMovedHandler = jasmine.createSpy("itemMovedHandler") - pane.on 'pane:item-moved', itemMovedHandler - - pane.moveItem(view1, 2) - expect(pane.getItems()).toEqual [editor1, view2, view1, editor2] + describe "when an item is moved within the same pane", -> + it "emits a 'pane:item-moved' event with the item and the new index", -> + pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") + paneModel.moveItem(view1, 2) expect(itemMovedHandler).toHaveBeenCalled() expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2] - itemMovedHandler.reset() - - pane.moveItem(editor1, 3) - expect(pane.getItems()).toEqual [view2, view1, editor2, editor1] - expect(itemMovedHandler).toHaveBeenCalled() - expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 3] - itemMovedHandler.reset() - - pane.moveItem(editor1, 1) - expect(pane.getItems()).toEqual [view2, editor1, view1, editor2] - expect(itemMovedHandler).toHaveBeenCalled() - expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1] - itemMovedHandler.reset() describe "::moveItemToPane(item, pane, index)", -> [pane2, view3] = [] From 2ef74de0f8b12686cb2b68e531655abbf89d5a45 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:26:47 -0700 Subject: [PATCH 04/18] Move ::moveItemToPane specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 23 ++++++++++++++++++++++ spec/pane-view-spec.coffee | 39 ++++++++------------------------------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 8ab7d30e3..c85e3ff48 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -134,6 +134,29 @@ describe "Pane", -> expect(pane.getItems()).toEqual [item3, item2, item1, item4] expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1) + describe "::moveItemToPane(item, pane, index)", -> + [container, pane1, pane2] = [] + [item1, item2, item3, item4, item5] = [] + + beforeEach -> + pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + container = new PaneContainer(root: pane1) + pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")]) + [item1, item2, item3] = pane1.items + [item4, item5] = pane2.items + + it "moves the item to the given pane at the given index", -> + pane1.moveItemToPane(item2, pane2, 1) + expect(pane1.items).toEqual [item1, item3] + expect(pane2.items).toEqual [item4, item2, item5] + + describe "when the moved item the last item in the source pane", -> + it "destroys the pane, but not the item", -> + item5.destroy() + pane2.moveItemToPane(item4, pane1, 0) + expect(pane2.isDestroyed()).toBe true + expect(item4.isDestroyed()).toBe false + describe "split methods", -> [pane1, container] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 2584081c5..0064a76f4 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -129,37 +129,14 @@ describe "PaneView", -> expect(itemMovedHandler).toHaveBeenCalled() expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2] - describe "::moveItemToPane(item, pane, index)", -> - [pane2, view3] = [] - - beforeEach -> - view3 = new TestView(id: 'view-3', text: "View 3") - pane2 = pane.splitRight(view3) - - it "moves the item to the given pane at the given index", -> - pane.moveItemToPane(view1, pane2, 1) - expect(pane.getItems()).toEqual [editor1, view2, editor2] - expect(pane2.getItems()).toEqual [view3, view1] - - describe "when it is the last item on the source pane", -> - it "removes the source pane, but does not destroy the item", -> - pane.destroyItem(view1) - pane.destroyItem(view2) - pane.destroyItem(editor2) - - expect(pane.getItems()).toEqual [editor1] - pane.moveItemToPane(editor1, pane2, 1) - - expect(pane.hasParent()).toBeFalsy() - expect(pane2.getItems()).toEqual [view3, editor1] - expect(editor1.isDestroyed()).toBe false - - describe "when the item is a jQuery object", -> - it "preserves data by detaching instead of removing", -> - view1.data('preservative', 1234) - pane.moveItemToPane(view1, pane2, 1) - pane2.activateItemAtIndex(1) - expect(pane2.activeView.data('preservative')).toBe 1234 + describe "when an item is moved to another pane", -> + it "detaches the item's view rather than removing it", -> + paneModel2 = paneModel.splitRight() + view1.data('preservative', 1234) + paneModel.moveItemToPane(view1, paneModel2, 1) + expect(view1.data('preservative')).toBe 1234 + paneModel2.activateItemAtIndex(1) + expect(view1.data('preservative')).toBe 1234 describe "pane:close", -> it "destroys all items and removes the pane", -> From a6d8f588c3cbf0bb156ff5974955b68501225a6b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:29:01 -0700 Subject: [PATCH 05/18] :lipstick: --- spec/pane-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index c85e3ff48..53cc9bb12 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -109,7 +109,7 @@ describe "Pane", -> describe "when an item emits a destroyed event", -> it "removes it from the list of items", -> - pane = new Pane(items: [new Model, new Model, new Model]) + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) [item1, item2, item3] = pane.items pane.items[1].destroy() expect(pane.items).toEqual [item1, item3] From cf6fc22c875f426d7ed46512ba44817ad7b10c47 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:31:13 -0700 Subject: [PATCH 06/18] Add spec for Pane::destroyItems to pane-spec --- spec/pane-spec.coffee | 11 +++++++++++ spec/pane-view-spec.coffee | 8 -------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 53cc9bb12..00861a5de 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -107,6 +107,17 @@ describe "Pane", -> pane.destroyItem(item) for item in pane.getItems() expect(pane.isDestroyed()).toBe true + describe "::destroyItems()", -> + it "destroys all items and the pane", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.destroyItems() + expect(item1.isDestroyed()).toBe true + expect(item2.isDestroyed()).toBe true + expect(item3.isDestroyed()).toBe true + expect(pane.isDestroyed()).toBe true + expect(pane.items).toEqual [] + describe "when an item emits a destroyed event", -> it "removes it from the list of items", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 0064a76f4..4ada7e21b 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -138,14 +138,6 @@ describe "PaneView", -> paneModel2.activateItemAtIndex(1) expect(view1.data('preservative')).toBe 1234 - describe "pane:close", -> - it "destroys all items and removes the pane", -> - pane.activateItem(editor1) - pane.trigger 'pane:close' - expect(pane.hasParent()).toBeFalsy() - expect(editor2.isDestroyed()).toBe true - expect(editor1.isDestroyed()).toBe true - describe "pane:close-other-items", -> it "destroys all items except the current", -> pane.activateItem(editor1) From e553fefc2570ca8ca03b658eb6de93e672bc0b32 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:32:39 -0700 Subject: [PATCH 07/18] Add spec for Pane::destroyInactiveItems to pane-spec --- spec/pane-spec.coffee | 8 ++++++++ spec/pane-view-spec.coffee | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 00861a5de..89bb60ca7 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -125,6 +125,14 @@ describe "Pane", -> pane.items[1].destroy() expect(pane.items).toEqual [item1, item3] + describe "::destroyInactiveItems()", -> + it "destroys all items but the active item", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.activateItem(item2) + pane.destroyInactiveItems() + expect(pane.items).toEqual [item2] + describe "::moveItem(item, index)", -> it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 4ada7e21b..d3c819301 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -138,13 +138,6 @@ describe "PaneView", -> paneModel2.activateItemAtIndex(1) expect(view1.data('preservative')).toBe 1234 - describe "pane:close-other-items", -> - it "destroys all items except the current", -> - pane.activateItem(editor1) - pane.trigger 'pane:close-other-items' - expect(editor2.isDestroyed()).toBe true - expect(pane.getItems()).toEqual [editor1] - describe "::saveActiveItem()", -> describe "when the current item has a uri", -> describe "when the current item has a save method", -> From 22c65f2407c703396fd066f5aca1a4bcd1cf91a5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:39:24 -0700 Subject: [PATCH 08/18] Move ::saveActiveItem specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 40 ++++++++++++++++++++++++++++++++++++++ spec/pane-view-spec.coffee | 36 ---------------------------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 89bb60ca7..f5b5b5e90 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -133,6 +133,46 @@ describe "Pane", -> pane.destroyInactiveItems() expect(pane.items).toEqual [item2] + describe "::saveActiveItem()", -> + [pane, activeItemUri] = [] + + beforeEach -> + pane = new Pane(items: [new Item("A")]) + spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') + pane.activeItem.getUri = -> activeItemUri + + describe "when the active item has a uri", -> + beforeEach -> + activeItemUri = "test" + + describe "when the active item has a save method", -> + it "saves the current item", -> + pane.activeItem.save = jasmine.createSpy("save") + pane.saveActiveItem() + expect(pane.activeItem.save).toHaveBeenCalled() + + describe "when the current item has no save method", -> + it "does nothing", -> + expect(pane.activeItem.save).toBeUndefined() + pane.saveActiveItem() + + describe "when the current item has no uri", -> + beforeEach -> + activeItemUri = null + + describe "when the current item has a saveAs method", -> + it "opens a save dialog and saves the current item as the selected path", -> + pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.saveActiveItem() + expect(atom.showSaveDialogSync).toHaveBeenCalled() + expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + + describe "when the current item has no saveAs method", -> + it "does nothing", -> + expect(pane.activeItem.saveAs).toBeUndefined() + pane.saveActiveItem() + expect(atom.showSaveDialogSync).not.toHaveBeenCalled() + describe "::moveItem(item, index)", -> it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index d3c819301..8564a7579 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -138,42 +138,6 @@ describe "PaneView", -> paneModel2.activateItemAtIndex(1) expect(view1.data('preservative')).toBe 1234 - describe "::saveActiveItem()", -> - describe "when the current item has a uri", -> - describe "when the current item has a save method", -> - it "saves the current item", -> - spyOn(editor2, 'save') - pane.activateItem(editor2) - pane.saveActiveItem() - expect(editor2.save).toHaveBeenCalled() - - describe "when the current item has no save method", -> - it "does nothing", -> - pane.activeItem.getUri = -> 'you are eye' - expect(pane.activeItem.save).toBeUndefined() - pane.saveActiveItem() - - describe "when the current item has no uri", -> - beforeEach -> - spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') - - describe "when the current item has a saveAs method", -> - it "opens a save dialog and saves the current item as the selected path", -> - newEditor = atom.project.openSync() - spyOn(newEditor, 'saveAs') - pane.activateItem(newEditor) - - pane.saveActiveItem() - - expect(atom.showSaveDialogSync).toHaveBeenCalled() - expect(newEditor.saveAs).toHaveBeenCalledWith('/selected/path') - - describe "when the current item has no saveAs method", -> - it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() - pane.saveActiveItem() - expect(atom.showSaveDialogSync).not.toHaveBeenCalled() - describe "::saveActiveItemAs()", -> beforeEach -> spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') From 243c4efe206fc1fcf17553f2809f9e796764e902 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:43:09 -0700 Subject: [PATCH 09/18] Move ::saveActiveItemAs specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 21 +++++++++++++++++++++ spec/pane-view-spec.coffee | 20 -------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index f5b5b5e90..10a405f94 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -173,6 +173,27 @@ describe "Pane", -> pane.saveActiveItem() expect(atom.showSaveDialogSync).not.toHaveBeenCalled() + describe "::saveActiveItemAs()", -> + [pane, activeItemUri] = [] + + beforeEach -> + pane = new Pane(items: [new Item("A")]) + spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') + pane.activeItem.getUri = -> "test" + + describe "when the current item has a saveAs method", -> + it "opens the save dialog and calls saveAs on the item with the selected path", -> + pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.saveActiveItemAs() + expect(atom.showSaveDialogSync).toHaveBeenCalledWith(activeItemUri) + expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + + describe "when the current item does not have a saveAs method", -> + it "does nothing", -> + expect(pane.activeItem.saveAs).toBeUndefined() + pane.saveActiveItemAs() + expect(atom.showSaveDialogSync).not.toHaveBeenCalled() + describe "::moveItem(item, index)", -> it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 8564a7579..4eb10af31 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -138,26 +138,6 @@ describe "PaneView", -> paneModel2.activateItemAtIndex(1) expect(view1.data('preservative')).toBe 1234 - describe "::saveActiveItemAs()", -> - beforeEach -> - spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') - - describe "when the current item has a saveAs method", -> - it "opens the save dialog and calls saveAs on the item with the selected path", -> - spyOn(editor2, 'saveAs') - pane.activateItem(editor2) - - pane.saveActiveItemAs() - - expect(atom.showSaveDialogSync).toHaveBeenCalledWith(path.dirname(editor2.getPath())) - expect(editor2.saveAs).toHaveBeenCalledWith('/selected/path') - - describe "when the current item does not have a saveAs method", -> - it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() - pane.saveActiveItemAs() - expect(atom.showSaveDialogSync).not.toHaveBeenCalled() - describe "pane:show-next-item and pane:show-previous-item", -> it "advances forward/backward through the pane's items, looping around at either end", -> expect(pane.activeItem).toBe view1 From 83696bb9c7313414e28f27e2a8520126c00c1a4b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:46:12 -0700 Subject: [PATCH 10/18] Move ::activateNext/PreviousItem specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 15 +++++++++++++++ spec/pane-view-spec.coffee | 12 ------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 10a405f94..d41cbcdc9 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -29,6 +29,21 @@ describe "Pane", -> expect(item in pane.items).toBe true expect(pane.activeItem).toBe item + describe "::activateNextItem() and ::activatePreviousItem()", -> + it "sets the active item to the next/previous item, looping around at either end", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + + expect(pane.activeItem).toBe item1 + pane.activatePreviousItem() + expect(pane.activeItem).toBe item3 + pane.activatePreviousItem() + expect(pane.activeItem).toBe item2 + pane.activateNextItem() + expect(pane.activeItem).toBe item3 + pane.activateNextItem() + expect(pane.activeItem).toBe item1 + describe "::destroyItem(item)", -> [pane, item1, item2, item3] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 4eb10af31..910b4d6cc 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -138,18 +138,6 @@ describe "PaneView", -> paneModel2.activateItemAtIndex(1) expect(view1.data('preservative')).toBe 1234 - describe "pane:show-next-item and pane:show-previous-item", -> - it "advances forward/backward through the pane's items, looping around at either end", -> - expect(pane.activeItem).toBe view1 - pane.trigger 'pane:show-previous-item' - expect(pane.activeItem).toBe editor2 - pane.trigger 'pane:show-previous-item' - expect(pane.activeItem).toBe view2 - pane.trigger 'pane:show-next-item' - expect(pane.activeItem).toBe editor2 - pane.trigger 'pane:show-next-item' - expect(pane.activeItem).toBe view1 - describe "pane:show-item-N events", -> it "shows the (n-1)th item if it exists", -> pane.trigger 'pane:show-item-2' From d8ddd52df84d335718b67d81c370f77237df353d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 19:49:15 -0700 Subject: [PATCH 11/18] Move ::activateItemAtIndex specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 17 +++++++++++++++++ spec/pane-view-spec.coffee | 9 --------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index d41cbcdc9..8e4bd88a9 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -44,6 +44,23 @@ describe "Pane", -> pane.activateNextItem() expect(pane.activeItem).toBe item1 + describe "::activateItemAtIndex(index)", -> + it "activates the item at the given index", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.activateItemAtIndex(2) + expect(pane.activeItem).toBe item3 + pane.activateItemAtIndex(1) + expect(pane.activeItem).toBe item2 + pane.activateItemAtIndex(0) + expect(pane.activeItem).toBe item1 + + # Doesn't fail with out-of-bounds indices + pane.activateItemAtIndex(100) + expect(pane.activeItem).toBe item1 + pane.activateItemAtIndex(-1) + expect(pane.activeItem).toBe item1 + describe "::destroyItem(item)", -> [pane, item1, item2, item3] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 910b4d6cc..e37d1e716 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -138,15 +138,6 @@ describe "PaneView", -> paneModel2.activateItemAtIndex(1) expect(view1.data('preservative')).toBe 1234 - describe "pane:show-item-N events", -> - it "shows the (n-1)th item if it exists", -> - pane.trigger 'pane:show-item-2' - expect(pane.activeItem).toBe pane.itemAtIndex(1) - pane.trigger 'pane:show-item-1' - expect(pane.activeItem).toBe pane.itemAtIndex(0) - pane.trigger 'pane:show-item-9' # don't fail on out-of-bounds indices - expect(pane.activeItem).toBe pane.itemAtIndex(0) - describe "when the title of the active item changes", -> it "emits pane:active-item-title-changed", -> activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") From dd0ae8a8ea0e68d6a7aa7f9a8208b0830989a3c1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 20:09:14 -0700 Subject: [PATCH 12/18] Don't remove PaneViews when their model is destroyed They will be removed by their parent PaneContainerView or PaneAxisView, and removing them in the previous way circumvented some of our focus preservation logic. --- src/pane-view.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 78e255aa1..7b5af1ac3 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -44,8 +44,6 @@ class PaneView extends View @handleEvents() handleEvents: -> - @subscribe @model, 'destroyed', => @remove() - @subscribe @model.$activeItem, @onActiveItemChanged @subscribe @model, 'item-added', @onItemAdded @subscribe @model, 'item-removed', @onItemRemoved From 9841a3588faf8df3d9230b933fe74ba4efcab808 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 20:10:01 -0700 Subject: [PATCH 13/18] Move specs on PaneView::remove to Pane::destroy in pane-spec --- spec/pane-spec.coffee | 7 +++++ spec/pane-view-spec.coffee | 61 ++++++++++---------------------------- 2 files changed, 23 insertions(+), 45 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 8e4bd88a9..a4e39a267 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -362,6 +362,13 @@ describe "Pane", -> expect(item1.isDestroyed()).toBe true expect(item2.isDestroyed()).toBe true + describe "if the pane is active", -> + it "makes the next pane active", -> + pane2 = pane1.splitRight() + expect(pane2.isActive()).toBe true + pane2.destroy() + expect(pane1.isActive()).to + describe "if the pane's parent has more than two children", -> it "removes the pane from its parent", -> pane2 = pane1.splitRight() diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index e37d1e716..dbb427720 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -167,12 +167,7 @@ describe "PaneView", -> waitsFor -> pane.items.length == 4 - describe "::remove()", -> - it "destroys all the pane's items", -> - pane.remove() - expect(editor1.isDestroyed()).toBe true - expect(editor2.isDestroyed()).toBe true - + describe "when a pane is destroyed", -> it "triggers a 'pane:removed' event with the pane", -> removedHandler = jasmine.createSpy("removedHandler") container.on 'pane:removed', removedHandler @@ -180,52 +175,28 @@ describe "PaneView", -> expect(removedHandler).toHaveBeenCalled() expect(removedHandler.argsForCall[0][1]).toBe pane - describe "when there are other panes", -> + describe "if the destroyed pane has focus", -> [paneToLeft, paneToRight] = [] - beforeEach -> - pane.activateItem(editor1) - paneToLeft = pane.splitLeft(pane.copyActiveItem()) - paneToRight = pane.splitRight(pane.copyActiveItem()) - container.attachToDom() + describe "if it is not the last pane in the container", -> + it "focuses the next pane", -> + paneModel.activateItem(editor1) + pane2Model = paneModel.splitRight(items: [paneModel.copyActiveItem()]) + pane2 = pane2Model._view + container.attachToDom() + expect(pane.hasFocus()).toBe false + pane2Model.destroy() + expect(pane.hasFocus()).toBe true - describe "when the removed pane is active", -> - it "makes the next the next pane active and focuses it", -> - pane.activate() - pane.remove() - expect(paneToLeft.isActive()).toBeFalsy() - expect(paneToRight.isActive()).toBeTruthy() - expect(paneToRight).toMatchSelector ':has(:focus)' - - describe "when the removed pane is not active", -> - it "does not affect the active pane or the focus", -> - paneToLeft.focus() - expect(paneToLeft.isActive()).toBeTruthy() - expect(paneToRight.isActive()).toBeFalsy() - - pane.remove() - expect(paneToLeft.isActive()).toBeTruthy() - expect(paneToRight.isActive()).toBeFalsy() - expect(paneToLeft).toMatchSelector ':has(:focus)' - - describe "when it is the last pane", -> - beforeEach -> - expect(container.getPanes().length).toBe 1 - atom.workspaceView = focus: jasmine.createSpy("workspaceView.focus") - - describe "when the removed pane is focused", -> - it "calls focus on workspaceView so we don't lose focus", -> + describe "if it is the last pane in the container", -> + it "shifts focus to the workspace view", -> + atom.workspaceView = {focus: jasmine.createSpy("atom.workspaceView.focus")} container.attachToDom() pane.focus() - pane.remove() + expect(container.hasFocus()).toBe true + paneModel.destroy() expect(atom.workspaceView.focus).toHaveBeenCalled() - describe "when the removed pane is not focused", -> - it "does not call focus on root view", -> - expect(pane).not.toMatchSelector ':has(:focus)' - pane.remove() - expect(atom.workspaceView.focus).not.toHaveBeenCalled() - describe "::getNextPane()", -> it "returns the next pane if one exists, wrapping around from the last pane to the first", -> pane.activateItem(editor1) From 453e034a5fc5c8335ade8b6feca88ab5bff3d5cf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 20:16:28 -0700 Subject: [PATCH 14/18] Simplify PaneView split specs because it's largely covered at the model --- spec/pane-view-spec.coffee | 73 ++++++-------------------------------- 1 file changed, 11 insertions(+), 62 deletions(-) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index dbb427720..0466a81b4 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -241,73 +241,22 @@ describe "PaneView", -> expect(becameInactiveHandler.callCount).toBe 1 - describe "split methods", -> - [pane1, view3, view4] = [] - beforeEach -> + describe "when a pane is split", -> + it "builds the appropriate pane-row and pane-column views", -> pane1 = pane + pane1Model = pane.model pane.activateItem(editor1) - view3 = new TestView(id: 'view-3', text: 'View 3') - view4 = new TestView(id: 'view-4', text: 'View 4') - describe "splitRight(items...)", -> - it "builds a row if needed, then appends a new pane after itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane1.splitRight(pane1.copyActiveItem()) - expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy + pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()]) + pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()]) + pane2 = pane2Model._view + pane3 = pane3Model._view - pane3 = pane2.splitRight(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-row .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]] + expect(container.find('> .pane-row > .pane').toArray()).toEqual [pane1[0]] + expect(container.find('> .pane-row > .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]] - it "builds a row if needed, then appends a new pane after itself ", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane1.splitRight() - expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - expect(pane2.items).toEqual [] - expect(pane2.activeItem).toBeUndefined() - - pane3 = pane2.splitRight() - expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0], pane3[0]] - expect(pane3.items).toEqual [] - expect(pane3.activeItem).toBeUndefined() - - describe "splitLeft(items...)", -> - it "builds a row if needed, then appends a new pane before itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane.splitLeft(pane1.copyActiveItem()) - expect(container.find('.pane-row .pane').toArray()).toEqual [pane2[0], pane[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy - - pane3 = pane2.splitLeft(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-row .pane').toArray()).toEqual [pane3[0], pane2[0], pane[0]] - - describe "splitDown(items...)", -> - it "builds a column if needed, then appends a new pane after itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane.splitDown(pane1.copyActiveItem()) - expect(container.find('.pane-column .pane').toArray()).toEqual [pane[0], pane2[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy - - pane3 = pane2.splitDown(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-column .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]] - - describe "splitUp(items...)", -> - it "builds a column if needed, then appends a new pane before itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane.splitUp(pane1.copyActiveItem()) - expect(container.find('.pane-column .pane').toArray()).toEqual [pane2[0], pane[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy - - pane3 = pane2.splitUp(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-column .pane').toArray()).toEqual [pane3[0], pane2[0], pane[0]] + pane1Model.destroy() + expect(container.find('> .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]] describe "::itemForUri(uri)", -> it "returns the item for which a call to .getUri() returns the given uri", -> From 1ad5158f1924f34a0ee0f9b5edc5a2d781b93d34 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 20:27:30 -0700 Subject: [PATCH 15/18] Streamline active status and focus specs in pane-view-spec The specs can be simplified now that much of this logic is covered in the model specs. --- spec/pane-view-spec.coffee | 59 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 0466a81b4..1d40df4b8 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -205,41 +205,48 @@ describe "PaneView", -> expect(pane.getNextPane()).toBe pane2 expect(pane2.getNextPane()).toBe pane + describe "when the pane's active status changes", -> + [pane2, pane2Model] = [] + + beforeEach -> + pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()]) + pane2 = pane2Model._view + expect(pane2Model.isActive()).toBe true + + it "adds or removes the .active class as appropriate", -> + expect(pane).not.toHaveClass('active') + paneModel.activate() + expect(pane).toHaveClass('active') + pane2Model.activate() + expect(pane).not.toHaveClass('active') + + it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", -> + pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler") + pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler") + paneModel.activate() + + expect(becameActiveHandler.callCount).toBe 1 + expect(becameInactiveHandler.callCount).toBe 0 + + pane2Model.activate() + expect(becameActiveHandler.callCount).toBe 1 + expect(becameInactiveHandler.callCount).toBe 1 + describe "when the pane is focused", -> beforeEach -> container.attachToDom() - it "focuses the active item view", -> + it "transfers focus to the active view", -> focusHandler = jasmine.createSpy("focusHandler") pane.activeItem.on 'focus', focusHandler pane.focus() expect(focusHandler).toHaveBeenCalled() - it "triggers 'pane:became-active' if it was not previously active", -> - pane2 = pane.splitRight(view2) # Make pane inactive - - becameActiveHandler = jasmine.createSpy("becameActiveHandler") - pane.on 'pane:became-active', becameActiveHandler - expect(pane.isActive()).toBeFalsy() - pane.focusin() - expect(pane.isActive()).toBeTruthy() - pane.focusin() - - expect(becameActiveHandler.callCount).toBe 1 - - it "triggers 'pane:became-inactive' when it was previously active", -> - pane2 = pane.splitRight(view2) # Make pane inactive - - becameInactiveHandler = jasmine.createSpy("becameInactiveHandler") - pane.on 'pane:became-inactive', becameInactiveHandler - - expect(pane.isActive()).toBeFalsy() - pane.focusin() - expect(pane.isActive()).toBeTruthy() - pane.splitRight(pane.copyActiveItem()) - expect(pane.isActive()).toBeFalsy() - - expect(becameInactiveHandler.callCount).toBe 1 + it "makes the pane active", -> + paneModel.splitRight(items: [pane.copyActiveItem()]) + expect(paneModel.isActive()).toBe false + pane.focus() + expect(paneModel.isActive()).toBe true describe "when a pane is split", -> it "builds the appropriate pane-row and pane-column views", -> From 8734eab8cb4929542322fd3b90cdc6f23fee1192 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 20:38:07 -0700 Subject: [PATCH 16/18] :lipstick: Give dummy pane items ::getUri and ::getPath methods --- spec/pane-spec.coffee | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index a4e39a267..9953d55f5 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -6,6 +6,8 @@ PaneContainer = require '../src/pane-container' describe "Pane", -> class Item extends Model constructor: (@name) -> + getUri: -> @uri + getPath: -> @path describe "construction", -> it "sets the active item to the first item", -> @@ -166,16 +168,15 @@ describe "Pane", -> expect(pane.items).toEqual [item2] describe "::saveActiveItem()", -> - [pane, activeItemUri] = [] + pane = null beforeEach -> pane = new Pane(items: [new Item("A")]) spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') - pane.activeItem.getUri = -> activeItemUri describe "when the active item has a uri", -> beforeEach -> - activeItemUri = "test" + pane.activeItem.uri = "test" describe "when the active item has a save method", -> it "saves the current item", -> @@ -189,9 +190,6 @@ describe "Pane", -> pane.saveActiveItem() describe "when the current item has no uri", -> - beforeEach -> - activeItemUri = null - describe "when the current item has a saveAs method", -> it "opens a save dialog and saves the current item as the selected path", -> pane.activeItem.saveAs = jasmine.createSpy("saveAs") @@ -206,18 +204,18 @@ describe "Pane", -> expect(atom.showSaveDialogSync).not.toHaveBeenCalled() describe "::saveActiveItemAs()", -> - [pane, activeItemUri] = [] + pane = null beforeEach -> pane = new Pane(items: [new Item("A")]) spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') - pane.activeItem.getUri = -> "test" describe "when the current item has a saveAs method", -> it "opens the save dialog and calls saveAs on the item with the selected path", -> + pane.activeItem.path = __filename pane.activeItem.saveAs = jasmine.createSpy("saveAs") pane.saveActiveItemAs() - expect(atom.showSaveDialogSync).toHaveBeenCalledWith(activeItemUri) + expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__dirname) expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') describe "when the current item does not have a saveAs method", -> From d9d28b5236566b3eb407c7a3a173ca3811099048 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 20:38:18 -0700 Subject: [PATCH 17/18] Move ::itemForUri specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 10 ++++++++++ spec/pane-view-spec.coffee | 5 ----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 9953d55f5..5ea65f32b 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -224,6 +224,16 @@ describe "Pane", -> pane.saveActiveItemAs() expect(atom.showSaveDialogSync).not.toHaveBeenCalled() + describe "::itemForUri(uri)", -> + it "returns the item for which a call to .getUri() returns the given uri", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) + [item1, item2, item3] = pane.items + item1.uri = "a" + item2.uri = "b" + expect(pane.itemForUri("a")).toBe item1 + expect(pane.itemForUri("b")).toBe item2 + expect(pane.itemForUri("bogus")).toBeUndefined() + describe "::moveItem(item, index)", -> it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 1d40df4b8..17638264e 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -265,11 +265,6 @@ describe "PaneView", -> pane1Model.destroy() expect(container.find('> .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]] - describe "::itemForUri(uri)", -> - it "returns the item for which a call to .getUri() returns the given uri", -> - expect(pane.itemForUri(editor1.getUri())).toBe editor1 - expect(pane.itemForUri(editor2.getUri())).toBe editor2 - describe "serialization", -> it "can serialize and deserialize the pane and all its items", -> newPane = new PaneView(pane.model.testSerialization()) From 1c6564f7a5c460423f987fdd37dbc35544be9fed Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Jan 2014 21:39:47 -0700 Subject: [PATCH 18/18] Move non-focus serialization specs from pane-view-spec to pane-spec --- spec/pane-spec.coffee | 40 +++++++++++++++++++++++++++++++++++++- spec/pane-view-spec.coffee | 21 -------------------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 5ea65f32b..71c6606ac 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -5,9 +5,18 @@ PaneContainer = require '../src/pane-container' describe "Pane", -> class Item extends Model - constructor: (@name) -> + @deserialize: ({name, uri}) -> new this(name, uri) + constructor: (@name, @uri) -> getUri: -> @uri getPath: -> @path + serialize: -> {deserializer: 'Item', @name, @uri} + isEqual: (other) -> @name is other?.name + + beforeEach -> + atom.deserializers.add(Item) + + afterEach -> + atom.deserializers.remove(Item) describe "construction", -> it "sets the active item to the first item", -> @@ -397,3 +406,32 @@ describe "Pane", -> expect(container.root.children).toEqual [pane1, pane2] pane2.destroy() expect(container.root).toBe pane1 + + describe "serialization", -> + pane = null + + beforeEach -> + pane = new Pane(items: [new Item("A", "a"), new Item("B", "b"), new Item("C", "c")]) + + it "can serialize and deserialize the pane and all its items", -> + newPane = pane.testSerialization() + expect(newPane.items).toEqual pane.items + + it "restores the active item on deserialization", -> + pane.activateItemAtIndex(1) + newPane = pane.testSerialization() + expect(newPane.activeItem).toEqual newPane.items[1] + + it "does not include items that cannot be deserialized", -> + spyOn(console, 'warn') + unserializable = {} + pane.activateItem(unserializable) + + newPane = pane.testSerialization() + expect(newPane.activeItem).toEqual pane.items[0] + expect(newPane.items.length).toBe pane.items.length - 1 + + it "includes the pane's focus state in the serialized state", -> + pane.focus() + newPane = pane.testSerialization() + expect(newPane.focused).toBe true diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 17638264e..fb1815308 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -266,27 +266,6 @@ describe "PaneView", -> expect(container.find('> .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]] describe "serialization", -> - it "can serialize and deserialize the pane and all its items", -> - newPane = new PaneView(pane.model.testSerialization()) - expect(newPane.getItems()).toEqual [view1, editor1, view2, editor2] - - it "restores the active item on deserialization", -> - pane.activateItem(editor2) - newPane = new PaneView(pane.model.testSerialization()) - expect(newPane.activeItem).toEqual editor2 - - it "does not show items that cannot be deserialized", -> - spyOn(console, 'warn') - - class Unserializable - getViewClass: -> TestView - - pane.activateItem(new Unserializable) - - newPane = new PaneView(pane.model.testSerialization()) - expect(newPane.activeItem).toEqual pane.items[0] - expect(newPane.items.length).toBe pane.items.length - 1 - it "focuses the pane after attach only if had focus when serialized", -> container.attachToDom() pane.focus()