mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-11 04:48:44 +03:00
473 lines
15 KiB
JavaScript
473 lines
15 KiB
JavaScript
const PaneContainer = require('../src/pane-container')
|
|
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
|
|
|
|
describe('PaneContainer', () => {
|
|
let confirm, params
|
|
|
|
beforeEach(() => {
|
|
confirm = spyOn(atom.applicationDelegate, 'confirm').andReturn(0)
|
|
params = {
|
|
location: 'center',
|
|
config: atom.config,
|
|
deserializerManager: atom.deserializers,
|
|
applicationDelegate: atom.applicationDelegate,
|
|
viewRegistry: atom.views
|
|
}
|
|
})
|
|
|
|
describe('serialization', () => {
|
|
let containerA, pane1A, pane2A, pane3A
|
|
|
|
beforeEach(() => {
|
|
// This is a dummy item to prevent panes from being empty on deserialization
|
|
class Item {
|
|
static deserialize () { return new (this)() }
|
|
serialize () { return {deserializer: 'Item'} }
|
|
}
|
|
atom.deserializers.add(Item)
|
|
|
|
containerA = new PaneContainer(params)
|
|
pane1A = containerA.getActivePane()
|
|
pane1A.addItem(new Item())
|
|
pane2A = pane1A.splitRight({items: [new Item()]})
|
|
pane3A = pane2A.splitDown({items: [new Item()]})
|
|
pane3A.focus()
|
|
})
|
|
|
|
it('preserves the focused pane across serialization', () => {
|
|
expect(pane3A.focused).toBe(true)
|
|
|
|
const containerB = new PaneContainer(params)
|
|
containerB.deserialize(containerA.serialize(), atom.deserializers)
|
|
const pane3B = containerB.getPanes()[2]
|
|
expect(pane3B.focused).toBe(true)
|
|
})
|
|
|
|
it('preserves the active pane across serialization, independent of focus', () => {
|
|
pane3A.activate()
|
|
expect(containerA.getActivePane()).toBe(pane3A)
|
|
|
|
const containerB = new PaneContainer(params)
|
|
containerB.deserialize(containerA.serialize(), atom.deserializers)
|
|
const pane3B = containerB.getPanes()[2]
|
|
expect(containerB.getActivePane()).toBe(pane3B)
|
|
})
|
|
|
|
it('makes the first pane active if no pane exists for the activePaneId', () => {
|
|
pane3A.activate()
|
|
const state = containerA.serialize()
|
|
state.activePaneId = -22
|
|
const containerB = new PaneContainer(params)
|
|
containerB.deserialize(state, atom.deserializers)
|
|
expect(containerB.getActivePane()).toBe(containerB.getPanes()[0])
|
|
})
|
|
|
|
describe('if there are empty panes after deserialization', () => {
|
|
beforeEach(() => {
|
|
pane3A.getItems()[0].serialize = () => ({deserializer: 'Bogus'})
|
|
})
|
|
|
|
describe("if the 'core.destroyEmptyPanes' config option is false (the default)", () =>
|
|
it('leaves the empty panes intact', () => {
|
|
const state = containerA.serialize()
|
|
const containerB = new PaneContainer(params)
|
|
containerB.deserialize(state, atom.deserializers)
|
|
const [leftPane, column] = containerB.getRoot().getChildren()
|
|
const [topPane, bottomPane] = column.getChildren()
|
|
|
|
expect(leftPane.getItems().length).toBe(1)
|
|
expect(topPane.getItems().length).toBe(1)
|
|
expect(bottomPane.getItems().length).toBe(0)
|
|
})
|
|
)
|
|
|
|
describe("if the 'core.destroyEmptyPanes' config option is true", () =>
|
|
it('removes empty panes on deserialization', () => {
|
|
atom.config.set('core.destroyEmptyPanes', true)
|
|
|
|
const state = containerA.serialize()
|
|
const containerB = new PaneContainer(params)
|
|
containerB.deserialize(state, atom.deserializers)
|
|
const [leftPane, rightPane] = containerB.getRoot().getChildren()
|
|
|
|
expect(leftPane.getItems().length).toBe(1)
|
|
expect(rightPane.getItems().length).toBe(1)
|
|
})
|
|
)
|
|
})
|
|
})
|
|
|
|
it('does not allow the root pane to be destroyed', () => {
|
|
const container = new PaneContainer(params)
|
|
container.getRoot().destroy()
|
|
expect(container.getRoot()).toBeDefined()
|
|
expect(container.getRoot().isDestroyed()).toBe(false)
|
|
})
|
|
|
|
describe('::getActivePane()', () => {
|
|
let container, pane1, pane2
|
|
|
|
beforeEach(() => {
|
|
container = new PaneContainer(params)
|
|
pane1 = container.getRoot()
|
|
})
|
|
|
|
it('returns the first pane if no pane has been made active', () => {
|
|
expect(container.getActivePane()).toBe(pane1)
|
|
expect(pane1.isActive()).toBe(true)
|
|
})
|
|
|
|
it('returns the most pane on which ::activate() was most recently called', () => {
|
|
pane2 = pane1.splitRight()
|
|
pane2.activate()
|
|
expect(container.getActivePane()).toBe(pane2)
|
|
expect(pane1.isActive()).toBe(false)
|
|
expect(pane2.isActive()).toBe(true)
|
|
pane1.activate()
|
|
expect(container.getActivePane()).toBe(pane1)
|
|
expect(pane1.isActive()).toBe(true)
|
|
expect(pane2.isActive()).toBe(false)
|
|
})
|
|
|
|
it('returns the next pane if the current active pane is destroyed', () => {
|
|
pane2 = pane1.splitRight()
|
|
pane2.activate()
|
|
pane2.destroy()
|
|
expect(container.getActivePane()).toBe(pane1)
|
|
expect(pane1.isActive()).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('::onDidChangeActivePane()', () => {
|
|
let container, pane1, pane2, observed
|
|
|
|
beforeEach(() => {
|
|
container = new PaneContainer(params)
|
|
container.getRoot().addItems([{}, {}])
|
|
container.getRoot().splitRight({items: [{}, {}]});
|
|
[pane1, pane2] = container.getPanes()
|
|
|
|
observed = []
|
|
container.onDidChangeActivePane(pane => observed.push(pane))
|
|
})
|
|
|
|
it('invokes observers when the active pane changes', () => {
|
|
pane1.activate()
|
|
pane2.activate()
|
|
expect(observed).toEqual([pane1, pane2])
|
|
})
|
|
})
|
|
|
|
describe('::onDidChangeActivePaneItem()', () => {
|
|
let container, pane1, pane2, observed
|
|
|
|
beforeEach(() => {
|
|
container = new PaneContainer(params)
|
|
container.getRoot().addItems([{}, {}])
|
|
container.getRoot().splitRight({items: [{}, {}]});
|
|
[pane1, pane2] = container.getPanes()
|
|
|
|
observed = []
|
|
container.onDidChangeActivePaneItem(item => observed.push(item))
|
|
})
|
|
|
|
it('invokes observers when the active item of the active pane changes', () => {
|
|
pane2.activateNextItem()
|
|
pane2.activateNextItem()
|
|
expect(observed).toEqual([pane2.itemAtIndex(1), pane2.itemAtIndex(0)])
|
|
})
|
|
|
|
it('invokes observers when the active pane changes', () => {
|
|
pane1.activate()
|
|
pane2.activate()
|
|
expect(observed).toEqual([pane1.itemAtIndex(0), pane2.itemAtIndex(0)])
|
|
})
|
|
})
|
|
|
|
describe('::onDidStopChangingActivePaneItem()', () => {
|
|
let container, pane1, pane2, observed
|
|
|
|
beforeEach(() => {
|
|
container = new PaneContainer(params)
|
|
container.getRoot().addItems([{}, {}])
|
|
container.getRoot().splitRight({items: [{}, {}]});
|
|
[pane1, pane2] = container.getPanes()
|
|
|
|
observed = []
|
|
container.onDidStopChangingActivePaneItem(item => observed.push(item))
|
|
})
|
|
|
|
it('invokes observers once when the active item of the active pane changes', () => {
|
|
pane2.activateNextItem()
|
|
pane2.activateNextItem()
|
|
expect(observed).toEqual([])
|
|
advanceClock(100)
|
|
expect(observed).toEqual([pane2.itemAtIndex(0)])
|
|
})
|
|
|
|
it('invokes observers once when the active pane changes', () => {
|
|
pane1.activate()
|
|
pane2.activate()
|
|
expect(observed).toEqual([])
|
|
advanceClock(100)
|
|
expect(observed).toEqual([pane2.itemAtIndex(0)])
|
|
})
|
|
})
|
|
|
|
describe('::onDidActivatePane', () => {
|
|
it('invokes observers when a pane is activated (even if it was already active)', () => {
|
|
const container = new PaneContainer(params)
|
|
container.getRoot().splitRight()
|
|
const [pane1, pane2] = container.getPanes()
|
|
|
|
const activatedPanes = []
|
|
container.onDidActivatePane(pane => activatedPanes.push(pane))
|
|
|
|
pane1.activate()
|
|
pane1.activate()
|
|
pane2.activate()
|
|
pane2.activate()
|
|
expect(activatedPanes).toEqual([pane1, pane1, pane2, pane2])
|
|
})
|
|
})
|
|
|
|
describe('::observePanes()', () => {
|
|
it('invokes observers with all current and future panes', () => {
|
|
const container = new PaneContainer(params)
|
|
container.getRoot().splitRight()
|
|
const [pane1, pane2] = container.getPanes()
|
|
|
|
const observed = []
|
|
container.observePanes(pane => observed.push(pane))
|
|
|
|
const pane3 = pane2.splitDown()
|
|
const pane4 = pane2.splitRight()
|
|
|
|
expect(observed).toEqual([pane1, pane2, pane3, pane4])
|
|
})
|
|
})
|
|
|
|
describe('::observePaneItems()', () =>
|
|
it('invokes observers with all current and future pane items', () => {
|
|
const container = new PaneContainer(params)
|
|
container.getRoot().addItems([{}, {}])
|
|
container.getRoot().splitRight({items: [{}]})
|
|
const pane2 = container.getPanes()[1]
|
|
const observed = []
|
|
container.observePaneItems(pane => observed.push(pane))
|
|
|
|
const pane3 = pane2.splitDown({items: [{}]})
|
|
pane3.addItems([{}, {}])
|
|
|
|
expect(observed).toEqual(container.getPaneItems())
|
|
})
|
|
)
|
|
|
|
describe('::confirmClose()', () => {
|
|
let container, pane1, pane2
|
|
|
|
beforeEach(() => {
|
|
class TestItem {
|
|
shouldPromptToSave () { return true }
|
|
getURI () { return 'test' }
|
|
}
|
|
|
|
container = new PaneContainer(params)
|
|
container.getRoot().splitRight();
|
|
[pane1, pane2] = container.getPanes()
|
|
pane1.addItem(new TestItem())
|
|
pane2.addItem(new TestItem())
|
|
})
|
|
|
|
it('returns true if the user saves all modified files when prompted', async () => {
|
|
confirm.andReturn(0)
|
|
const saved = await container.confirmClose()
|
|
expect(confirm).toHaveBeenCalled()
|
|
expect(saved).toBeTruthy()
|
|
})
|
|
|
|
it('returns false if the user cancels saving any modified file', async () => {
|
|
confirm.andReturn(1)
|
|
const saved = await container.confirmClose()
|
|
expect(confirm).toHaveBeenCalled()
|
|
expect(saved).toBeFalsy()
|
|
})
|
|
})
|
|
|
|
describe('::onDidAddPane(callback)', () => {
|
|
it('invokes the given callback when panes are added', () => {
|
|
const container = new PaneContainer(params)
|
|
const events = []
|
|
container.onDidAddPane((event) => {
|
|
expect(container.getPanes().includes(event.pane)).toBe(true)
|
|
events.push(event)
|
|
})
|
|
|
|
const pane1 = container.getActivePane()
|
|
const pane2 = pane1.splitRight()
|
|
const pane3 = pane2.splitDown()
|
|
|
|
expect(events).toEqual([{pane: pane2}, {pane: pane3}])
|
|
})
|
|
})
|
|
|
|
describe('::onWillDestroyPane(callback)', () => {
|
|
it('invokes the given callback before panes or their items are destroyed', () => {
|
|
class TestItem {
|
|
constructor () { this._isDestroyed = false }
|
|
destroy () { this._isDestroyed = true }
|
|
isDestroyed () { return this._isDestroyed }
|
|
}
|
|
|
|
const container = new PaneContainer(params)
|
|
const events = []
|
|
container.onWillDestroyPane((event) => {
|
|
const itemsDestroyed = event.pane.getItems().map((item) => item.isDestroyed())
|
|
events.push([event, {itemsDestroyed}])
|
|
})
|
|
|
|
const pane1 = container.getActivePane()
|
|
const pane2 = pane1.splitRight()
|
|
pane2.addItem(new TestItem())
|
|
|
|
pane2.destroy()
|
|
|
|
expect(events).toEqual([[{pane: pane2}, {itemsDestroyed: [false]}]])
|
|
})
|
|
})
|
|
|
|
describe('::onDidDestroyPane(callback)', () => {
|
|
it('invokes the given callback when panes are destroyed', () => {
|
|
const container = new PaneContainer(params)
|
|
const events = []
|
|
container.onDidDestroyPane((event) => {
|
|
expect(container.getPanes().includes(event.pane)).toBe(false)
|
|
events.push(event)
|
|
})
|
|
|
|
const pane1 = container.getActivePane()
|
|
const pane2 = pane1.splitRight()
|
|
const pane3 = pane2.splitDown()
|
|
|
|
pane2.destroy()
|
|
pane3.destroy()
|
|
|
|
expect(events).toEqual([{pane: pane2}, {pane: pane3}])
|
|
})
|
|
|
|
it('invokes the given callback when the container is destroyed', () => {
|
|
const container = new PaneContainer(params)
|
|
const events = []
|
|
container.onDidDestroyPane((event) => {
|
|
expect(container.getPanes().includes(event.pane)).toBe(false)
|
|
events.push(event)
|
|
})
|
|
|
|
const pane1 = container.getActivePane()
|
|
const pane2 = pane1.splitRight()
|
|
const pane3 = pane2.splitDown()
|
|
|
|
container.destroy()
|
|
|
|
expect(events).toEqual([{pane: pane1}, {pane: pane2}, {pane: pane3}])
|
|
})
|
|
})
|
|
|
|
describe('::onWillDestroyPaneItem() and ::onDidDestroyPaneItem', () => {
|
|
it('invokes the given callbacks when an item will be destroyed on any pane', async () => {
|
|
const container = new PaneContainer(params)
|
|
const pane1 = container.getRoot()
|
|
const item1 = {}
|
|
const item2 = {}
|
|
const item3 = {}
|
|
|
|
pane1.addItem(item1)
|
|
const events = []
|
|
container.onWillDestroyPaneItem(event => events.push(['will', event]))
|
|
container.onDidDestroyPaneItem(event => events.push(['did', event]))
|
|
const pane2 = pane1.splitRight({items: [item2, item3]})
|
|
|
|
await pane1.destroyItem(item1)
|
|
await pane2.destroyItem(item3)
|
|
await pane2.destroyItem(item2)
|
|
|
|
expect(events).toEqual([
|
|
['will', {item: item1, pane: pane1, index: 0}],
|
|
['did', {item: item1, pane: pane1, index: 0}],
|
|
['will', {item: item3, pane: pane2, index: 1}],
|
|
['did', {item: item3, pane: pane2, index: 1}],
|
|
['will', {item: item2, pane: pane2, index: 0}],
|
|
['did', {item: item2, pane: pane2, index: 0}]
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('::saveAll()', () =>
|
|
it('saves all modified pane items', async () => {
|
|
const container = new PaneContainer(params)
|
|
const pane1 = container.getRoot()
|
|
pane1.splitRight()
|
|
|
|
const item1 = {
|
|
saved: false,
|
|
getURI () { return '' },
|
|
isModified () { return true },
|
|
save () { this.saved = true }
|
|
}
|
|
const item2 = {
|
|
saved: false,
|
|
getURI () { return '' },
|
|
isModified () { return false },
|
|
save () { this.saved = true }
|
|
}
|
|
const item3 = {
|
|
saved: false,
|
|
getURI () { return '' },
|
|
isModified () { return true },
|
|
save () { this.saved = true }
|
|
}
|
|
|
|
pane1.addItem(item1)
|
|
pane1.addItem(item2)
|
|
pane1.addItem(item3)
|
|
|
|
container.saveAll()
|
|
|
|
expect(item1.saved).toBe(true)
|
|
expect(item2.saved).toBe(false)
|
|
expect(item3.saved).toBe(true)
|
|
})
|
|
)
|
|
|
|
describe('::moveActiveItemToPane(destPane) and ::copyActiveItemToPane(destPane)', () => {
|
|
let container, pane1, pane2, item1
|
|
|
|
beforeEach(() => {
|
|
class TestItem {
|
|
constructor (id) { this.id = id }
|
|
copy () { return new TestItem(this.id) }
|
|
}
|
|
|
|
container = new PaneContainer(params)
|
|
pane1 = container.getRoot()
|
|
item1 = new TestItem('1')
|
|
pane2 = pane1.splitRight({items: [item1]})
|
|
})
|
|
|
|
describe('::::moveActiveItemToPane(destPane)', () =>
|
|
it('moves active item to given pane and focuses it', () => {
|
|
container.moveActiveItemToPane(pane1)
|
|
expect(pane1.getActiveItem()).toBe(item1)
|
|
})
|
|
)
|
|
|
|
describe('::::copyActiveItemToPane(destPane)', () =>
|
|
it('copies active item to given pane and focuses it', () => {
|
|
container.copyActiveItemToPane(pane1)
|
|
expect(container.paneForItem(item1)).toBe(pane2)
|
|
expect(pane1.getActiveItem().id).toBe(item1.id)
|
|
})
|
|
)
|
|
})
|
|
})
|