PaneContainer = require 'pane-container' Pane = require 'pane' {$$} = require 'space-pen' $ = require 'jquery' describe "Pane", -> [container, view1, view2, editSession1, editSession2, pane] = [] beforeEach -> container = new PaneContainer view1 = $$ -> @div id: 'view-1', tabindex: -1, 'View 1' view2 = $$ -> @div id: 'view-2', tabindex: -1, 'View 2' editSession1 = project.open('sample.js') editSession2 = project.open('sample.txt') pane = new Pane(view1, editSession1, view2, editSession2) container.append(pane) describe ".initialize(items...)", -> it "displays the first item in the pane", -> expect(pane.itemViews.find('#view-1')).toExist() describe ".showItem(item)", -> it "hides all item views except the one being shown and sets the activeItem", -> expect(pane.activeItem).toBe view1 pane.showItem(view2) expect(view1.css('display')).toBe 'none' expect(view2.css('display')).toBe '' expect(pane.activeItem).toBe view2 it "triggers 'pane:active-item-changed' if the item isn't already the activeItem", -> pane.makeActive() itemChangedHandler = jasmine.createSpy("itemChangedHandler") container.on 'pane:active-item-changed', itemChangedHandler expect(pane.activeItem).toBe view1 pane.showItem(view2) pane.showItem(view2) expect(itemChangedHandler.callCount).toBe 1 expect(itemChangedHandler.argsForCall[0][1]).toBe view2 itemChangedHandler.reset() pane.showItem(editSession1) expect(itemChangedHandler).toHaveBeenCalled() expect(itemChangedHandler.argsForCall[0][1]).toBe editSession1 itemChangedHandler.reset() describe "if the pane's active view is focused before calling showItem", -> it "focuses the new active view", -> container.attachToDom() pane.focus() expect(pane.activeView).not.toBe view2 expect(pane.activeView).toMatchSelector ':focus' pane.showItem(view2) expect(view2).toMatchSelector ':focus' describe "when the given item isn't yet in the items list on the pane", -> view3 = null beforeEach -> view3 = $$ -> @div id: 'view-3', "View 3" pane.showItem(editSession1) expect(pane.getActiveItemIndex()).toBe 1 it "adds it to the items list after the active item", -> pane.showItem(view3) expect(pane.getItems()).toEqual [view1, editSession1, view3, view2, editSession2] expect(pane.activeItem).toBe view3 expect(pane.getActiveItemIndex()).toBe 2 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.showItem(view3) expect(events).toEqual [['pane:item-added', view3, 2], ['pane:active-item-changed', view3]] 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.showItem(editSession1) editor = pane.activeView expect(editor.css('display')).toBe '' expect(editor.activeEditSession).toBe editSession1 describe "when a valid view has already been appended for another item", -> it "recycles the existing view by assigning the selected item to it", -> pane.showItem(editSession1) pane.showItem(editSession2) expect(pane.itemViews.find('.editor').length).toBe 1 editor = pane.activeView expect(editor.css('display')).toBe '' expect(editor.activeEditSession).toBe editSession2 describe "when showing a view item", -> 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.showItem(view2) expect(pane.itemViews.find('#view-2')).toExist() expect(pane.activeView).toBe view2 describe ".destroyItem(item)", -> describe "if the item is not modified", -> it "removes the item and tries to call destroy on it", -> pane.destroyItem(editSession2) expect(pane.getItems().indexOf(editSession2)).toBe -1 expect(editSession2.destroyed).toBeTruthy() describe "if the item is modified", -> beforeEach -> spyOn(atom, 'confirm') spyOn(atom, 'showSaveDialog') spyOn(editSession2, 'save') spyOn(editSession2, 'saveAs') atom.confirm.selectOption = (buttonText) -> for arg, i in @argsForCall[0] when arg is buttonText @argsForCall[0][i + 1]?() editSession2.insertText('a') expect(editSession2.isModified()).toBeTruthy() pane.destroyItem(editSession2) it "presents a dialog with the option to save the item first", -> expect(atom.confirm).toHaveBeenCalled() expect(pane.getItems().indexOf(editSession2)).not.toBe -1 expect(editSession2.destroyed).toBeFalsy() describe "if the [Save] option is selected", -> describe "when the item has a uri", -> it "saves the item before removing and destroying it", -> atom.confirm.selectOption('Save') expect(editSession2.save).toHaveBeenCalled() expect(pane.getItems().indexOf(editSession2)).toBe -1 expect(editSession2.destroyed).toBeTruthy() 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", -> editSession2.buffer.setPath(undefined) atom.confirm.selectOption('Save') expect(atom.showSaveDialog).toHaveBeenCalled() atom.showSaveDialog.argsForCall[0][0]("/selected/path") expect(editSession2.saveAs).toHaveBeenCalledWith("/selected/path") expect(pane.getItems().indexOf(editSession2)).toBe -1 expect(editSession2.destroyed).toBeTruthy() describe "if the [Don't Save] option is selected", -> it "removes and destroys the item without saving it", -> atom.confirm.selectOption("Don't Save") expect(editSession2.save).not.toHaveBeenCalled() expect(pane.getItems().indexOf(editSession2)).toBe -1 expect(editSession2.destroyed).toBeTruthy() describe "if the [Cancel] option is selected", -> it "does not save, remove, or destroy the item", -> atom.confirm.selectOption("Cancel") expect(editSession2.save).not.toHaveBeenCalled() expect(pane.getItems().indexOf(editSession2)).not.toBe -1 expect(editSession2.destroyed).toBeFalsy() describe ".removeItem(item)", -> it "removes the item from the items list and shows the next item if it was showing", -> pane.removeItem(view1) expect(pane.getItems()).toEqual [editSession1, view2, editSession2] expect(pane.activeItem).toBe editSession1 pane.showItem(editSession2) pane.removeItem(editSession2) expect(pane.getItems()).toEqual [editSession1, view2] expect(pane.activeItem).toBe editSession1 it "triggers 'pane:item-removed' with the item and its former index", -> itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") pane.on 'pane:item-removed', itemRemovedHandler pane.removeItem(editSession1) expect(itemRemovedHandler).toHaveBeenCalled() expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 1] describe "when removing the last item", -> it "removes the pane", -> pane.removeItem(item) for item in pane.getItems() expect(pane.hasParent()).toBeFalsy() describe "when the pane is focused", -> it "shifts focus to the next pane", -> container.attachToDom() pane2 = pane.splitRight($$ -> @div class: 'view-3', tabindex: -1, 'View 3') pane.focus() expect(pane).toMatchSelector(':has(:focus)') pane.removeItem(item) for item in pane.getItems() expect(pane2).toMatchSelector ':has(:focus)' describe "when the item is a view", -> it "removes the item from the 'item-views' div", -> expect(view1.parent()).toMatchSelector pane.itemViews pane.removeItem(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.showItem(editSession2) pane.removeItem(editSession2) expect(pane.itemViews.find('.editor')).toExist() pane.removeItem(editSession1) expect(pane.itemViews.find('.editor')).not.toExist() 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 [editSession1, view2, view1, editSession2] expect(itemMovedHandler).toHaveBeenCalled() expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2] itemMovedHandler.reset() pane.moveItem(editSession1, 3) expect(pane.getItems()).toEqual [view2, view1, editSession2, editSession1] expect(itemMovedHandler).toHaveBeenCalled() expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 3] itemMovedHandler.reset() pane.moveItem(editSession1, 1) expect(pane.getItems()).toEqual [view2, editSession1, view1, editSession2] expect(itemMovedHandler).toHaveBeenCalled() expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 1] itemMovedHandler.reset() describe ".moveItemToPane(item, pane, index)", -> [pane2, view3] = [] beforeEach -> view3 = $$ -> @div id: 'view-3', "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 [editSession1, view2, editSession2] 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.removeItem(view1) pane.removeItem(view2) pane.removeItem(editSession2) expect(pane.getItems()).toEqual [editSession1] pane.moveItemToPane(editSession1, pane2, 1) expect(pane.hasParent()).toBeFalsy() expect(pane2.getItems()).toEqual [view3, editSession1] expect(editSession1.destroyed).toBeFalsy() 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.showItemAtIndex(1) expect(pane2.activeView.data('preservative')).toBe 1234 describe "core:close", -> it "destroys the active item and does not bubble the event", -> containerCloseHandler = jasmine.createSpy("containerCloseHandler") container.on 'core:close', containerCloseHandler pane.showItem(editSession1) initialItemCount = pane.getItems().length pane.trigger 'core:close' expect(pane.getItems().length).toBe initialItemCount - 1 expect(editSession1.destroyed).toBeTruthy() expect(containerCloseHandler).not.toHaveBeenCalled() describe "pane:close", -> it "destroys all items and removes the pane", -> pane.showItem(editSession1) pane.trigger 'pane:close' expect(pane.hasParent()).toBeFalsy() expect(editSession2.destroyed).toBeTruthy() expect(editSession1.destroyed).toBeTruthy() describe "pane:close-other-items", -> it "destroys all items except the current", -> pane.showItem(editSession1) pane.trigger 'pane:close-other-items' expect(editSession2.destroyed).toBeTruthy() expect(pane.getItems()).toEqual [editSession1] describe "core:save", -> describe "when the current item has a uri", -> describe "when the current item has a save method", -> it "saves the current item", -> spyOn(editSession2, 'save') pane.showItem(editSession2) pane.trigger 'core:save' expect(editSession2.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.trigger 'core:save' describe "when the current item has no uri", -> beforeEach -> spyOn(atom, 'showSaveDialog') describe "when the current item has a saveAs method", -> it "opens a save dialog and saves the current item as the selected path", -> spyOn(editSession2, 'saveAs') editSession2.buffer.setPath(undefined) pane.showItem(editSession2) pane.trigger 'core:save' expect(atom.showSaveDialog).toHaveBeenCalled() atom.showSaveDialog.argsForCall[0][0]('/selected/path') expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path') describe "when the current item has no saveAs method", -> it "does nothing", -> expect(pane.activeItem.saveAs).toBeUndefined() pane.trigger 'core:save' expect(atom.showSaveDialog).not.toHaveBeenCalled() describe "core:save-as", -> beforeEach -> spyOn(atom, 'showSaveDialog') 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(editSession2, 'saveAs') pane.showItem(editSession2) pane.trigger 'core:save-as' expect(atom.showSaveDialog).toHaveBeenCalled() atom.showSaveDialog.argsForCall[0][0]('/selected/path') expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path') describe "when the current item does not have a saveAs method", -> it "does nothing", -> expect(pane.activeItem.saveAs).toBeUndefined() pane.trigger 'core:save-as' expect(atom.showSaveDialog).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 pane.trigger 'pane:show-previous-item' expect(pane.activeItem).toBe editSession2 pane.trigger 'pane:show-previous-item' expect(pane.activeItem).toBe view2 pane.trigger 'pane:show-next-item' expect(pane.activeItem).toBe editSession2 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' 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") pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler expect(pane.activeItem).toBe view1 view2.trigger 'title-changed' expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() view1.trigger 'title-changed' expect(activeItemTitleChangedHandler).toHaveBeenCalled() activeItemTitleChangedHandler.reset() pane.showItem(view2) view2.trigger 'title-changed' expect(activeItemTitleChangedHandler).toHaveBeenCalled() describe ".remove()", -> it "destroys all the pane's items", -> pane.remove() expect(editSession1.destroyed).toBeTruthy() expect(editSession2.destroyed).toBeTruthy() it "triggers a 'pane:removed' event with the pane", -> removedHandler = jasmine.createSpy("removedHandler") container.on 'pane:removed', removedHandler pane.remove() expect(removedHandler).toHaveBeenCalled() expect(removedHandler.argsForCall[0][1]).toBe pane describe "when there are other panes", -> [paneToLeft, paneToRight] = [] beforeEach -> pane.showItem(editSession1) paneToLeft = pane.splitLeft() paneToRight = pane.splitRight() container.attachToDom() describe "when the removed pane is focused", -> it "activates and focuses the next pane", -> pane.focus() pane.remove() expect(paneToLeft.isActive()).toBeFalsy() expect(paneToRight.isActive()).toBeTruthy() expect(paneToRight).toMatchSelector ':has(:focus)' describe "when the removed pane is active but not focused", -> it "activates the next pane, but does not focus it", -> $(document.activeElement).blur() expect(pane).not.toMatchSelector ':has(:focus)' pane.makeActive() pane.remove() expect(paneToLeft.isActive()).toBeFalsy() expect(paneToRight.isActive()).toBeTruthy() expect(paneToRight).not.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 window.rootView = focus: jasmine.createSpy("rootView.focus") describe "when the removed pane is focused", -> it "calls focus on rootView so we don't lose focus", -> container.attachToDom() pane.focus() pane.remove() expect(rootView.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(rootView.focus).not.toHaveBeenCalled() describe ".getNextPane()", -> it "returns the next pane if one exists, wrapping around from the last pane to the first", -> pane.showItem(editSession1) expect(pane.getNextPane()).toBeUndefined pane2 = pane.splitRight() expect(pane.getNextPane()).toBe pane2 expect(pane2.getNextPane()).toBe pane describe "when the pane is focused", -> it "focuses the active item 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", -> becameActiveHandler = jasmine.createSpy("becameActiveHandler") container.on 'pane:became-active', becameActiveHandler expect(pane.isActive()).toBeFalsy() pane.focusin() expect(pane.isActive()).toBeTruthy() pane.focusin() expect(becameActiveHandler.callCount).toBe 1 describe "split methods", -> [pane1, view3, view4] = [] beforeEach -> pane1 = pane pane.showItem(editSession1) view3 = $$ -> @div id: 'view-3', 'View 3' view4 = $$ -> @div id: 'view-4', '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() expect(container.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]] expect(pane2.items).toEqual [editSession1] expect(pane2.activeItem).not.toBe editSession1 # it's a copy pane3 = pane2.splitRight(view3, view4) expect(pane3.getItems()).toEqual [view3, view4] expect(container.find('.row .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]] describe "splitRight(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() expect(container.find('.row .pane').toArray()).toEqual [pane2[0], pane[0]] expect(pane2.items).toEqual [editSession1] expect(pane2.activeItem).not.toBe editSession1 # it's a copy pane3 = pane2.splitLeft(view3, view4) expect(pane3.getItems()).toEqual [view3, view4] expect(container.find('.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() expect(container.find('.column .pane').toArray()).toEqual [pane[0], pane2[0]] expect(pane2.items).toEqual [editSession1] expect(pane2.activeItem).not.toBe editSession1 # it's a copy pane3 = pane2.splitDown(view3, view4) expect(pane3.getItems()).toEqual [view3, view4] expect(container.find('.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() expect(container.find('.column .pane').toArray()).toEqual [pane2[0], pane[0]] expect(pane2.items).toEqual [editSession1] expect(pane2.activeItem).not.toBe editSession1 # it's a copy pane3 = pane2.splitUp(view3, view4) expect(pane3.getItems()).toEqual [view3, view4] expect(container.find('.column .pane').toArray()).toEqual [pane3[0], pane2[0], pane[0]] it "lays out nested panes by equally dividing their containing row / column", -> container.width(520).height(240).attachToDom() pane1.showItem($("1")) pane1 .splitLeft($("2")) .splitUp($("3")) .splitLeft($("4")) .splitDown($("5")) row1 = container.children(':eq(0)') expect(row1.children().length).toBe 2 column1 = row1.children(':eq(0)').view() pane1 = row1.children(':eq(1)').view() expect(column1.outerWidth()).toBe Math.round(2/3 * container.width()) expect(column1.outerHeight()).toBe container.height() expect(pane1.outerWidth()).toBe Math.round(1/3 * container.width()) expect(pane1.outerHeight()).toBe container.height() expect(Math.round(pane1.position().left)).toBe column1.outerWidth() expect(column1.children().length).toBe 2 row2 = column1.children(':eq(0)').view() pane2 = column1.children(':eq(1)').view() expect(row2.outerWidth()).toBe column1.outerWidth() expect(row2.height()).toBe 2/3 * container.height() expect(pane2.outerWidth()).toBe column1.outerWidth() expect(pane2.outerHeight()).toBe 1/3 * container.height() expect(Math.round(pane2.position().top)).toBe row2.height() expect(row2.children().length).toBe 2 column3 = row2.children(':eq(0)').view() pane3 = row2.children(':eq(1)').view() expect(column3.outerWidth()).toBe Math.round(1/3 * container.width()) expect(column3.outerHeight()).toBe row2.outerHeight() # the built in rounding seems to be rounding x.5 down, but we need to go up. this sucks. expect(Math.round(pane3.trueWidth())).toBe Math.round(1/3 * container.width()) expect(pane3.height()).toBe row2.outerHeight() expect(Math.round(pane3.position().left)).toBe column3.width() expect(column3.children().length).toBe 2 pane4 = column3.children(':eq(0)').view() pane5 = column3.children(':eq(1)').view() expect(pane4.outerWidth()).toBe column3.width() expect(pane4.outerHeight()).toBe 1/3 * container.height() expect(pane5.outerWidth()).toBe column3.width() expect(Math.round(pane5.position().top)).toBe pane4.outerHeight() expect(pane5.outerHeight()).toBe 1/3 * container.height() pane5.remove() expect(column3.parent()).not.toExist() expect(pane2.outerHeight()).toBe Math.floor(1/2 * container.height()) expect(pane3.outerHeight()).toBe Math.floor(1/2 * container.height()) expect(pane4.outerHeight()).toBe Math.floor(1/2 * container.height()) pane4.remove() expect(row2.parent()).not.toExist() expect(pane1.outerWidth()).toBe Math.floor(1/2 * container.width()) expect(pane2.outerWidth()).toBe Math.floor(1/2 * container.width()) expect(pane3.outerWidth()).toBe Math.floor(1/2 * container.width()) pane3.remove() expect(column1.parent()).not.toExist() expect(pane2.outerHeight()).toBe container.height() pane2.remove() expect(row1.parent()).not.toExist() expect(container.children().length).toBe 1 expect(container.children('.pane').length).toBe 1 expect(pane1.outerWidth()).toBe container.width() describe "autosave", -> [initialActiveItem, initialActiveItemUri] = [] beforeEach -> initialActiveItem = pane.activeItem initialActiveItemUri = null pane.activeItem.getUri = -> initialActiveItemUri pane.activeItem.save = jasmine.createSpy("activeItem.save") spyOn(pane, 'saveItem').andCallThrough() describe "when the active view loses focus", -> it "saves the item if core.autosave is true and the item has a uri", -> pane.activeView.trigger 'focusout' expect(pane.saveItem).not.toHaveBeenCalled() expect(pane.activeItem.save).not.toHaveBeenCalled() config.set('core.autosave', true) pane.activeView.trigger 'focusout' expect(pane.saveItem).not.toHaveBeenCalled() expect(pane.activeItem.save).not.toHaveBeenCalled() initialActiveItemUri = '/tmp/hi' pane.activeView.trigger 'focusout' expect(pane.activeItem.save).toHaveBeenCalled() describe "when an item becomes inactive", -> it "saves the item if core.autosave is true and the item has a uri", -> expect(view2).not.toBe pane.activeItem expect(pane.saveItem).not.toHaveBeenCalled() expect(initialActiveItem.save).not.toHaveBeenCalled() pane.showItem(view2) pane.showItem(initialActiveItem) config.set('core.autosave', true) pane.showItem(view2) expect(pane.saveItem).not.toHaveBeenCalled() expect(initialActiveItem.save).not.toHaveBeenCalled() pane.showItem(initialActiveItem) initialActiveItemUri = '/tmp/hi' pane.showItem(view2) expect(initialActiveItem.save).toHaveBeenCalled() describe "when an item is destroyed", -> it "saves the item if core.autosave is true and the item has a uri", -> # doesn't have to be the active item expect(view2).not.toBe pane.activeItem pane.showItem(view2) pane.destroyItem(editSession1) expect(pane.saveItem).not.toHaveBeenCalled() config.set("core.autosave", true) view2.getUri = -> undefined view2.save = -> pane.destroyItem(view2) expect(pane.saveItem).not.toHaveBeenCalled() initialActiveItemUri = '/tmp/hi' pane.destroyItem(initialActiveItem) expect(initialActiveItem.save).toHaveBeenCalled() describe ".itemForUri(uri)", -> it "returns the item for which a call to .getUri() returns the given uri", -> expect(pane.itemForUri(editSession1.getUri())).toBe editSession1 expect(pane.itemForUri(editSession2.getUri())).toBe editSession2 describe "serialization", -> it "can serialize and deserialize the pane and all its serializable items", -> newPane = deserialize(pane.serialize()) expect(newPane.getItems()).toEqual [editSession1, editSession2] it "restores the active item on deserialization if it serializable", -> pane.showItem(editSession2) newPane = deserialize(pane.serialize()) expect(newPane.activeItem).toEqual editSession2 it "defaults to the first item on deserialization if the active item was not serializable", -> expect(view2.serialize?()).toBeFalsy() pane.showItem(view2) newPane = deserialize(pane.serialize()) expect(newPane.activeItem).toEqual editSession1 it "focuses the pane after attach only if had focus when serialized", -> container.attachToDom() pane.focus() state = pane.serialize() pane.remove() newPane = deserialize(state) container.append(newPane) expect(newPane).toMatchSelector(':has(:focus)') $(document.activeElement).blur() state = newPane.serialize() newPane.remove() newerPane = deserialize(state) expect(newerPane).not.toMatchSelector(':has(:focus)')