mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-11 11:15:24 +03:00
926 lines
31 KiB
JavaScript
926 lines
31 KiB
JavaScript
const { conditionPromise } = require('./async-spec-helpers');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const temp = require('temp').track();
|
|
const AtomEnvironment = require('../src/atom-environment');
|
|
|
|
describe('AtomEnvironment', () => {
|
|
afterEach(() => {
|
|
try {
|
|
temp.cleanupSync();
|
|
} catch (error) {}
|
|
});
|
|
|
|
describe('window sizing methods', () => {
|
|
describe('::getPosition and ::setPosition', () => {
|
|
let originalPosition = null;
|
|
beforeEach(() => (originalPosition = atom.getPosition()));
|
|
|
|
afterEach(() => atom.setPosition(originalPosition.x, originalPosition.y));
|
|
|
|
it('sets the position of the window, and can retrieve the position just set', () => {
|
|
atom.setPosition(22, 45);
|
|
expect(atom.getPosition()).toEqual({ x: 22, y: 45 });
|
|
});
|
|
});
|
|
|
|
describe('::getSize and ::setSize', () => {
|
|
let originalSize = null;
|
|
beforeEach(() => (originalSize = atom.getSize()));
|
|
afterEach(() => atom.setSize(originalSize.width, originalSize.height));
|
|
|
|
it('sets the size of the window, and can retrieve the size just set', async () => {
|
|
const newWidth = originalSize.width - 12;
|
|
const newHeight = originalSize.height - 23;
|
|
await atom.setSize(newWidth, newHeight);
|
|
expect(atom.getSize()).toEqual({ width: newWidth, height: newHeight });
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('.isReleasedVersion()', () => {
|
|
it('returns false if the version is a SHA and true otherwise', () => {
|
|
let version = '0.1.0';
|
|
spyOn(atom, 'getVersion').andCallFake(() => version);
|
|
expect(atom.isReleasedVersion()).toBe(true);
|
|
version = '36b5518';
|
|
expect(atom.isReleasedVersion()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('.versionSatisfies()', () => {
|
|
it('returns appropriately for provided range', () => {
|
|
let testPulsarVersion = '0.1.0';
|
|
spyOn(atom, 'getVersion').andCallFake(() => testPulsarVersion);
|
|
expect(atom.versionSatisfies('>0.2.0')).toBe(false);
|
|
expect(atom.versionSatisfies('>=0.x.x <=2.x.x')).toBe(true);
|
|
expect(atom.versionSatisfies('^0.1.x')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('loading default config', () => {
|
|
it('loads the default core config schema', () => {
|
|
expect(atom.config.get('core.excludeVcsIgnoredPaths')).toBe(true);
|
|
expect(atom.config.get('core.followSymlinks')).toBe(true);
|
|
expect(atom.config.get('editor.showInvisibles')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('window onerror handler', () => {
|
|
let devToolsPromise = null;
|
|
beforeEach(() => {
|
|
devToolsPromise = Promise.resolve();
|
|
spyOn(atom, 'openDevTools').andReturn(devToolsPromise);
|
|
spyOn(atom, 'executeJavaScriptInDevTools');
|
|
});
|
|
|
|
it('will open the dev tools when an error is triggered', async () => {
|
|
try {
|
|
a + 1; // eslint-disable-line no-undef, no-unused-expressions
|
|
} catch (e) {
|
|
window.onerror(e.toString(), 'abc', 2, 3, e);
|
|
}
|
|
|
|
await devToolsPromise;
|
|
expect(atom.openDevTools).toHaveBeenCalled();
|
|
expect(atom.executeJavaScriptInDevTools).toHaveBeenCalled();
|
|
});
|
|
|
|
describe('::onWillThrowError', () => {
|
|
let willThrowSpy = null;
|
|
|
|
beforeEach(() => {
|
|
willThrowSpy = jasmine.createSpy();
|
|
});
|
|
|
|
it('is called when there is an error', () => {
|
|
let error = null;
|
|
atom.onWillThrowError(willThrowSpy);
|
|
try {
|
|
a + 1; // eslint-disable-line no-undef, no-unused-expressions
|
|
} catch (e) {
|
|
error = e;
|
|
window.onerror(e.toString(), 'abc', 2, 3, e);
|
|
}
|
|
|
|
delete willThrowSpy.mostRecentCall.args[0].preventDefault;
|
|
expect(willThrowSpy).toHaveBeenCalledWith({
|
|
message: error.toString(),
|
|
url: 'abc',
|
|
line: 2,
|
|
column: 3,
|
|
originalError: error
|
|
});
|
|
});
|
|
|
|
it('will not show the devtools when preventDefault() is called', () => {
|
|
willThrowSpy.andCallFake(errorObject => errorObject.preventDefault());
|
|
atom.onWillThrowError(willThrowSpy);
|
|
|
|
try {
|
|
a + 1; // eslint-disable-line no-undef, no-unused-expressions
|
|
} catch (e) {
|
|
window.onerror(e.toString(), 'abc', 2, 3, e);
|
|
}
|
|
|
|
expect(willThrowSpy).toHaveBeenCalled();
|
|
expect(atom.openDevTools).not.toHaveBeenCalled();
|
|
expect(atom.executeJavaScriptInDevTools).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('::onDidThrowError', () => {
|
|
let didThrowSpy = null;
|
|
beforeEach(() => (didThrowSpy = jasmine.createSpy()));
|
|
|
|
it('is called when there is an error', () => {
|
|
let error = null;
|
|
atom.onDidThrowError(didThrowSpy);
|
|
try {
|
|
a + 1; // eslint-disable-line no-undef, no-unused-expressions
|
|
} catch (e) {
|
|
error = e;
|
|
window.onerror(e.toString(), 'abc', 2, 3, e);
|
|
}
|
|
expect(didThrowSpy).toHaveBeenCalledWith({
|
|
message: error.toString(),
|
|
url: 'abc',
|
|
line: 2,
|
|
column: 3,
|
|
originalError: error
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('.assert(condition, message, callback)', () => {
|
|
let errors = null;
|
|
|
|
beforeEach(() => {
|
|
errors = [];
|
|
spyOn(atom, 'isReleasedVersion').andReturn(true);
|
|
atom.onDidFailAssertion(error => errors.push(error));
|
|
});
|
|
|
|
describe('if the condition is false', () => {
|
|
it('notifies onDidFailAssertion handlers with an error object based on the call site of the assertion', () => {
|
|
const result = atom.assert(false, 'a == b');
|
|
expect(result).toBe(false);
|
|
expect(errors.length).toBe(1);
|
|
expect(errors[0].message).toBe('Assertion failed: a == b');
|
|
expect(errors[0].stack).toContain('atom-environment-spec');
|
|
});
|
|
|
|
describe('if passed a callback function', () => {
|
|
it("calls the callback with the assertion failure's error object", () => {
|
|
let error = null;
|
|
atom.assert(false, 'a == b', e => (error = e));
|
|
expect(error).toBe(errors[0]);
|
|
});
|
|
});
|
|
|
|
describe('if passed metadata', () => {
|
|
it("assigns the metadata on the assertion failure's error object", () => {
|
|
atom.assert(false, 'a == b', { foo: 'bar' });
|
|
expect(errors[0].metadata).toEqual({ foo: 'bar' });
|
|
});
|
|
});
|
|
|
|
describe('when Atom has been built from source', () => {
|
|
it('throws an error', () => {
|
|
atom.isReleasedVersion.andReturn(false);
|
|
expect(() => atom.assert(false, 'testing')).toThrow(
|
|
'Assertion failed: testing'
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('if the condition is true', () => {
|
|
it('does nothing', () => {
|
|
const result = atom.assert(true, 'a == b');
|
|
expect(result).toBe(true);
|
|
expect(errors).toEqual([]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('saving and loading', () => {
|
|
beforeEach(() => (atom.enablePersistence = true));
|
|
|
|
afterEach(() => (atom.enablePersistence = false));
|
|
|
|
it('selects the state based on the current project paths', async () => {
|
|
jasmine.useRealClock();
|
|
|
|
const [dir1, dir2] = [temp.mkdirSync('dir1-'), temp.mkdirSync('dir2-')];
|
|
|
|
const loadSettings = Object.assign(atom.getLoadSettings(), {
|
|
initialProjectRoots: [dir1],
|
|
windowState: null
|
|
});
|
|
|
|
spyOn(atom, 'getLoadSettings').andCallFake(() => loadSettings);
|
|
spyOn(atom, 'serialize').andReturn({ stuff: 'cool' });
|
|
|
|
atom.project.setPaths([dir1, dir2]);
|
|
|
|
// State persistence will fail if other Atom instances are running
|
|
expect(await atom.stateStore.connect()).toBe(true);
|
|
|
|
await atom.saveState();
|
|
expect(await atom.loadState()).toBeFalsy();
|
|
|
|
loadSettings.initialProjectRoots = [dir2, dir1];
|
|
expect(await atom.loadState()).toEqual({ stuff: 'cool' });
|
|
});
|
|
|
|
it('saves state when the CPU is idle after a keydown or mousedown event', () => {
|
|
const atomEnv = new AtomEnvironment({
|
|
applicationDelegate: global.atom.applicationDelegate
|
|
});
|
|
const idleCallbacks = [];
|
|
atomEnv.initialize({
|
|
window: {
|
|
requestIdleCallback(callback) {
|
|
idleCallbacks.push(callback);
|
|
},
|
|
addEventListener() {},
|
|
removeEventListener() {}
|
|
},
|
|
document: document.implementation.createHTMLDocument()
|
|
});
|
|
|
|
spyOn(atomEnv, 'saveState');
|
|
|
|
const keydown = new KeyboardEvent('keydown');
|
|
atomEnv.document.dispatchEvent(keydown);
|
|
advanceClock(atomEnv.saveStateDebounceInterval);
|
|
idleCallbacks.shift()();
|
|
expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: false });
|
|
expect(atomEnv.saveState).not.toHaveBeenCalledWith({ isUnloading: true });
|
|
|
|
atomEnv.saveState.reset();
|
|
const mousedown = new MouseEvent('mousedown');
|
|
atomEnv.document.dispatchEvent(mousedown);
|
|
advanceClock(atomEnv.saveStateDebounceInterval);
|
|
idleCallbacks.shift()();
|
|
expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: false });
|
|
expect(atomEnv.saveState).not.toHaveBeenCalledWith({ isUnloading: true });
|
|
|
|
atomEnv.destroy();
|
|
});
|
|
|
|
it('ignores mousedown/keydown events happening after calling prepareToUnloadEditorWindow', async () => {
|
|
const atomEnv = new AtomEnvironment({
|
|
applicationDelegate: global.atom.applicationDelegate
|
|
});
|
|
const idleCallbacks = [];
|
|
atomEnv.initialize({
|
|
window: {
|
|
requestIdleCallback(callback) {
|
|
idleCallbacks.push(callback);
|
|
},
|
|
addEventListener() {},
|
|
removeEventListener() {}
|
|
},
|
|
document: document.implementation.createHTMLDocument()
|
|
});
|
|
|
|
spyOn(atomEnv, 'saveState');
|
|
|
|
let mousedown = new MouseEvent('mousedown');
|
|
atomEnv.document.dispatchEvent(mousedown);
|
|
expect(atomEnv.saveState).not.toHaveBeenCalled();
|
|
await atomEnv.prepareToUnloadEditorWindow();
|
|
expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: true });
|
|
|
|
advanceClock(atomEnv.saveStateDebounceInterval);
|
|
idleCallbacks.shift()();
|
|
expect(atomEnv.saveState.calls.length).toBe(1);
|
|
|
|
mousedown = new MouseEvent('mousedown');
|
|
atomEnv.document.dispatchEvent(mousedown);
|
|
advanceClock(atomEnv.saveStateDebounceInterval);
|
|
idleCallbacks.shift()();
|
|
expect(atomEnv.saveState.calls.length).toBe(1);
|
|
|
|
atomEnv.destroy();
|
|
});
|
|
|
|
it('serializes the project state with all the options supplied in saveState', async () => {
|
|
spyOn(atom.project, 'serialize').andReturn({ foo: 42 });
|
|
|
|
await atom.saveState({ anyOption: 'any option' });
|
|
expect(atom.project.serialize.calls.length).toBe(1);
|
|
expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({
|
|
anyOption: 'any option'
|
|
});
|
|
});
|
|
|
|
it('serializes the text editor registry', async () => {
|
|
await atom.packages.activatePackage('language-text');
|
|
const editor = await atom.workspace.open('sample.js');
|
|
expect(atom.grammars.assignLanguageMode(editor, 'text.plain')).toBe(true);
|
|
|
|
const atom2 = new AtomEnvironment({
|
|
applicationDelegate: atom.applicationDelegate,
|
|
window: document.createElement('div'),
|
|
document: Object.assign(document.createElement('div'), {
|
|
body: document.createElement('div'),
|
|
head: document.createElement('div')
|
|
})
|
|
});
|
|
atom2.initialize({ document, window });
|
|
|
|
await atom2.deserialize(atom.serialize());
|
|
await atom2.packages.activatePackage('language-text');
|
|
const editor2 = atom2.workspace.getActiveTextEditor();
|
|
expect(
|
|
editor2
|
|
.getBuffer()
|
|
.getLanguageMode()
|
|
.getLanguageId()
|
|
).toBe('text.plain');
|
|
atom2.destroy();
|
|
});
|
|
|
|
describe('deserialization failures', () => {
|
|
it('propagates unrecognized project state restoration failures', async () => {
|
|
let err;
|
|
spyOn(atom.project, 'deserialize').andCallFake(() => {
|
|
err = new Error('deserialization failure');
|
|
return Promise.reject(err);
|
|
});
|
|
spyOn(atom.notifications, 'addError');
|
|
|
|
await atom.deserialize({ project: 'should work' });
|
|
expect(atom.notifications.addError).toHaveBeenCalledWith(
|
|
'Unable to deserialize project',
|
|
{
|
|
description: 'deserialization failure',
|
|
stack: err.stack
|
|
}
|
|
);
|
|
});
|
|
|
|
it('disregards missing project folder errors', async () => {
|
|
spyOn(atom.project, 'deserialize').andCallFake(() => {
|
|
const err = new Error('deserialization failure');
|
|
err.missingProjectPaths = ['nah'];
|
|
return Promise.reject(err);
|
|
});
|
|
spyOn(atom.notifications, 'addError');
|
|
|
|
await atom.deserialize({ project: 'should work' });
|
|
expect(atom.notifications.addError).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('openInitialEmptyEditorIfNecessary', () => {
|
|
describe('when there are no paths set', () => {
|
|
beforeEach(() =>
|
|
spyOn(atom, 'getLoadSettings').andReturn({ hasOpenFiles: false })
|
|
);
|
|
|
|
it('opens an empty buffer', () => {
|
|
spyOn(atom.workspace, 'open');
|
|
atom.openInitialEmptyEditorIfNecessary();
|
|
expect(atom.workspace.open).toHaveBeenCalledWith(null, {
|
|
pending: true
|
|
});
|
|
});
|
|
|
|
it('does not open an empty buffer when a buffer is already open', async () => {
|
|
await atom.workspace.open();
|
|
spyOn(atom.workspace, 'open');
|
|
atom.openInitialEmptyEditorIfNecessary();
|
|
expect(atom.workspace.open).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does not open an empty buffer when core.openEmptyEditorOnStart is false', async () => {
|
|
atom.config.set('core.openEmptyEditorOnStart', false);
|
|
spyOn(atom.workspace, 'open');
|
|
atom.openInitialEmptyEditorIfNecessary();
|
|
expect(atom.workspace.open).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('when the project has a path', () => {
|
|
beforeEach(() => {
|
|
spyOn(atom, 'getLoadSettings').andReturn({ hasOpenFiles: true });
|
|
spyOn(atom.workspace, 'open');
|
|
});
|
|
|
|
it('does not open an empty buffer', () => {
|
|
atom.openInitialEmptyEditorIfNecessary();
|
|
expect(atom.workspace.open).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('adding a project folder', () => {
|
|
it('does nothing if the user dismisses the file picker', () => {
|
|
const projectRoots = atom.project.getPaths();
|
|
spyOn(atom, 'pickFolder').andCallFake(callback => callback(null));
|
|
atom.addProjectFolder();
|
|
expect(atom.project.getPaths()).toEqual(projectRoots);
|
|
});
|
|
|
|
describe('when there is no saved state for the added folders', () => {
|
|
beforeEach(() => {
|
|
spyOn(atom, 'loadState').andReturn(Promise.resolve(null));
|
|
spyOn(atom, 'attemptRestoreProjectStateForPaths');
|
|
});
|
|
|
|
it('adds the selected folder to the project', async () => {
|
|
atom.project.setPaths([]);
|
|
const tempDirectory = temp.mkdirSync('a-new-directory');
|
|
spyOn(atom, 'pickFolder').andCallFake(callback =>
|
|
callback([tempDirectory])
|
|
);
|
|
await atom.addProjectFolder();
|
|
expect(atom.project.getPaths()).toEqual([tempDirectory]);
|
|
expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('when there is saved state for the relevant directories', () => {
|
|
const state = Symbol('savedState');
|
|
|
|
beforeEach(() => {
|
|
spyOn(atom, 'getStateKey').andCallFake(dirs => dirs.join(':'));
|
|
spyOn(atom, 'loadState').andCallFake(async key =>
|
|
key === __dirname ? state : null
|
|
);
|
|
spyOn(atom, 'attemptRestoreProjectStateForPaths');
|
|
spyOn(atom, 'pickFolder').andCallFake(callback =>
|
|
callback([__dirname])
|
|
);
|
|
atom.project.setPaths([]);
|
|
});
|
|
|
|
describe('when there are no project folders', () => {
|
|
it('attempts to restore the project state', async () => {
|
|
await atom.addProjectFolder();
|
|
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
|
|
state,
|
|
[__dirname]
|
|
);
|
|
expect(atom.project.getPaths()).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('when there are already project folders', () => {
|
|
const openedPath = path.join(__dirname, 'fixtures');
|
|
|
|
beforeEach(() => atom.project.setPaths([openedPath]));
|
|
|
|
it('does not attempt to restore the project state, instead adding the project paths', async () => {
|
|
await atom.addProjectFolder();
|
|
expect(
|
|
atom.attemptRestoreProjectStateForPaths
|
|
).not.toHaveBeenCalled();
|
|
expect(atom.project.getPaths()).toEqual([openedPath, __dirname]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('attemptRestoreProjectStateForPaths(state, projectPaths, filesToOpen)', () => {
|
|
describe('when the window is clean (empty or has only unnamed, unmodified buffers)', () => {
|
|
beforeEach(async () => {
|
|
// Unnamed, unmodified buffer doesn't count toward "clean"-ness
|
|
await atom.workspace.open();
|
|
});
|
|
|
|
it('automatically restores the saved state into the current environment', async () => {
|
|
const projectPath = temp.mkdirSync();
|
|
const filePath1 = path.join(projectPath, 'file-1');
|
|
const filePath2 = path.join(projectPath, 'file-2');
|
|
const filePath3 = path.join(projectPath, 'file-3');
|
|
fs.writeFileSync(filePath1, 'abc');
|
|
fs.writeFileSync(filePath2, 'def');
|
|
fs.writeFileSync(filePath3, 'ghi');
|
|
|
|
const env1 = new AtomEnvironment({
|
|
applicationDelegate: atom.applicationDelegate
|
|
});
|
|
env1.project.setPaths([projectPath]);
|
|
await env1.workspace.open(filePath1);
|
|
await env1.workspace.open(filePath2);
|
|
await env1.workspace.open(filePath3);
|
|
const env1State = env1.serialize();
|
|
env1.destroy();
|
|
|
|
const env2 = new AtomEnvironment({
|
|
applicationDelegate: atom.applicationDelegate
|
|
});
|
|
await env2.attemptRestoreProjectStateForPaths(
|
|
env1State,
|
|
[projectPath],
|
|
[filePath2]
|
|
);
|
|
const restoredURIs = env2.workspace.getPaneItems().map(p => p.getURI());
|
|
expect(restoredURIs).toEqual([filePath1, filePath2, filePath3]);
|
|
env2.destroy();
|
|
});
|
|
|
|
describe('when a dock has a non-text editor', () => {
|
|
it("doesn't prompt the user to restore state", () => {
|
|
const dock = atom.workspace.getLeftDock();
|
|
dock.getActivePane().addItem({
|
|
getTitle() {
|
|
return 'title';
|
|
},
|
|
element: document.createElement('div')
|
|
});
|
|
const state = {};
|
|
spyOn(atom, 'confirm');
|
|
atom.attemptRestoreProjectStateForPaths(
|
|
state,
|
|
[__dirname],
|
|
[__filename]
|
|
);
|
|
expect(atom.confirm).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the window is dirty', () => {
|
|
let editor;
|
|
|
|
beforeEach(async () => {
|
|
editor = await atom.workspace.open();
|
|
editor.setText('new editor');
|
|
});
|
|
|
|
describe('when a dock has a modified editor', () => {
|
|
it('prompts the user to restore the state', () => {
|
|
const dock = atom.workspace.getLeftDock();
|
|
dock.getActivePane().addItem(editor);
|
|
spyOn(atom, 'confirm').andReturn(1);
|
|
spyOn(atom.project, 'addPath');
|
|
spyOn(atom.workspace, 'open');
|
|
const state = Symbol('state');
|
|
atom.attemptRestoreProjectStateForPaths(
|
|
state,
|
|
[__dirname],
|
|
[__filename]
|
|
);
|
|
expect(atom.confirm).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it('prompts the user to restore the state in a new window, discarding it and adding folder to current window', async () => {
|
|
jasmine.useRealClock();
|
|
spyOn(atom, 'confirm').andCallFake((options, callback) => callback(1));
|
|
spyOn(atom.project, 'addPath');
|
|
spyOn(atom.workspace, 'open');
|
|
const state = Symbol('state');
|
|
|
|
atom.attemptRestoreProjectStateForPaths(
|
|
state,
|
|
[__dirname],
|
|
[__filename]
|
|
);
|
|
expect(atom.confirm).toHaveBeenCalled();
|
|
await conditionPromise(() => atom.project.addPath.callCount === 1);
|
|
|
|
expect(atom.project.addPath).toHaveBeenCalledWith(__dirname);
|
|
expect(atom.workspace.open.callCount).toBe(1);
|
|
expect(atom.workspace.open).toHaveBeenCalledWith(__filename);
|
|
});
|
|
|
|
it('prompts the user to restore the state in a new window, opening a new window', async () => {
|
|
jasmine.useRealClock();
|
|
spyOn(atom, 'confirm').andCallFake((options, callback) => callback(0));
|
|
spyOn(atom, 'open');
|
|
const state = Symbol('state');
|
|
|
|
atom.attemptRestoreProjectStateForPaths(
|
|
state,
|
|
[__dirname],
|
|
[__filename]
|
|
);
|
|
expect(atom.confirm).toHaveBeenCalled();
|
|
await conditionPromise(() => atom.open.callCount === 1);
|
|
expect(atom.open).toHaveBeenCalledWith({
|
|
pathsToOpen: [__dirname, __filename],
|
|
newWindow: true,
|
|
devMode: atom.inDevMode(),
|
|
safeMode: atom.inSafeMode()
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('::unloadEditorWindow()', () => {
|
|
it('saves the BlobStore so it can be loaded after reload', () => {
|
|
const configDirPath = temp.mkdirSync('atom-spec-environment');
|
|
const fakeBlobStore = jasmine.createSpyObj('blob store', ['save']);
|
|
const atomEnvironment = new AtomEnvironment({
|
|
applicationDelegate: atom.applicationDelegate,
|
|
enablePersistence: true
|
|
});
|
|
atomEnvironment.initialize({
|
|
configDirPath,
|
|
blobStore: fakeBlobStore,
|
|
window,
|
|
document
|
|
});
|
|
|
|
atomEnvironment.unloadEditorWindow();
|
|
|
|
expect(fakeBlobStore.save).toHaveBeenCalled();
|
|
|
|
atomEnvironment.destroy();
|
|
});
|
|
});
|
|
|
|
describe('::destroy()', () => {
|
|
it('does not throw exceptions when unsubscribing from ipc events (regression)', async () => {
|
|
const fakeDocument = {
|
|
addEventListener() {},
|
|
removeEventListener() {},
|
|
head: document.createElement('head'),
|
|
body: document.createElement('body')
|
|
};
|
|
const atomEnvironment = new AtomEnvironment({
|
|
applicationDelegate: atom.applicationDelegate
|
|
});
|
|
atomEnvironment.initialize({ window, document: fakeDocument });
|
|
spyOn(atomEnvironment.packages, 'loadPackages').andReturn(
|
|
Promise.resolve()
|
|
);
|
|
spyOn(atomEnvironment.packages, 'activate').andReturn(Promise.resolve());
|
|
spyOn(atomEnvironment, 'displayWindow').andReturn(Promise.resolve());
|
|
await atomEnvironment.startEditorWindow();
|
|
atomEnvironment.unloadEditorWindow();
|
|
atomEnvironment.destroy();
|
|
});
|
|
});
|
|
|
|
describe('::whenShellEnvironmentLoaded()', () => {
|
|
let atomEnvironment, envLoaded, spy;
|
|
|
|
beforeEach(() => {
|
|
let resolvePromise = null;
|
|
const promise = new Promise(resolve => {
|
|
resolvePromise = resolve;
|
|
});
|
|
envLoaded = () => {
|
|
resolvePromise();
|
|
return promise;
|
|
};
|
|
atomEnvironment = new AtomEnvironment({
|
|
applicationDelegate: atom.applicationDelegate,
|
|
updateProcessEnv() {
|
|
return promise;
|
|
}
|
|
});
|
|
atomEnvironment.initialize({ window, document });
|
|
spy = jasmine.createSpy();
|
|
});
|
|
|
|
afterEach(() => atomEnvironment.destroy());
|
|
|
|
it('is triggered once the shell environment is loaded', async () => {
|
|
atomEnvironment.whenShellEnvironmentLoaded(spy);
|
|
atomEnvironment.updateProcessEnvAndTriggerHooks();
|
|
await envLoaded();
|
|
expect(spy).toHaveBeenCalled();
|
|
});
|
|
|
|
it('triggers the callback immediately if the shell environment is already loaded', async () => {
|
|
atomEnvironment.updateProcessEnvAndTriggerHooks();
|
|
await envLoaded();
|
|
atomEnvironment.whenShellEnvironmentLoaded(spy);
|
|
expect(spy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('::openLocations(locations)', () => {
|
|
beforeEach(() => {
|
|
atom.project.setPaths([]);
|
|
});
|
|
|
|
describe('when there is no saved state', () => {
|
|
beforeEach(() => {
|
|
spyOn(atom, 'loadState').andReturn(Promise.resolve(null));
|
|
});
|
|
|
|
describe('when the opened path exists', () => {
|
|
it('opens a file', async () => {
|
|
const pathToOpen = __filename;
|
|
await atom.openLocations([
|
|
{ pathToOpen, exists: true, isFile: true }
|
|
]);
|
|
expect(atom.project.getPaths()).toEqual([]);
|
|
});
|
|
|
|
it('opens a directory as a project folder', async () => {
|
|
const pathToOpen = __dirname;
|
|
await atom.openLocations([
|
|
{ pathToOpen, exists: true, isDirectory: true }
|
|
]);
|
|
expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual(
|
|
[]
|
|
);
|
|
expect(atom.project.getPaths()).toEqual([pathToOpen]);
|
|
});
|
|
});
|
|
|
|
describe('when the opened path does not exist', () => {
|
|
it('opens it as a new file', async () => {
|
|
const pathToOpen = path.join(
|
|
__dirname,
|
|
'this-path-does-not-exist.txt'
|
|
);
|
|
await atom.openLocations([{ pathToOpen, exists: false }]);
|
|
expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual(
|
|
[pathToOpen]
|
|
);
|
|
expect(atom.project.getPaths()).toEqual([]);
|
|
});
|
|
|
|
it('may be required to be an existing directory', async () => {
|
|
spyOn(atom.notifications, 'addWarning');
|
|
|
|
const nonExistent = path.join(__dirname, 'no');
|
|
const existingFile = __filename;
|
|
const existingDir = path.join(__dirname, 'fixtures');
|
|
|
|
await atom.openLocations([
|
|
{ pathToOpen: nonExistent, isDirectory: true },
|
|
{ pathToOpen: existingFile, isDirectory: true },
|
|
{ pathToOpen: existingDir, isDirectory: true }
|
|
]);
|
|
|
|
expect(atom.workspace.getTextEditors()).toEqual([]);
|
|
expect(atom.project.getPaths()).toEqual([existingDir]);
|
|
|
|
expect(atom.notifications.addWarning).toHaveBeenCalledWith(
|
|
'Unable to open project folders',
|
|
{
|
|
description: `The directories \`${nonExistent}\` and \`${existingFile}\` do not exist.`
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('when the opened path is handled by a registered directory provider', () => {
|
|
let serviceDisposable;
|
|
|
|
beforeEach(() => {
|
|
serviceDisposable = atom.packages.serviceHub.provide(
|
|
'atom.directory-provider',
|
|
'0.1.0',
|
|
{
|
|
directoryForURISync(uri) {
|
|
if (uri.startsWith('remote://')) {
|
|
return {
|
|
getPath() {
|
|
return uri;
|
|
}
|
|
};
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
waitsFor(() => atom.project.directoryProviders.length > 0);
|
|
});
|
|
|
|
afterEach(() => {
|
|
serviceDisposable.dispose();
|
|
});
|
|
|
|
it("adds it to the project's paths as is", async () => {
|
|
const pathToOpen = 'remote://server:7644/some/dir/path';
|
|
spyOn(atom.project, 'addPath');
|
|
await atom.openLocations([{ pathToOpen }]);
|
|
expect(atom.project.addPath).toHaveBeenCalledWith(pathToOpen);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when there is saved state for the relevant directories', () => {
|
|
const state = Symbol('savedState');
|
|
|
|
beforeEach(() => {
|
|
spyOn(atom, 'getStateKey').andCallFake(dirs => dirs.join(':'));
|
|
spyOn(atom, 'loadState').andCallFake(function(key) {
|
|
if (key === __dirname) {
|
|
return Promise.resolve(state);
|
|
} else {
|
|
return Promise.resolve(null);
|
|
}
|
|
});
|
|
spyOn(atom, 'attemptRestoreProjectStateForPaths');
|
|
});
|
|
|
|
describe('when there are no project folders', () => {
|
|
it('attempts to restore the project state', async () => {
|
|
const pathToOpen = __dirname;
|
|
await atom.openLocations([{ pathToOpen, isDirectory: true }]);
|
|
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
|
|
state,
|
|
[pathToOpen],
|
|
[]
|
|
);
|
|
expect(atom.project.getPaths()).toEqual([]);
|
|
});
|
|
|
|
it('includes missing mandatory project folders in computation of initial state key', async () => {
|
|
const existingDir = path.join(__dirname, 'fixtures');
|
|
const missingDir = path.join(__dirname, 'no');
|
|
|
|
atom.loadState.andCallFake(function(key) {
|
|
if (key === `${existingDir}:${missingDir}`) {
|
|
return Promise.resolve(state);
|
|
} else {
|
|
return Promise.resolve(null);
|
|
}
|
|
});
|
|
|
|
await atom.openLocations([
|
|
{ pathToOpen: existingDir },
|
|
{ pathToOpen: missingDir, isDirectory: true }
|
|
]);
|
|
|
|
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
|
|
state,
|
|
[existingDir],
|
|
[]
|
|
);
|
|
expect(atom.project.getPaths(), [existingDir]);
|
|
});
|
|
|
|
it('opens the specified files', async () => {
|
|
await atom.openLocations([
|
|
{ pathToOpen: __dirname, isDirectory: true },
|
|
{ pathToOpen: __filename }
|
|
]);
|
|
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
|
|
state,
|
|
[__dirname],
|
|
[__filename]
|
|
);
|
|
expect(atom.project.getPaths()).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('when there are already project folders', () => {
|
|
beforeEach(() => atom.project.setPaths([__dirname]));
|
|
|
|
it('does not attempt to restore the project state, instead adding the project paths', async () => {
|
|
const pathToOpen = path.join(__dirname, 'fixtures');
|
|
await atom.openLocations([
|
|
{ pathToOpen, exists: true, isDirectory: true }
|
|
]);
|
|
expect(
|
|
atom.attemptRestoreProjectStateForPaths
|
|
).not.toHaveBeenCalled();
|
|
expect(atom.project.getPaths()).toEqual([__dirname, pathToOpen]);
|
|
});
|
|
|
|
it('opens the specified files', async () => {
|
|
const pathToOpen = path.join(__dirname, 'fixtures');
|
|
const fileToOpen = path.join(pathToOpen, 'michelle-is-awesome.txt');
|
|
await atom.openLocations([
|
|
{ pathToOpen, exists: true, isDirectory: true },
|
|
{ pathToOpen: fileToOpen, exists: true, isFile: true }
|
|
]);
|
|
expect(
|
|
atom.attemptRestoreProjectStateForPaths
|
|
).not.toHaveBeenCalledWith(state, [pathToOpen], [fileToOpen]);
|
|
expect(atom.project.getPaths()).toEqual([__dirname, pathToOpen]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('::getReleaseChannel()', () => {
|
|
let version;
|
|
|
|
beforeEach(() => {
|
|
spyOn(atom, 'getVersion').andCallFake(() => version);
|
|
});
|
|
|
|
it('returns the correct channel based on the version number', () => {
|
|
version = '1.5.6';
|
|
expect(atom.getReleaseChannel()).toBe('stable');
|
|
|
|
version = '1.5.0-beta10';
|
|
expect(atom.getReleaseChannel()).toBe('beta');
|
|
|
|
version = '1.7.0-dev-5340c91';
|
|
expect(atom.getReleaseChannel()).toBe('dev');
|
|
});
|
|
});
|
|
});
|