123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- const PaneContainer = require('../src/pane-container');
- describe('PaneContainer', () => {
- let confirm, params;
- beforeEach(() => {
- confirm = spyOn(atom.applicationDelegate, 'confirm').andCallFake(
- (options, callback) => callback(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.andCallFake((options, callback) => callback(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.andCallFake((options, callback) => callback(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.length).toBe(6);
- expect(events[1]).toEqual([
- 'did',
- { item: item1, pane: pane1, index: 0 }
- ]);
- expect(events[3]).toEqual([
- 'did',
- { item: item3, pane: pane2, index: 1 }
- ]);
- expect(events[5]).toEqual([
- 'did',
- { item: item2, pane: pane2, index: 0 }
- ]);
- expect(events[0][0]).toEqual('will');
- expect(events[0][1].item).toEqual(item1);
- expect(events[0][1].pane).toEqual(pane1);
- expect(events[0][1].index).toEqual(0);
- expect(typeof events[0][1].prevent).toEqual('function');
- expect(events[2][0]).toEqual('will');
- expect(events[2][1].item).toEqual(item3);
- expect(events[2][1].pane).toEqual(pane2);
- expect(events[2][1].index).toEqual(1);
- expect(typeof events[2][1].prevent).toEqual('function');
- expect(events[4][0]).toEqual('will');
- expect(events[4][1].item).toEqual(item2);
- expect(events[4][1].pane).toEqual(pane2);
- expect(events[4][1].index).toEqual(0);
- expect(typeof events[4][1].prevent).toEqual('function');
- });
- });
- 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);
- }));
- });
- });
|