pulsar/spec/package-manager-spec.js

1916 lines
71 KiB
JavaScript
Raw Normal View History

2019-05-31 19:33:56 +03:00
const path = require('path');
const url = require('url');
const Package = require('../src/package');
const PackageManager = require('../src/package-manager');
const temp = require('temp').track();
const fs = require('fs-plus');
const { Disposable } = require('atom');
const { buildKeydownEvent } = require('../src/keymap-extensions');
const { mockLocalStorage } = require('./spec-helper');
const ModuleCache = require('../src/module-cache');
2017-09-29 02:18:56 +03:00
describe('PackageManager', () => {
2019-05-31 19:33:56 +03:00
function createTestElement(className) {
const element = document.createElement('div');
element.className = className;
return element;
2017-09-29 02:18:56 +03:00
}
beforeEach(() => {
2019-05-31 19:33:56 +03:00
spyOn(ModuleCache, 'add');
});
2017-09-29 02:18:56 +03:00
describe('initialize', () => {
it('adds regular package path', () => {
2019-05-31 19:33:56 +03:00
const packageManger = new PackageManager({});
const configDirPath = path.join('~', 'someConfig');
packageManger.initialize({ configDirPath });
expect(packageManger.packageDirPaths.length).toBe(1);
2019-02-22 10:55:17 +03:00
expect(packageManger.packageDirPaths[0]).toBe(
path.join(configDirPath, 'packages')
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('adds regular package path, dev package path, and Atom repo package path in dev mode and dev resource path is set', () => {
2019-05-31 19:33:56 +03:00
const packageManger = new PackageManager({});
const configDirPath = path.join('~', 'someConfig');
const resourcePath = path.join('~', '/atom');
packageManger.initialize({ configDirPath, resourcePath, devMode: true });
expect(packageManger.packageDirPaths.length).toBe(3);
2019-02-22 10:55:17 +03:00
expect(packageManger.packageDirPaths).toContain(
path.join(configDirPath, 'packages')
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(packageManger.packageDirPaths).toContain(
path.join(configDirPath, 'dev', 'packages')
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(packageManger.packageDirPaths).toContain(
path.join(resourcePath, 'packages')
2019-05-31 19:33:56 +03:00
);
});
});
2017-09-29 02:18:56 +03:00
describe('::getApmPath()', () => {
it('returns the path to the apm command', () => {
2019-05-31 19:33:56 +03:00
let apmPath = path.join(
process.resourcesPath,
'app',
'apm',
'bin',
'apm'
);
2017-09-29 02:18:56 +03:00
if (process.platform === 'win32') {
2019-05-31 19:33:56 +03:00
apmPath += '.cmd';
2017-09-29 02:18:56 +03:00
}
2019-05-31 19:33:56 +03:00
expect(atom.packages.getApmPath()).toBe(apmPath);
});
2017-09-29 02:18:56 +03:00
describe('when the core.apmPath setting is set', () => {
2019-05-31 19:33:56 +03:00
beforeEach(() => atom.config.set('core.apmPath', '/path/to/apm'));
2017-09-29 02:18:56 +03:00
2017-09-29 03:09:55 +03:00
it('returns the value of the core.apmPath config setting', () => {
2019-05-31 19:33:56 +03:00
expect(atom.packages.getApmPath()).toBe('/path/to/apm');
});
});
});
2017-09-29 02:18:56 +03:00
describe('::loadPackages()', () => {
2019-05-31 19:33:56 +03:00
beforeEach(() => spyOn(atom.packages, 'loadAvailablePackage'));
2017-09-29 02:18:56 +03:00
afterEach(async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackages();
atom.packages.unloadPackages();
});
2017-09-29 02:18:56 +03:00
it('sets hasLoadedInitialPackages', () => {
2019-05-31 19:33:56 +03:00
expect(atom.packages.hasLoadedInitialPackages()).toBe(false);
atom.packages.loadPackages();
expect(atom.packages.hasLoadedInitialPackages()).toBe(true);
});
});
2017-09-29 02:18:56 +03:00
describe('::loadPackage(name)', () => {
2019-05-31 19:33:56 +03:00
beforeEach(() => atom.config.set('core.disabledPackages', []));
2017-09-29 02:18:56 +03:00
it('returns the package', () => {
2019-05-31 19:33:56 +03:00
const pack = atom.packages.loadPackage('package-with-index');
expect(pack instanceof Package).toBe(true);
expect(pack.metadata.name).toBe('package-with-index');
});
2017-09-29 02:18:56 +03:00
it('returns the package if it has an invalid keymap', () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
const pack = atom.packages.loadPackage('package-with-broken-keymap');
expect(pack instanceof Package).toBe(true);
expect(pack.metadata.name).toBe('package-with-broken-keymap');
});
2017-09-29 02:18:56 +03:00
it('returns the package if it has an invalid stylesheet', () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
const pack = atom.packages.loadPackage('package-with-invalid-styles');
expect(pack instanceof Package).toBe(true);
expect(pack.metadata.name).toBe('package-with-invalid-styles');
expect(pack.stylesheets.length).toBe(0);
const addErrorHandler = jasmine.createSpy();
atom.notifications.onDidAddNotification(addErrorHandler);
expect(() => pack.reloadStylesheets()).not.toThrow();
expect(addErrorHandler.callCount).toBe(2);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[1][0].message).toContain(
'Failed to reload the package-with-invalid-styles package stylesheets'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[1][0].options.packageName).toEqual(
'package-with-invalid-styles'
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('returns null if the package has an invalid package.json', () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
const addErrorHandler = jasmine.createSpy();
atom.notifications.onDidAddNotification(addErrorHandler);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.loadPackage('package-with-broken-package-json')
2019-05-31 19:33:56 +03:00
).toBeNull();
expect(addErrorHandler.callCount).toBe(1);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].message).toContain(
'Failed to load the package-with-broken-package-json package'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
'package-with-broken-package-json'
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
2017-09-29 03:09:55 +03:00
it('returns null if the package name or path starts with a dot', () => {
2019-02-22 10:55:17 +03:00
expect(
atom.packages.loadPackage('/Users/user/.atom/packages/.git')
2019-05-31 19:33:56 +03:00
).toBeNull();
});
2017-09-29 02:18:56 +03:00
it('normalizes short repository urls in package.json', () => {
2019-02-22 10:55:17 +03:00
let { metadata } = atom.packages.loadPackage(
'package-with-short-url-package-json'
2019-05-31 19:33:56 +03:00
);
expect(metadata.repository.type).toBe('git');
expect(metadata.repository.url).toBe('https://github.com/example/repo');
({ metadata } = atom.packages.loadPackage(
2019-02-22 10:55:17 +03:00
'package-with-invalid-url-package-json'
2019-05-31 19:33:56 +03:00
));
expect(metadata.repository.type).toBe('git');
expect(metadata.repository.url).toBe('foo');
});
2017-09-29 02:18:56 +03:00
it('trims git+ from the beginning and .git from the end of repository URLs, even if npm already normalized them ', () => {
2019-02-22 10:55:17 +03:00
const { metadata } = atom.packages.loadPackage(
'package-with-prefixed-and-suffixed-repo-url'
2019-05-31 19:33:56 +03:00
);
expect(metadata.repository.type).toBe('git');
expect(metadata.repository.url).toBe('https://github.com/example/repo');
});
2017-09-29 02:18:56 +03:00
it('returns null if the package is not found in any package directory', () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'warn');
2019-02-22 10:55:17 +03:00
expect(
atom.packages.loadPackage('this-package-cannot-be-found')
2019-05-31 19:33:56 +03:00
).toBeNull();
expect(console.warn.callCount).toBe(1);
expect(console.warn.argsForCall[0][0]).toContain('Could not resolve');
});
2017-09-29 02:18:56 +03:00
describe('when the package is deprecated', () => {
it('returns null', () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'warn');
2019-02-22 10:55:17 +03:00
expect(
atom.packages.loadPackage(
path.join(__dirname, 'fixtures', 'packages', 'wordcount')
)
2019-05-31 19:33:56 +03:00
).toBeNull();
2019-02-22 10:55:17 +03:00
expect(atom.packages.isDeprecatedPackage('wordcount', '2.1.9')).toBe(
true
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.0')).toBe(
true
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.1')).toBe(
false
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.getDeprecatedPackageMetadata('wordcount').version
2019-05-31 19:33:56 +03:00
).toBe('<=2.2.0');
});
});
2017-09-29 02:18:56 +03:00
it('invokes ::onDidLoadPackage listeners with the loaded package', () => {
2019-05-31 19:33:56 +03:00
let loadedPackage = null;
2017-09-29 02:18:56 +03:00
atom.packages.onDidLoadPackage(pack => {
2019-05-31 19:33:56 +03:00
loadedPackage = pack;
});
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.loadPackage('package-with-main');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(loadedPackage.name).toBe('package-with-main');
});
2017-09-29 02:18:56 +03:00
it("registers any deserializers specified in the package's package.json", () => {
2019-05-31 19:33:56 +03:00
atom.packages.loadPackage('package-with-deserializers');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const state1 = { deserializer: 'Deserializer1', a: 'b' };
2017-09-29 02:18:56 +03:00
expect(atom.deserializers.deserialize(state1)).toEqual({
wasDeserializedBy: 'deserializeMethod1',
state: state1
2019-05-31 19:33:56 +03:00
});
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const state2 = { deserializer: 'Deserializer2', c: 'd' };
2017-09-29 02:18:56 +03:00
expect(atom.deserializers.deserialize(state2)).toEqual({
wasDeserializedBy: 'deserializeMethod2',
state: state2
2019-05-31 19:33:56 +03:00
});
});
2017-09-29 02:18:56 +03:00
it('early-activates any atom.directory-provider or atom.repository-provider services that the package provide', () => {
2019-05-31 19:33:56 +03:00
jasmine.useRealClock();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const providers = [];
2019-02-22 10:55:17 +03:00
atom.packages.serviceHub.consume(
'atom.directory-provider',
'^0.1.0',
provider => providers.push(provider)
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.loadPackage('package-with-directory-provider');
2019-02-22 10:55:17 +03:00
expect(providers.map(p => p.name)).toEqual([
'directory provider from package-with-directory-provider'
2019-05-31 19:33:56 +03:00
]);
});
2017-09-29 02:18:56 +03:00
describe("when there are view providers specified in the package's package.json", () => {
2019-05-31 19:33:56 +03:00
const model1 = { worksWithViewProvider1: true };
const model2 = { worksWithViewProvider2: true };
2017-09-29 02:18:56 +03:00
afterEach(async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-with-view-providers');
atom.packages.unloadPackage('package-with-view-providers');
});
2017-09-29 02:18:56 +03:00
it('does not load the view providers immediately', () => {
2019-05-31 19:33:56 +03:00
const pack = atom.packages.loadPackage('package-with-view-providers');
expect(pack.mainModule).toBeNull();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(() => atom.views.getView(model1)).toThrow();
expect(() => atom.views.getView(model2)).toThrow();
});
2017-09-29 02:18:56 +03:00
it('registers the view providers when the package is activated', async () => {
2019-05-31 19:33:56 +03:00
atom.packages.loadPackage('package-with-view-providers');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-view-providers');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const element1 = atom.views.getView(model1);
expect(element1 instanceof HTMLDivElement).toBe(true);
expect(element1.dataset.createdBy).toBe('view-provider-1');
2019-05-31 19:33:56 +03:00
const element2 = atom.views.getView(model2);
expect(element2 instanceof HTMLDivElement).toBe(true);
expect(element2.dataset.createdBy).toBe('view-provider-2');
});
2017-09-29 02:18:56 +03:00
it("registers the view providers when any of the package's deserializers are used", () => {
2019-05-31 19:33:56 +03:00
atom.packages.loadPackage('package-with-view-providers');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
spyOn(atom.views, 'addViewProvider').andCallThrough();
2017-09-29 02:18:56 +03:00
atom.deserializers.deserialize({
deserializer: 'DeserializerFromPackageWithViewProviders',
a: 'b'
2019-05-31 19:33:56 +03:00
});
expect(atom.views.addViewProvider.callCount).toBe(2);
2017-09-29 02:18:56 +03:00
atom.deserializers.deserialize({
deserializer: 'DeserializerFromPackageWithViewProviders',
a: 'b'
2019-05-31 19:33:56 +03:00
});
expect(atom.views.addViewProvider.callCount).toBe(2);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const element1 = atom.views.getView(model1);
expect(element1 instanceof HTMLDivElement).toBe(true);
expect(element1.dataset.createdBy).toBe('view-provider-1');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const element2 = atom.views.getView(model2);
expect(element2 instanceof HTMLDivElement).toBe(true);
expect(element2.dataset.createdBy).toBe('view-provider-2');
});
});
2017-09-29 02:18:56 +03:00
it("registers the config schema in the package's metadata, if present", () => {
2019-05-31 19:33:56 +03:00
let pack = atom.packages.loadPackage('package-with-json-config-schema');
2017-09-29 02:18:56 +03:00
expect(atom.config.getSchema('package-with-json-config-schema')).toEqual({
type: 'object',
properties: {
2019-02-22 10:55:17 +03:00
a: { type: 'number', default: 5 },
b: { type: 'string', default: 'five' }
2017-09-29 02:18:56 +03:00
}
2019-05-31 19:33:56 +03:00
});
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(pack.mainModule).toBeNull();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.unloadPackage('package-with-json-config-schema');
atom.config.clear();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
pack = atom.packages.loadPackage('package-with-json-config-schema');
2017-09-29 02:18:56 +03:00
expect(atom.config.getSchema('package-with-json-config-schema')).toEqual({
type: 'object',
properties: {
2019-02-22 10:55:17 +03:00
a: { type: 'number', default: 5 },
b: { type: 'string', default: 'five' }
2017-09-29 02:18:56 +03:00
}
2019-05-31 19:33:56 +03:00
});
});
2017-09-29 02:18:56 +03:00
describe('when a package does not have deserializers, view providers or a config schema in its package.json', () => {
2019-05-31 19:33:56 +03:00
beforeEach(() => mockLocalStorage());
2017-09-29 02:18:56 +03:00
it("defers loading the package's main module if the package previously used no Atom APIs when its main module was required", () => {
2019-05-31 19:33:56 +03:00
const pack1 = atom.packages.loadPackage('package-with-main');
expect(pack1.mainModule).toBeDefined();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.unloadPackage('package-with-main');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const pack2 = atom.packages.loadPackage('package-with-main');
expect(pack2.mainModule).toBeNull();
});
2017-09-29 02:18:56 +03:00
it("does not defer loading the package's main module if the package previously used Atom APIs when its main module was required", () => {
2019-02-22 10:55:17 +03:00
const pack1 = atom.packages.loadPackage(
'package-with-eval-time-api-calls'
2019-05-31 19:33:56 +03:00
);
expect(pack1.mainModule).toBeDefined();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.unloadPackage('package-with-eval-time-api-calls');
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
const pack2 = atom.packages.loadPackage(
'package-with-eval-time-api-calls'
2019-05-31 19:33:56 +03:00
);
expect(pack2.mainModule).not.toBeNull();
});
});
});
2017-09-29 02:18:56 +03:00
describe('::loadAvailablePackage(availablePackage)', () => {
describe('if the package was preloaded', () => {
it('adds the package path to the module cache', () => {
2019-02-22 10:55:17 +03:00
const availablePackage = atom.packages
.getAvailablePackages()
2019-05-31 19:33:56 +03:00
.find(p => p.name === 'spell-check');
availablePackage.isBundled = true;
2019-02-22 10:55:17 +03:00
expect(
atom.packages.preloadedPackages[availablePackage.name]
2019-05-31 19:33:56 +03:00
).toBeUndefined();
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
false
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const metadata = atom.packages.loadPackageMetadata(availablePackage);
2019-02-22 10:55:17 +03:00
atom.packages.preloadPackage(availablePackage.name, {
rootDirPath: path.relative(
atom.packages.resourcePath,
availablePackage.path
),
metadata
2019-05-31 19:33:56 +03:00
});
atom.packages.loadAvailablePackage(availablePackage);
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(true);
2019-02-22 10:55:17 +03:00
expect(ModuleCache.add).toHaveBeenCalledWith(
availablePackage.path,
metadata
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('deactivates it if it had been disabled', () => {
2019-02-22 10:55:17 +03:00
const availablePackage = atom.packages
.getAvailablePackages()
2019-05-31 19:33:56 +03:00
.find(p => p.name === 'spell-check');
availablePackage.isBundled = true;
2019-02-22 10:55:17 +03:00
expect(
atom.packages.preloadedPackages[availablePackage.name]
2019-05-31 19:33:56 +03:00
).toBeUndefined();
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
false
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const metadata = atom.packages.loadPackageMetadata(availablePackage);
2017-09-29 02:18:56 +03:00
const preloadedPackage = atom.packages.preloadPackage(
availablePackage.name,
{
2019-02-22 10:55:17 +03:00
rootDirPath: path.relative(
atom.packages.resourcePath,
availablePackage.path
),
2017-09-29 02:18:56 +03:00
metadata
}
2019-05-31 19:33:56 +03:00
);
expect(preloadedPackage.keymapActivated).toBe(true);
expect(preloadedPackage.settingsActivated).toBe(true);
expect(preloadedPackage.menusActivated).toBe(true);
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
atom.packages.loadAvailablePackage(
availablePackage,
new Set([availablePackage.name])
2019-05-31 19:33:56 +03:00
);
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
false
);
expect(preloadedPackage.keymapActivated).toBe(false);
expect(preloadedPackage.settingsActivated).toBe(false);
expect(preloadedPackage.menusActivated).toBe(false);
});
2017-09-29 02:18:56 +03:00
it('deactivates it and reloads the new one if trying to load the same package outside of the bundle', () => {
2019-02-22 10:55:17 +03:00
const availablePackage = atom.packages
.getAvailablePackages()
2019-05-31 19:33:56 +03:00
.find(p => p.name === 'spell-check');
availablePackage.isBundled = true;
2019-02-22 10:55:17 +03:00
expect(
atom.packages.preloadedPackages[availablePackage.name]
2019-05-31 19:33:56 +03:00
).toBeUndefined();
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(
false
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const metadata = atom.packages.loadPackageMetadata(availablePackage);
2017-09-29 02:18:56 +03:00
const preloadedPackage = atom.packages.preloadPackage(
availablePackage.name,
{
2019-02-22 10:55:17 +03:00
rootDirPath: path.relative(
atom.packages.resourcePath,
availablePackage.path
),
2017-09-29 02:18:56 +03:00
metadata
}
2019-05-31 19:33:56 +03:00
);
expect(preloadedPackage.keymapActivated).toBe(true);
expect(preloadedPackage.settingsActivated).toBe(true);
expect(preloadedPackage.menusActivated).toBe(true);
availablePackage.isBundled = false;
atom.packages.loadAvailablePackage(availablePackage);
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(true);
expect(preloadedPackage.keymapActivated).toBe(false);
expect(preloadedPackage.settingsActivated).toBe(false);
expect(preloadedPackage.menusActivated).toBe(false);
});
});
2017-09-29 02:18:56 +03:00
describe('if the package was not preloaded', () => {
it('adds the package path to the module cache', () => {
2019-02-22 10:55:17 +03:00
const availablePackage = atom.packages
.getAvailablePackages()
2019-05-31 19:33:56 +03:00
.find(p => p.name === 'spell-check');
availablePackage.isBundled = true;
const metadata = atom.packages.loadPackageMetadata(availablePackage);
atom.packages.loadAvailablePackage(availablePackage);
2019-02-22 10:55:17 +03:00
expect(ModuleCache.add).toHaveBeenCalledWith(
availablePackage.path,
metadata
2019-05-31 19:33:56 +03:00
);
});
});
});
2017-09-29 02:18:56 +03:00
describe('preloading', () => {
it('requires the main module, loads the config schema and activates keymaps, menus and settings without reactivating them during package activation', () => {
2019-02-22 10:55:17 +03:00
const availablePackage = atom.packages
.getAvailablePackages()
2019-05-31 19:33:56 +03:00
.find(p => p.name === 'spell-check');
availablePackage.isBundled = true;
const metadata = atom.packages.loadPackageMetadata(availablePackage);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.preloadedPackages[availablePackage.name]
2019-05-31 19:33:56 +03:00
).toBeUndefined();
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.packagesCache = {};
2017-09-29 02:18:56 +03:00
atom.packages.packagesCache[availablePackage.name] = {
main: path.join(availablePackage.path, metadata.main),
grammarPaths: []
2019-05-31 19:33:56 +03:00
};
2017-09-29 02:18:56 +03:00
const preloadedPackage = atom.packages.preloadPackage(
availablePackage.name,
{
2019-02-22 10:55:17 +03:00
rootDirPath: path.relative(
atom.packages.resourcePath,
availablePackage.path
),
2017-09-29 02:18:56 +03:00
metadata
}
2019-05-31 19:33:56 +03:00
);
expect(preloadedPackage.keymapActivated).toBe(true);
expect(preloadedPackage.settingsActivated).toBe(true);
expect(preloadedPackage.menusActivated).toBe(true);
expect(preloadedPackage.mainModule).toBeTruthy();
expect(preloadedPackage.configSchemaRegisteredOnLoad).toBeTruthy();
spyOn(atom.keymaps, 'add');
spyOn(atom.menu, 'add');
spyOn(atom.contextMenu, 'add');
spyOn(atom.config, 'setSchema');
atom.packages.loadAvailablePackage(availablePackage);
2019-02-22 10:55:17 +03:00
expect(preloadedPackage.getMainModulePath()).toBe(
path.join(availablePackage.path, metadata.main)
2019-05-31 19:33:56 +03:00
);
atom.packages.activatePackage(availablePackage.name);
expect(atom.keymaps.add).not.toHaveBeenCalled();
expect(atom.menu.add).not.toHaveBeenCalled();
expect(atom.contextMenu.add).not.toHaveBeenCalled();
expect(atom.config.setSchema).not.toHaveBeenCalled();
expect(preloadedPackage.keymapActivated).toBe(true);
expect(preloadedPackage.settingsActivated).toBe(true);
expect(preloadedPackage.menusActivated).toBe(true);
expect(preloadedPackage.mainModule).toBeTruthy();
expect(preloadedPackage.configSchemaRegisteredOnLoad).toBeTruthy();
});
2017-09-29 02:18:56 +03:00
it('deactivates disabled keymaps during package activation', () => {
2019-02-22 10:55:17 +03:00
const availablePackage = atom.packages
.getAvailablePackages()
2019-05-31 19:33:56 +03:00
.find(p => p.name === 'spell-check');
availablePackage.isBundled = true;
const metadata = atom.packages.loadPackageMetadata(availablePackage);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.preloadedPackages[availablePackage.name]
2019-05-31 19:33:56 +03:00
).toBeUndefined();
expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.packagesCache = {};
2017-09-29 02:18:56 +03:00
atom.packages.packagesCache[availablePackage.name] = {
main: path.join(availablePackage.path, metadata.main),
grammarPaths: []
2019-05-31 19:33:56 +03:00
};
2017-09-29 02:18:56 +03:00
const preloadedPackage = atom.packages.preloadPackage(
availablePackage.name,
{
2019-02-22 10:55:17 +03:00
rootDirPath: path.relative(
atom.packages.resourcePath,
availablePackage.path
),
2017-09-29 02:18:56 +03:00
metadata
}
2019-05-31 19:33:56 +03:00
);
expect(preloadedPackage.keymapActivated).toBe(true);
expect(preloadedPackage.settingsActivated).toBe(true);
expect(preloadedPackage.menusActivated).toBe(true);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.loadAvailablePackage(availablePackage);
2019-02-22 10:55:17 +03:00
atom.config.set('core.packagesWithKeymapsDisabled', [
availablePackage.name
2019-05-31 19:33:56 +03:00
]);
atom.packages.activatePackage(availablePackage.name);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(preloadedPackage.keymapActivated).toBe(false);
expect(preloadedPackage.settingsActivated).toBe(true);
expect(preloadedPackage.menusActivated).toBe(true);
});
});
2017-09-29 02:18:56 +03:00
describe('::unloadPackage(name)', () => {
describe('when the package is active', () => {
it('throws an error', async () => {
2019-05-31 19:33:56 +03:00
const pack = await atom.packages.activatePackage('package-with-main');
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy();
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy();
2019-05-31 19:33:56 +03:00
expect(() => atom.packages.unloadPackage(pack.name)).toThrow();
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy();
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy();
});
});
2017-09-29 02:18:56 +03:00
describe('when the package is not loaded', () => {
it('throws an error', () => {
2019-05-31 19:33:56 +03:00
expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy();
expect(() => atom.packages.unloadPackage('unloaded')).toThrow();
expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy();
});
});
2017-09-29 02:18:56 +03:00
describe('when the package is loaded', () => {
it('no longers reports it as being loaded', () => {
2019-05-31 19:33:56 +03:00
const pack = atom.packages.loadPackage('package-with-main');
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy();
atom.packages.unloadPackage(pack.name);
expect(atom.packages.isPackageLoaded(pack.name)).toBeFalsy();
});
});
2017-09-29 02:18:56 +03:00
it('invokes ::onDidUnloadPackage listeners with the unloaded package', () => {
2019-05-31 19:33:56 +03:00
atom.packages.loadPackage('package-with-main');
let unloadedPackage;
2017-09-29 02:18:56 +03:00
atom.packages.onDidUnloadPackage(pack => {
2019-05-31 19:33:56 +03:00
unloadedPackage = pack;
});
atom.packages.unloadPackage('package-with-main');
expect(unloadedPackage.name).toBe('package-with-main');
});
});
2017-09-29 02:18:56 +03:00
describe('::activatePackage(id)', () => {
describe('when called multiple times', () => {
it('it only calls activate on the package once', async () => {
2019-05-31 19:33:56 +03:00
spyOn(Package.prototype, 'activateNow').andCallThrough();
await atom.packages.activatePackage('package-with-index');
await atom.packages.activatePackage('package-with-index');
await atom.packages.activatePackage('package-with-index');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(Package.prototype.activateNow.callCount).toBe(1);
});
});
2017-09-29 02:18:56 +03:00
describe('when the package has a main module', () => {
2017-11-09 03:28:11 +03:00
beforeEach(() => {
2019-06-01 01:19:59 +03:00
spyOn(Package.prototype, 'requireMainModule').andCallThrough();
});
2017-11-09 03:28:11 +03:00
2017-09-29 02:18:56 +03:00
describe('when the metadata specifies a main module path˜', () => {
it('requires the module at the specified path', async () => {
2019-05-31 19:33:56 +03:00
const mainModule = require('./fixtures/packages/package-with-main/main-module');
spyOn(mainModule, 'activate');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const pack = await atom.packages.activatePackage('package-with-main');
expect(mainModule.activate).toHaveBeenCalled();
expect(pack.mainModule).toBe(mainModule);
});
});
2017-09-29 02:18:56 +03:00
describe('when the metadata does not specify a main module', () => {
it('requires index.coffee', async () => {
2019-05-31 19:33:56 +03:00
const indexModule = require('./fixtures/packages/package-with-index/index');
spyOn(indexModule, 'activate');
2019-05-31 19:33:56 +03:00
const pack = await atom.packages.activatePackage(
'package-with-index'
);
expect(indexModule.activate).toHaveBeenCalled();
expect(pack.mainModule).toBe(indexModule);
});
});
2017-09-29 02:18:56 +03:00
it('assigns config schema, including defaults when package contains a schema', async () => {
2019-02-22 10:55:17 +03:00
expect(
atom.config.get('package-with-config-schema.numbers.one')
2019-05-31 19:33:56 +03:00
).toBeUndefined();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-config-schema');
2019-02-22 10:55:17 +03:00
expect(atom.config.get('package-with-config-schema.numbers.one')).toBe(
1
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(atom.config.get('package-with-config-schema.numbers.two')).toBe(
2
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(
atom.config.set('package-with-config-schema.numbers.one', 'nope')
2019-05-31 19:33:56 +03:00
).toBe(false);
2019-02-22 10:55:17 +03:00
expect(
atom.config.set('package-with-config-schema.numbers.one', '10')
2019-05-31 19:33:56 +03:00
).toBe(true);
2019-02-22 10:55:17 +03:00
expect(atom.config.get('package-with-config-schema.numbers.one')).toBe(
10
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
describe('when the package metadata includes `activationCommands`', () => {
2019-05-31 19:33:56 +03:00
let mainModule, promise, workspaceCommandListener, registration;
2017-09-29 02:18:56 +03:00
beforeEach(() => {
2019-06-01 01:19:59 +03:00
jasmine.attachToDOM(atom.workspace.getElement());
mainModule = require('./fixtures/packages/package-with-activation-commands/index');
mainModule.activationCommandCallCount = 0;
spyOn(mainModule, 'activate').andCallThrough();
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
workspaceCommandListener = jasmine.createSpy(
'workspaceCommandListener'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
registration = atom.commands.add(
'atom-workspace',
2019-02-22 10:55:17 +03:00
'activation-command',
workspaceCommandListener
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
promise = atom.packages.activatePackage(
'package-with-activation-commands'
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
afterEach(() => {
if (registration) {
2019-05-31 19:33:56 +03:00
registration.dispose();
2017-09-29 02:18:56 +03:00
}
2019-05-31 19:33:56 +03:00
mainModule = null;
});
2017-09-29 02:18:56 +03:00
it('defers requiring/activating the main module until an activation event bubbles to the root view', async () => {
2019-05-31 19:33:56 +03:00
expect(Package.prototype.requireMainModule.callCount).toBe(0);
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
atom.workspace
.getElement()
.dispatchEvent(
new CustomEvent('activation-command', { bubbles: true })
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await promise;
expect(Package.prototype.requireMainModule.callCount).toBe(1);
});
2017-09-29 02:18:56 +03:00
it('triggers the activation event on all handlers registered during activation', async () => {
2019-05-31 19:33:56 +03:00
await atom.workspace.open();
2019-02-22 10:55:17 +03:00
const editorElement = atom.workspace
.getActiveTextEditor()
2019-05-31 19:33:56 +03:00
.getElement();
2019-02-22 10:55:17 +03:00
const editorCommandListener = jasmine.createSpy(
'editorCommandListener'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
atom.commands.add(
'atom-text-editor',
'activation-command',
editorCommandListener
2019-05-31 19:33:56 +03:00
);
2019-05-31 19:33:56 +03:00
atom.commands.dispatch(editorElement, 'activation-command');
expect(mainModule.activate.callCount).toBe(1);
expect(mainModule.activationCommandCallCount).toBe(1);
expect(editorCommandListener.callCount).toBe(1);
expect(workspaceCommandListener.callCount).toBe(1);
2019-05-31 19:33:56 +03:00
atom.commands.dispatch(editorElement, 'activation-command');
expect(mainModule.activationCommandCallCount).toBe(2);
expect(editorCommandListener.callCount).toBe(2);
expect(workspaceCommandListener.callCount).toBe(2);
expect(mainModule.activate.callCount).toBe(1);
});
2017-09-29 02:18:56 +03:00
it('activates the package immediately when the events are empty', async () => {
2019-05-31 19:33:56 +03:00
mainModule = require('./fixtures/packages/package-with-empty-activation-commands/index');
spyOn(mainModule, 'activate').andCallThrough();
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
atom.packages.activatePackage(
'package-with-empty-activation-commands'
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(mainModule.activate.callCount).toBe(1);
});
2017-09-29 02:18:56 +03:00
it('adds a notification when the activation commands are invalid', () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
const addErrorHandler = jasmine.createSpy();
atom.notifications.onDidAddNotification(addErrorHandler);
2019-02-22 10:55:17 +03:00
expect(() =>
atom.packages.activatePackage(
'package-with-invalid-activation-commands'
)
2019-05-31 19:33:56 +03:00
).not.toThrow();
expect(addErrorHandler.callCount).toBe(1);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].message).toContain(
'Failed to activate the package-with-invalid-activation-commands package'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
'package-with-invalid-activation-commands'
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('adds a notification when the context menu is invalid', () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
const addErrorHandler = jasmine.createSpy();
atom.notifications.onDidAddNotification(addErrorHandler);
2019-02-22 10:55:17 +03:00
expect(() =>
atom.packages.activatePackage('package-with-invalid-context-menu')
2019-05-31 19:33:56 +03:00
).not.toThrow();
expect(addErrorHandler.callCount).toBe(1);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].message).toContain(
'Failed to activate the package-with-invalid-context-menu package'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
'package-with-invalid-context-menu'
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('adds a notification when the grammar is invalid', async () => {
2019-05-31 19:33:56 +03:00
let notificationEvent;
2017-09-29 02:18:56 +03:00
await new Promise(resolve => {
2019-02-22 10:55:17 +03:00
const subscription = atom.notifications.onDidAddNotification(
event => {
2019-05-31 19:33:56 +03:00
notificationEvent = event;
subscription.dispose();
resolve();
2019-02-22 10:55:17 +03:00
}
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.activatePackage('package-with-invalid-grammar');
});
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
expect(notificationEvent.message).toContain(
'Failed to load a package-with-invalid-grammar package grammar'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(notificationEvent.options.packageName).toEqual(
'package-with-invalid-grammar'
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('adds a notification when the settings are invalid', async () => {
2019-05-31 19:33:56 +03:00
let notificationEvent;
2017-09-29 02:18:56 +03:00
await new Promise(resolve => {
2019-02-22 10:55:17 +03:00
const subscription = atom.notifications.onDidAddNotification(
event => {
2019-05-31 19:33:56 +03:00
notificationEvent = event;
subscription.dispose();
resolve();
2019-02-22 10:55:17 +03:00
}
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.activatePackage('package-with-invalid-settings');
});
2019-02-22 10:55:17 +03:00
expect(notificationEvent.message).toContain(
'Failed to load the package-with-invalid-settings package settings'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(notificationEvent.options.packageName).toEqual(
'package-with-invalid-settings'
2019-06-01 01:19:59 +03:00
);
});
});
2017-11-09 03:28:11 +03:00
describe('when the package metadata includes both activation commands and deserializers', () => {
2019-06-01 01:19:59 +03:00
let mainModule, promise, workspaceCommandListener, registration;
2017-11-09 03:28:11 +03:00
beforeEach(() => {
2019-06-01 01:19:59 +03:00
jasmine.attachToDOM(atom.workspace.getElement());
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
mainModule = require('./fixtures/packages/package-with-activation-commands-and-deserializers/index');
mainModule.activationCommandCallCount = 0;
spyOn(mainModule, 'activate').andCallThrough();
workspaceCommandListener = jasmine.createSpy(
'workspaceCommandListener'
);
registration = atom.commands.add(
'.workspace',
'activation-command-2',
workspaceCommandListener
);
2017-11-09 03:28:11 +03:00
2019-06-01 01:19:59 +03:00
promise = atom.packages.activatePackage(
'package-with-activation-commands-and-deserializers'
);
});
2017-11-09 03:28:11 +03:00
afterEach(() => {
if (registration) {
2019-06-01 01:19:59 +03:00
registration.dispose();
2017-11-09 03:28:11 +03:00
}
2019-06-01 01:19:59 +03:00
mainModule = null;
});
2017-11-09 03:28:11 +03:00
it('activates the package when a deserializer is called', async () => {
2019-06-01 01:19:59 +03:00
expect(Package.prototype.requireMainModule.callCount).toBe(0);
2017-11-09 03:28:11 +03:00
2019-06-01 01:19:59 +03:00
const state1 = { deserializer: 'Deserializer1', a: 'b' };
2017-12-04 22:44:12 +03:00
expect(atom.deserializers.deserialize(state1, atom)).toEqual({
2017-11-09 03:28:11 +03:00
wasDeserializedBy: 'deserializeMethod1',
state: state1
2019-06-01 01:19:59 +03:00
});
2017-11-09 03:28:11 +03:00
2019-06-01 01:19:59 +03:00
await promise;
expect(Package.prototype.requireMainModule.callCount).toBe(1);
});
2017-11-09 03:28:11 +03:00
it('defers requiring/activating the main module until an activation event bubbles to the root view', async () => {
2019-06-01 01:19:59 +03:00
expect(Package.prototype.requireMainModule.callCount).toBe(0);
2017-11-09 03:28:11 +03:00
2019-06-01 01:19:59 +03:00
atom.workspace
.getElement()
.dispatchEvent(
new CustomEvent('activation-command-2', { bubbles: true })
);
2017-11-09 03:28:11 +03:00
2019-06-01 01:19:59 +03:00
await promise;
expect(mainModule.activate.callCount).toBe(1);
expect(mainModule.activationCommandCallCount).toBe(1);
expect(Package.prototype.requireMainModule.callCount).toBe(1);
});
});
2017-09-29 02:18:56 +03:00
2017-11-15 03:16:00 +03:00
describe('when the package metadata includes `activationHooks`', () => {
2019-06-01 01:19:59 +03:00
let mainModule, promise;
2017-09-29 02:18:56 +03:00
2017-11-15 03:16:00 +03:00
beforeEach(() => {
2019-06-01 01:19:59 +03:00
mainModule = require('./fixtures/packages/package-with-activation-hooks/index');
spyOn(mainModule, 'activate').andCallThrough();
});
2017-09-29 02:18:56 +03:00
2017-11-15 03:16:00 +03:00
it('defers requiring/activating the main module until an triggering of an activation hook occurs', async () => {
2019-06-01 01:19:59 +03:00
promise = atom.packages.activatePackage(
'package-with-activation-hooks'
);
expect(Package.prototype.requireMainModule.callCount).toBe(0);
atom.packages.triggerActivationHook(
'language-fictitious:grammar-used'
);
atom.packages.triggerDeferredActivationHooks();
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
await promise;
expect(Package.prototype.requireMainModule.callCount).toBe(1);
});
2017-09-29 02:18:56 +03:00
2017-11-15 03:16:00 +03:00
it('does not double register activation hooks when deactivating and reactivating', async () => {
2019-06-01 01:19:59 +03:00
promise = atom.packages.activatePackage(
'package-with-activation-hooks'
);
expect(mainModule.activate.callCount).toBe(0);
atom.packages.triggerActivationHook(
'language-fictitious:grammar-used'
);
atom.packages.triggerDeferredActivationHooks();
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
await promise;
expect(mainModule.activate.callCount).toBe(1);
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
await atom.packages.deactivatePackage(
'package-with-activation-hooks'
);
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
promise = atom.packages.activatePackage(
'package-with-activation-hooks'
);
atom.packages.triggerActivationHook(
'language-fictitious:grammar-used'
);
atom.packages.triggerDeferredActivationHooks();
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
await promise;
expect(mainModule.activate.callCount).toBe(2);
});
2017-09-29 02:18:56 +03:00
2017-11-15 03:16:00 +03:00
it('activates the package immediately when activationHooks is empty', async () => {
2019-06-01 01:19:59 +03:00
mainModule = require('./fixtures/packages/package-with-empty-activation-hooks/index');
spyOn(mainModule, 'activate').andCallThrough();
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
expect(Package.prototype.requireMainModule.callCount).toBe(0);
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
await atom.packages.activatePackage(
'package-with-empty-activation-hooks'
);
expect(mainModule.activate.callCount).toBe(1);
expect(Package.prototype.requireMainModule.callCount).toBe(1);
});
2017-09-29 02:18:56 +03:00
2017-11-15 03:16:00 +03:00
it('activates the package immediately if the activation hook had already been triggered', async () => {
2019-06-01 01:19:59 +03:00
atom.packages.triggerActivationHook(
'language-fictitious:grammar-used'
);
atom.packages.triggerDeferredActivationHooks();
expect(Package.prototype.requireMainModule.callCount).toBe(0);
2017-09-29 02:18:56 +03:00
2019-06-01 01:19:59 +03:00
await atom.packages.activatePackage('package-with-activation-hooks');
expect(mainModule.activate.callCount).toBe(1);
expect(Package.prototype.requireMainModule.callCount).toBe(1);
});
});
2017-09-29 02:18:56 +03:00
2017-11-15 03:16:00 +03:00
describe('when the package metadata includes `workspaceOpeners`', () => {
2019-06-01 01:19:59 +03:00
let mainModule, promise;
2017-11-15 02:53:56 +03:00
2017-11-15 03:16:00 +03:00
beforeEach(() => {
2019-06-01 01:19:59 +03:00
mainModule = require('./fixtures/packages/package-with-workspace-openers/index');
spyOn(mainModule, 'activate').andCallThrough();
});
2017-11-15 02:53:56 +03:00
2017-11-15 03:16:00 +03:00
it('defers requiring/activating the main module until a registered opener is called', async () => {
2019-06-01 01:19:59 +03:00
promise = atom.packages.activatePackage(
'package-with-workspace-openers'
);
expect(Package.prototype.requireMainModule.callCount).toBe(0);
atom.workspace.open('atom://fictitious');
2017-11-15 02:53:56 +03:00
2019-06-01 01:19:59 +03:00
await promise;
expect(Package.prototype.requireMainModule.callCount).toBe(1);
expect(mainModule.openerCount).toBe(1);
});
2017-11-15 02:53:56 +03:00
2017-11-15 03:16:00 +03:00
it('activates the package immediately when the events are empty', async () => {
2019-06-01 01:19:59 +03:00
mainModule = require('./fixtures/packages/package-with-empty-workspace-openers/index');
spyOn(mainModule, 'activate').andCallThrough();
2017-11-15 02:53:56 +03:00
2019-06-01 01:19:59 +03:00
atom.packages.activatePackage('package-with-empty-workspace-openers');
2017-11-15 02:53:56 +03:00
2019-06-01 01:19:59 +03:00
expect(mainModule.activate.callCount).toBe(1);
});
});
});
2017-11-15 02:53:56 +03:00
2017-09-29 02:18:56 +03:00
describe('when the package has no main module', () => {
it('does not throw an exception', () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'error');
spyOn(console, 'warn').andCallThrough();
2019-02-22 10:55:17 +03:00
expect(() =>
atom.packages.activatePackage('package-without-module')
2019-05-31 19:33:56 +03:00
).not.toThrow();
expect(console.error).not.toHaveBeenCalled();
expect(console.warn).not.toHaveBeenCalled();
});
});
2017-09-29 02:18:56 +03:00
describe('when the package does not export an activate function', () => {
it('activates the package and does not throw an exception or log a warning', async () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'warn');
await atom.packages.activatePackage('package-with-no-activate');
expect(console.warn).not.toHaveBeenCalled();
});
});
2017-09-29 02:18:56 +03:00
it("passes the activate method the package's previously serialized state if it exists", async () => {
2019-02-22 10:55:17 +03:00
const pack = await atom.packages.activatePackage(
'package-with-serialization'
2019-05-31 19:33:56 +03:00
);
expect(pack.mainModule.someNumber).not.toBe(77);
pack.mainModule.someNumber = 77;
atom.packages.serializePackage('package-with-serialization');
await atom.packages.deactivatePackage('package-with-serialization');
2019-05-31 19:33:56 +03:00
spyOn(pack.mainModule, 'activate').andCallThrough();
await atom.packages.activatePackage('package-with-serialization');
expect(pack.mainModule.activate).toHaveBeenCalledWith({ someNumber: 77 });
});
2017-09-29 02:18:56 +03:00
it('invokes ::onDidActivatePackage listeners with the activated package', async () => {
2019-05-31 19:33:56 +03:00
let activatedPackage;
2017-09-29 02:18:56 +03:00
atom.packages.onDidActivatePackage(pack => {
2019-05-31 19:33:56 +03:00
activatedPackage = pack;
});
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-main');
expect(activatedPackage.name).toBe('package-with-main');
});
2017-09-29 02:18:56 +03:00
describe("when the package's main module throws an error on load", () => {
it('adds a notification instead of throwing an exception', () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
atom.config.set('core.disabledPackages', []);
const addErrorHandler = jasmine.createSpy();
atom.notifications.onDidAddNotification(addErrorHandler);
2019-02-22 10:55:17 +03:00
expect(() =>
atom.packages.activatePackage('package-that-throws-an-exception')
2019-05-31 19:33:56 +03:00
).not.toThrow();
expect(addErrorHandler.callCount).toBe(1);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].message).toContain(
'Failed to load the package-that-throws-an-exception package'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual(
'package-that-throws-an-exception'
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('re-throws the exception in test mode', () => {
2019-05-31 19:33:56 +03:00
atom.config.set('core.disabledPackages', []);
2019-02-22 10:55:17 +03:00
expect(() =>
atom.packages.activatePackage('package-that-throws-an-exception')
2019-05-31 19:33:56 +03:00
).toThrow('This package throws an exception');
});
});
2017-09-29 02:18:56 +03:00
describe('when the package is not found', () => {
it('rejects the promise', async () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'warn');
atom.config.set('core.disabledPackages', []);
2017-09-29 02:18:56 +03:00
try {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('this-doesnt-exist');
expect('Error to be thrown').toBe('');
} catch (error) {
2019-05-31 19:33:56 +03:00
expect(console.warn.callCount).toBe(1);
2019-02-22 10:55:17 +03:00
expect(error.message).toContain(
"Failed to load package 'this-doesnt-exist'"
2019-05-31 19:33:56 +03:00
);
}
2019-05-31 19:33:56 +03:00
});
});
2017-09-29 02:18:56 +03:00
describe('keymap loading', () => {
describe("when the metadata does not contain a 'keymaps' manifest", () => {
it('loads all the .cson/.json files in the keymaps directory', async () => {
2019-05-31 19:33:56 +03:00
const element1 = createTestElement('test-1');
const element2 = createTestElement('test-2');
const element3 = createTestElement('test-3');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element2
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element3
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-keymaps');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})[0].command
2019-05-31 19:33:56 +03:00
).toBe('test-1');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element2
})[0].command
2019-05-31 19:33:56 +03:00
).toBe('test-2');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element3
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
});
});
2017-09-29 02:18:56 +03:00
describe("when the metadata contains a 'keymaps' manifest", () => {
it('loads only the keymaps specified by the manifest, in the specified order', async () => {
2019-05-31 19:33:56 +03:00
const element1 = createTestElement('test-1');
const element3 = createTestElement('test-3');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-keymaps-manifest');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})[0].command
2019-05-31 19:33:56 +03:00
).toBe('keymap-1');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-n',
target: element1
})[0].command
2019-05-31 19:33:56 +03:00
).toBe('keymap-2');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-y',
target: element3
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
});
});
2017-09-29 02:18:56 +03:00
describe('when the keymap file is empty', () => {
it('does not throw an error on activation', async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-empty-keymap');
2019-02-22 10:55:17 +03:00
expect(
atom.packages.isPackageActive('package-with-empty-keymap')
2019-05-31 19:33:56 +03:00
).toBe(true);
});
});
2017-09-29 02:18:56 +03:00
describe("when the package's keymaps have been disabled", () => {
it('does not add the keymaps', async () => {
2019-05-31 19:33:56 +03:00
const element1 = createTestElement('test-1');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
atom.config.set('core.packagesWithKeymapsDisabled', [
'package-with-keymaps-manifest'
2019-05-31 19:33:56 +03:00
]);
await atom.packages.activatePackage('package-with-keymaps-manifest');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
});
});
2017-09-29 02:18:56 +03:00
describe('when setting core.packagesWithKeymapsDisabled', () => {
it("ignores package names in the array that aren't loaded", () => {
2019-05-31 19:33:56 +03:00
atom.packages.observePackagesWithKeymapsDisabled();
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
expect(() =>
atom.config.set('core.packagesWithKeymapsDisabled', [
'package-does-not-exist'
])
2019-05-31 19:33:56 +03:00
).not.toThrow();
2019-02-22 10:55:17 +03:00
expect(() =>
atom.config.set('core.packagesWithKeymapsDisabled', [])
2019-05-31 19:33:56 +03:00
).not.toThrow();
});
});
2017-09-29 02:18:56 +03:00
describe("when the package's keymaps are disabled and re-enabled after it is activated", () => {
it('removes and re-adds the keymaps', async () => {
2019-05-31 19:33:56 +03:00
const element1 = createTestElement('test-1');
atom.packages.observePackagesWithKeymapsDisabled();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-keymaps-manifest');
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
atom.config.set('core.packagesWithKeymapsDisabled', [
'package-with-keymaps-manifest'
2019-05-31 19:33:56 +03:00
]);
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.config.set('core.packagesWithKeymapsDisabled', []);
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: element1
})[0].command
2019-05-31 19:33:56 +03:00
).toBe('keymap-1');
});
});
2017-09-29 02:18:56 +03:00
describe('when the package is de-activated and re-activated', () => {
2019-05-31 19:33:56 +03:00
let element, events, userKeymapPath;
2017-09-29 02:18:56 +03:00
beforeEach(() => {
2019-05-31 19:33:56 +03:00
userKeymapPath = path.join(temp.mkdirSync(), 'user-keymaps.cson');
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn(userKeymapPath);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
element = createTestElement('test-1');
jasmine.attachToDOM(element);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
events = [];
element.addEventListener('user-command', e => events.push(e));
element.addEventListener('test-1', e => events.push(e));
});
2017-09-29 02:18:56 +03:00
afterEach(() => {
2019-05-31 19:33:56 +03:00
element.remove();
2017-09-29 02:18:56 +03:00
// Avoid leaking user keymap subscription
2019-05-31 19:33:56 +03:00
atom.keymaps.watchSubscriptions[userKeymapPath].dispose();
delete atom.keymaps.watchSubscriptions[userKeymapPath];
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
temp.cleanupSync();
});
2017-09-29 02:18:56 +03:00
it("doesn't override user-defined keymaps", async () => {
2019-02-22 10:55:17 +03:00
fs.writeFileSync(
userKeymapPath,
`".test-1": {"ctrl-z": "user-command"}`
2019-05-31 19:33:56 +03:00
);
atom.keymaps.loadUserKeymap();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-keymaps');
2019-02-22 10:55:17 +03:00
atom.keymaps.handleKeyboardEvent(
buildKeydownEvent('z', { ctrl: true, target: element })
2019-05-31 19:33:56 +03:00
);
expect(events.length).toBe(1);
expect(events[0].type).toBe('user-command');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-with-keymaps');
await atom.packages.activatePackage('package-with-keymaps');
2019-02-22 10:55:17 +03:00
atom.keymaps.handleKeyboardEvent(
buildKeydownEvent('z', { ctrl: true, target: element })
2019-05-31 19:33:56 +03:00
);
expect(events.length).toBe(2);
expect(events[1].type).toBe('user-command');
});
});
});
2017-09-29 02:18:56 +03:00
describe('menu loading', () => {
beforeEach(() => {
2019-05-31 19:33:56 +03:00
atom.contextMenu.definitions = [];
atom.menu.template = [];
});
2017-09-29 02:18:56 +03:00
describe("when the metadata does not contain a 'menus' manifest", () => {
it('loads all the .cson/.json files in the menus directory', async () => {
2019-05-31 19:33:56 +03:00
const element = createTestElement('test-1');
expect(atom.contextMenu.templateForElement(element)).toEqual([]);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-menus');
expect(atom.menu.template.length).toBe(2);
expect(atom.menu.template[0].label).toBe('Second to Last');
expect(atom.menu.template[1].label).toBe('Last');
2019-02-22 10:55:17 +03:00
expect(atom.contextMenu.templateForElement(element)[0].label).toBe(
'Menu item 1'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(atom.contextMenu.templateForElement(element)[1].label).toBe(
'Menu item 2'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(atom.contextMenu.templateForElement(element)[2].label).toBe(
'Menu item 3'
2019-05-31 19:33:56 +03:00
);
});
});
2017-09-29 02:18:56 +03:00
describe("when the metadata contains a 'menus' manifest", () => {
it('loads only the menus specified by the manifest, in the specified order', async () => {
2019-05-31 19:33:56 +03:00
const element = createTestElement('test-1');
expect(atom.contextMenu.templateForElement(element)).toEqual([]);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-menus-manifest');
expect(atom.menu.template[0].label).toBe('Second to Last');
expect(atom.menu.template[1].label).toBe('Last');
2019-02-22 10:55:17 +03:00
expect(atom.contextMenu.templateForElement(element)[0].label).toBe(
'Menu item 2'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(atom.contextMenu.templateForElement(element)[1].label).toBe(
'Menu item 1'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(
atom.contextMenu.templateForElement(element)[2]
2019-05-31 19:33:56 +03:00
).toBeUndefined();
});
});
2017-09-29 02:18:56 +03:00
describe('when the menu file is empty', () => {
it('does not throw an error on activation', async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-empty-menu');
2019-02-22 10:55:17 +03:00
expect(atom.packages.isPackageActive('package-with-empty-menu')).toBe(
true
2019-05-31 19:33:56 +03:00
);
});
});
});
2017-09-29 02:18:56 +03:00
describe('stylesheet loading', () => {
describe("when the metadata contains a 'styleSheets' manifest", () => {
it('loads style sheets from the styles directory as specified by the manifest', async () => {
2019-02-22 10:55:17 +03:00
const one = require.resolve(
'./fixtures/packages/package-with-style-sheets-manifest/styles/1.css'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const two = require.resolve(
'./fixtures/packages/package-with-style-sheets-manifest/styles/2.less'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const three = require.resolve(
'./fixtures/packages/package-with-style-sheets-manifest/styles/3.css'
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(atom.themes.stylesheetElementForId(one)).toBeNull();
expect(atom.themes.stylesheetElementForId(two)).toBeNull();
expect(atom.themes.stylesheetElementForId(three)).toBeNull();
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
await atom.packages.activatePackage(
'package-with-style-sheets-manifest'
2019-05-31 19:33:56 +03:00
);
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull();
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull();
expect(atom.themes.stylesheetElementForId(three)).toBeNull();
2019-02-22 10:55:17 +03:00
expect(
getComputedStyle(document.querySelector('#jasmine-content'))
.fontSize
2019-05-31 19:33:56 +03:00
).toBe('1px');
});
});
2017-09-29 02:18:56 +03:00
describe("when the metadata does not contain a 'styleSheets' manifest", () => {
it('loads all style sheets from the styles directory', async () => {
2019-02-22 10:55:17 +03:00
const one = require.resolve(
'./fixtures/packages/package-with-styles/styles/1.css'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const two = require.resolve(
'./fixtures/packages/package-with-styles/styles/2.less'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const three = require.resolve(
'./fixtures/packages/package-with-styles/styles/3.test-context.css'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const four = require.resolve(
'./fixtures/packages/package-with-styles/styles/4.css'
2019-05-31 19:33:56 +03:00
);
expect(atom.themes.stylesheetElementForId(one)).toBeNull();
expect(atom.themes.stylesheetElementForId(two)).toBeNull();
expect(atom.themes.stylesheetElementForId(three)).toBeNull();
expect(atom.themes.stylesheetElementForId(four)).toBeNull();
await atom.packages.activatePackage('package-with-styles');
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull();
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull();
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull();
expect(atom.themes.stylesheetElementForId(four)).not.toBeNull();
2019-02-22 10:55:17 +03:00
expect(
getComputedStyle(document.querySelector('#jasmine-content'))
.fontSize
2019-05-31 19:33:56 +03:00
).toBe('3px');
});
});
2017-09-29 02:18:56 +03:00
it("assigns the stylesheet's context based on the filename", async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-styles');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
let count = 0;
for (let styleElement of atom.styles.getStyleElements()) {
if (styleElement.sourcePath.match(/1.css/)) {
2019-05-31 19:33:56 +03:00
expect(styleElement.context).toBe(undefined);
count++;
}
2017-09-29 02:18:56 +03:00
if (styleElement.sourcePath.match(/2.less/)) {
2019-05-31 19:33:56 +03:00
expect(styleElement.context).toBe(undefined);
count++;
}
2017-09-29 02:18:56 +03:00
if (styleElement.sourcePath.match(/3.test-context.css/)) {
2019-05-31 19:33:56 +03:00
expect(styleElement.context).toBe('test-context');
count++;
}
2017-09-29 02:18:56 +03:00
if (styleElement.sourcePath.match(/4.css/)) {
2019-05-31 19:33:56 +03:00
expect(styleElement.context).toBe(undefined);
count++;
2017-09-29 02:18:56 +03:00
}
}
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(count).toBe(4);
});
});
2017-09-29 02:18:56 +03:00
describe('grammar loading', () => {
it("loads the package's grammars", async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-grammars');
expect(atom.grammars.selectGrammar('a.alot').name).toBe('Alot');
expect(atom.grammars.selectGrammar('a.alittle').name).toBe('Alittle');
});
it('loads any tree-sitter grammars defined in the package', async () => {
2019-05-31 19:33:56 +03:00
atom.config.set('core.useTreeSitterParsers', true);
await atom.packages.activatePackage('package-with-tree-sitter-grammar');
const grammar = atom.grammars.selectGrammar('test.somelang');
expect(grammar.name).toBe('Some Language');
expect(grammar.languageModule.isFakeTreeSitterParser).toBe(true);
});
});
2017-09-29 02:18:56 +03:00
describe('scoped-property loading', () => {
it('loads the scoped properties', async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-settings');
2019-02-22 10:55:17 +03:00
expect(
atom.config.get('editor.increaseIndentPattern', {
scope: ['.source.omg']
})
2019-05-31 19:33:56 +03:00
).toBe('^a');
});
});
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
describe('URI handler registration', () => {
2017-10-18 01:23:10 +03:00
it("registers the package's specified URI handler", async () => {
2019-05-31 19:33:56 +03:00
const uri = 'atom://package-with-uri-handler/some/url?with=args';
const mod = require('./fixtures/packages/package-with-uri-handler');
spyOn(mod, 'handleURI');
spyOn(atom.packages, 'hasLoadedInitialPackages').andReturn(true);
2019-02-22 10:55:17 +03:00
const activationPromise = atom.packages.activatePackage(
'package-with-uri-handler'
2019-05-31 19:33:56 +03:00
);
atom.dispatchURIMessage(uri);
await activationPromise;
expect(mod.handleURI).toHaveBeenCalledWith(url.parse(uri, true), uri);
});
});
2017-09-29 02:18:56 +03:00
describe('service registration', () => {
it("registers the package's provided and consumed services", async () => {
2019-05-31 19:33:56 +03:00
const consumerModule = require('./fixtures/packages/package-with-consumed-services');
2019-05-31 19:33:56 +03:00
let firstServiceV3Disposed = false;
let firstServiceV4Disposed = false;
let secondServiceDisposed = false;
2019-02-22 10:55:17 +03:00
spyOn(consumerModule, 'consumeFirstServiceV3').andReturn(
new Disposable(() => {
2019-05-31 19:33:56 +03:00
firstServiceV3Disposed = true;
2019-02-22 10:55:17 +03:00
})
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
spyOn(consumerModule, 'consumeFirstServiceV4').andReturn(
new Disposable(() => {
2019-05-31 19:33:56 +03:00
firstServiceV4Disposed = true;
2019-02-22 10:55:17 +03:00
})
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
spyOn(consumerModule, 'consumeSecondService').andReturn(
new Disposable(() => {
2019-05-31 19:33:56 +03:00
secondServiceDisposed = true;
2019-02-22 10:55:17 +03:00
})
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-consumed-services');
await atom.packages.activatePackage('package-with-provided-services');
expect(consumerModule.consumeFirstServiceV3.callCount).toBe(1);
2019-02-22 10:55:17 +03:00
expect(consumerModule.consumeFirstServiceV3).toHaveBeenCalledWith(
'first-service-v3'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(consumerModule.consumeFirstServiceV4).toHaveBeenCalledWith(
'first-service-v4'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(consumerModule.consumeSecondService).toHaveBeenCalledWith(
'second-service'
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
consumerModule.consumeFirstServiceV3.reset();
consumerModule.consumeFirstServiceV4.reset();
consumerModule.consumeSecondService.reset();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-with-provided-services');
expect(firstServiceV3Disposed).toBe(true);
expect(firstServiceV4Disposed).toBe(true);
expect(secondServiceDisposed).toBe(true);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-with-consumed-services');
await atom.packages.activatePackage('package-with-provided-services');
expect(consumerModule.consumeFirstServiceV3).not.toHaveBeenCalled();
expect(consumerModule.consumeFirstServiceV4).not.toHaveBeenCalled();
expect(consumerModule.consumeSecondService).not.toHaveBeenCalled();
});
2017-09-29 02:18:56 +03:00
it('ignores provided and consumed services that do not exist', async () => {
2019-05-31 19:33:56 +03:00
const addErrorHandler = jasmine.createSpy();
atom.notifications.onDidAddNotification(addErrorHandler);
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
await atom.packages.activatePackage(
'package-with-missing-consumed-services'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
await atom.packages.activatePackage(
'package-with-missing-provided-services'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.isPackageActive(
'package-with-missing-consumed-services'
)
2019-05-31 19:33:56 +03:00
).toBe(true);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.isPackageActive(
'package-with-missing-provided-services'
)
2019-05-31 19:33:56 +03:00
).toBe(true);
expect(addErrorHandler.callCount).toBe(0);
});
});
});
2017-09-29 02:18:56 +03:00
describe('::serialize', () => {
it('does not serialize packages that threw an error during activation', async () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
spyOn(console, 'warn');
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
const badPack = await atom.packages.activatePackage(
'package-that-throws-on-activate'
2019-05-31 19:33:56 +03:00
);
spyOn(badPack.mainModule, 'serialize').andCallThrough();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.serialize();
expect(badPack.mainModule.serialize).not.toHaveBeenCalled();
});
2017-09-29 02:18:56 +03:00
it("absorbs exceptions that are thrown by the package module's serialize method", async () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'error');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-serialize-error');
await atom.packages.activatePackage('package-with-serialization');
atom.packages.serialize();
2019-02-22 10:55:17 +03:00
expect(
atom.packages.packageStates['package-with-serialize-error']
2019-05-31 19:33:56 +03:00
).toBeUndefined();
2019-02-22 10:55:17 +03:00
expect(atom.packages.packageStates['package-with-serialization']).toEqual(
{ someNumber: 1 }
2019-05-31 19:33:56 +03:00
);
expect(console.error).toHaveBeenCalled();
});
});
2017-09-29 02:18:56 +03:00
describe('::deactivatePackages()', () => {
it('deactivates all packages but does not serialize them', async () => {
2019-02-22 10:55:17 +03:00
const pack1 = await atom.packages.activatePackage(
'package-with-deactivate'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const pack2 = await atom.packages.activatePackage(
'package-with-serialization'
2019-05-31 19:33:56 +03:00
);
2019-05-31 19:33:56 +03:00
spyOn(pack1.mainModule, 'deactivate');
spyOn(pack2.mainModule, 'serialize');
await atom.packages.deactivatePackages();
expect(pack1.mainModule.deactivate).toHaveBeenCalled();
expect(pack2.mainModule.serialize).not.toHaveBeenCalled();
});
});
2017-09-29 02:18:56 +03:00
describe('::deactivatePackage(id)', () => {
2019-05-31 19:33:56 +03:00
afterEach(() => atom.packages.unloadPackages());
2017-09-29 02:18:56 +03:00
it("calls `deactivate` on the package's main module if activate was successful", async () => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
const pack = await atom.packages.activatePackage(
'package-with-deactivate'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.isPackageActive('package-with-deactivate')
2019-05-31 19:33:56 +03:00
).toBeTruthy();
spyOn(pack.mainModule, 'deactivate').andCallThrough();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-with-deactivate');
expect(pack.mainModule.deactivate).toHaveBeenCalled();
expect(atom.packages.isPackageActive('package-with-module')).toBeFalsy();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
spyOn(console, 'warn');
2019-02-22 10:55:17 +03:00
const badPack = await atom.packages.activatePackage(
'package-that-throws-on-activate'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
expect(
atom.packages.isPackageActive('package-that-throws-on-activate')
2019-05-31 19:33:56 +03:00
).toBeTruthy();
spyOn(badPack.mainModule, 'deactivate').andCallThrough();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-that-throws-on-activate');
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled();
2019-02-22 10:55:17 +03:00
expect(
atom.packages.isPackageActive('package-that-throws-on-activate')
2019-05-31 19:33:56 +03:00
).toBeFalsy();
});
2017-09-29 02:18:56 +03:00
it("absorbs exceptions that are thrown by the package module's deactivate method", async () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'error');
await atom.packages.activatePackage('package-that-throws-on-deactivate');
await atom.packages.deactivatePackage(
'package-that-throws-on-deactivate'
);
expect(console.error).toHaveBeenCalled();
});
2017-09-29 02:18:56 +03:00
it("removes the package's grammars", async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-grammars');
await atom.packages.deactivatePackage('package-with-grammars');
expect(atom.grammars.selectGrammar('a.alot').name).toBe('Null Grammar');
expect(atom.grammars.selectGrammar('a.alittle').name).toBe(
'Null Grammar'
);
});
2017-09-29 02:18:56 +03:00
it("removes the package's keymaps", async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-keymaps');
await atom.packages.deactivatePackage('package-with-keymaps');
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: createTestElement('test-1')
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
2019-02-22 10:55:17 +03:00
expect(
atom.keymaps.findKeyBindings({
keystrokes: 'ctrl-z',
target: createTestElement('test-2')
})
2019-05-31 19:33:56 +03:00
).toHaveLength(0);
});
2017-09-29 02:18:56 +03:00
it("removes the package's stylesheets", async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-styles');
await atom.packages.deactivatePackage('package-with-styles');
2017-09-29 02:18:56 +03:00
2019-02-22 10:55:17 +03:00
const one = require.resolve(
'./fixtures/packages/package-with-style-sheets-manifest/styles/1.css'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const two = require.resolve(
'./fixtures/packages/package-with-style-sheets-manifest/styles/2.less'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
const three = require.resolve(
'./fixtures/packages/package-with-style-sheets-manifest/styles/3.css'
2019-05-31 19:33:56 +03:00
);
expect(atom.themes.stylesheetElementForId(one)).not.toExist();
expect(atom.themes.stylesheetElementForId(two)).not.toExist();
expect(atom.themes.stylesheetElementForId(three)).not.toExist();
});
2017-09-29 02:18:56 +03:00
it("removes the package's scoped-properties", async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-settings');
2019-02-22 10:55:17 +03:00
expect(
atom.config.get('editor.increaseIndentPattern', {
scope: ['.source.omg']
})
2019-05-31 19:33:56 +03:00
).toBe('^a');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-with-settings');
2019-02-22 10:55:17 +03:00
expect(
atom.config.get('editor.increaseIndentPattern', {
scope: ['.source.omg']
})
2019-05-31 19:33:56 +03:00
).toBeUndefined();
});
2017-09-29 02:18:56 +03:00
it('invokes ::onDidDeactivatePackage listeners with the deactivated package', async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.activatePackage('package-with-main');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
let deactivatedPackage;
atom.packages.onDidDeactivatePackage(pack => {
2019-05-31 19:33:56 +03:00
deactivatedPackage = pack;
});
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackage('package-with-main');
expect(deactivatedPackage.name).toBe('package-with-main');
});
});
2017-09-29 02:18:56 +03:00
describe('::activate()', () => {
beforeEach(() => {
2019-05-31 19:33:56 +03:00
spyOn(atom, 'inSpecMode').andReturn(false);
jasmine.snapshotDeprecations();
spyOn(console, 'warn');
atom.packages.loadPackages();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const loadedPackages = atom.packages.getLoadedPackages();
expect(loadedPackages.length).toBeGreaterThan(0);
});
2017-09-29 02:18:56 +03:00
afterEach(async () => {
2019-05-31 19:33:56 +03:00
await atom.packages.deactivatePackages();
atom.packages.unloadPackages();
jasmine.restoreDeprecationsSnapshot();
});
2017-09-29 02:18:56 +03:00
it('sets hasActivatedInitialPackages', async () => {
2019-05-31 19:33:56 +03:00
spyOn(atom.styles, 'getUserStyleSheetPath').andReturn(null);
spyOn(atom.packages, 'activatePackages');
expect(atom.packages.hasActivatedInitialPackages()).toBe(false);
2019-05-31 19:33:56 +03:00
await atom.packages.activate();
expect(atom.packages.hasActivatedInitialPackages()).toBe(true);
});
2017-09-29 02:18:56 +03:00
it('activates all the packages, and none of the themes', () => {
2019-05-31 19:33:56 +03:00
const packageActivator = spyOn(atom.packages, 'activatePackages');
const themeActivator = spyOn(atom.themes, 'activatePackages');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.activate();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(packageActivator).toHaveBeenCalled();
expect(themeActivator).toHaveBeenCalled();
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const packages = packageActivator.mostRecentCall.args[0];
2019-02-22 10:55:17 +03:00
for (let pack of packages) {
2019-05-31 19:33:56 +03:00
expect(['atom', 'textmate']).toContain(pack.getType());
2019-02-22 10:55:17 +03:00
}
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const themes = themeActivator.mostRecentCall.args[0];
themes.map(theme => expect(['theme']).toContain(theme.getType()));
});
2017-09-29 02:18:56 +03:00
it('calls callbacks registered with ::onDidActivateInitialPackages', async () => {
2019-05-31 19:33:56 +03:00
const package1 = atom.packages.loadPackage('package-with-main');
const package2 = atom.packages.loadPackage('package-with-index');
2019-02-22 10:55:17 +03:00
const package3 = atom.packages.loadPackage(
'package-with-activation-commands'
2019-05-31 19:33:56 +03:00
);
2019-02-22 10:55:17 +03:00
spyOn(atom.packages, 'getLoadedPackages').andReturn([
package1,
package2,
package3
2019-05-31 19:33:56 +03:00
]);
spyOn(atom.themes, 'activatePackages');
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.activate();
2019-02-22 10:55:17 +03:00
await new Promise(resolve =>
atom.packages.onDidActivateInitialPackages(resolve)
2019-05-31 19:33:56 +03:00
);
2019-05-31 19:33:56 +03:00
jasmine.unspy(atom.packages, 'getLoadedPackages');
expect(atom.packages.getActivePackages().includes(package1)).toBe(true);
expect(atom.packages.getActivePackages().includes(package2)).toBe(true);
expect(atom.packages.getActivePackages().includes(package3)).toBe(false);
});
});
2017-09-29 02:18:56 +03:00
describe('::enablePackage(id) and ::disablePackage(id)', () => {
describe('with packages', () => {
it('enables a disabled package', async () => {
2019-05-31 19:33:56 +03:00
const packageName = 'package-with-main';
atom.config.pushAtKeyPath('core.disabledPackages', packageName);
atom.packages.observeDisabledPackages();
expect(atom.config.get('core.disabledPackages')).toContain(packageName);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
const pack = atom.packages.enablePackage(packageName);
2019-02-22 10:55:17 +03:00
await new Promise(resolve =>
atom.packages.onDidActivatePackage(resolve)
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(atom.packages.getLoadedPackages()).toContain(pack);
expect(atom.packages.getActivePackages()).toContain(pack);
2019-02-22 10:55:17 +03:00
expect(atom.config.get('core.disabledPackages')).not.toContain(
packageName
2019-05-31 19:33:56 +03:00
);
});
2017-09-29 02:18:56 +03:00
it('disables an enabled package', async () => {
2019-05-31 19:33:56 +03:00
const packageName = 'package-with-main';
const pack = await atom.packages.activatePackage(packageName);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.observeDisabledPackages();
2019-02-22 10:55:17 +03:00
expect(atom.config.get('core.disabledPackages')).not.toContain(
packageName
2019-05-31 19:33:56 +03:00
);
await new Promise(resolve => {
2019-05-31 19:33:56 +03:00
atom.packages.onDidDeactivatePackage(resolve);
atom.packages.disablePackage(packageName);
});
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(atom.packages.getActivePackages()).not.toContain(pack);
expect(atom.config.get('core.disabledPackages')).toContain(packageName);
});
2017-09-29 02:18:56 +03:00
it('returns null if the package cannot be loaded', () => {
2019-05-31 19:33:56 +03:00
spyOn(console, 'warn');
expect(atom.packages.enablePackage('this-doesnt-exist')).toBeNull();
expect(console.warn.callCount).toBe(1);
});
2017-09-29 02:18:56 +03:00
it('does not disable an already disabled package', () => {
2019-05-31 19:33:56 +03:00
const packageName = 'package-with-main';
atom.config.pushAtKeyPath('core.disabledPackages', packageName);
atom.packages.observeDisabledPackages();
expect(atom.config.get('core.disabledPackages')).toContain(packageName);
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
atom.packages.disablePackage(packageName);
2019-02-22 10:55:17 +03:00
const packagesDisabled = atom.config
.get('core.disabledPackages')
2019-05-31 19:33:56 +03:00
.filter(pack => pack === packageName);
expect(packagesDisabled.length).toEqual(1);
});
});
2017-09-29 02:18:56 +03:00
describe('with themes', () => {
2019-05-31 19:33:56 +03:00
beforeEach(() => atom.themes.activateThemes());
afterEach(() => atom.themes.deactivateThemes());
2017-09-29 02:18:56 +03:00
it('enables and disables a theme', async () => {
2019-05-31 19:33:56 +03:00
const packageName = 'theme-with-package-file';
expect(atom.config.get('core.themes')).not.toContain(packageName);
2019-02-22 10:55:17 +03:00
expect(atom.config.get('core.disabledPackages')).not.toContain(
packageName
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
// enabling of theme
2019-05-31 19:33:56 +03:00
const pack = atom.packages.enablePackage(packageName);
2019-02-22 10:55:17 +03:00
await new Promise(resolve =>
atom.packages.onDidActivatePackage(resolve)
2019-05-31 19:33:56 +03:00
);
expect(atom.packages.isPackageActive(packageName)).toBe(true);
expect(atom.config.get('core.themes')).toContain(packageName);
2019-02-22 10:55:17 +03:00
expect(atom.config.get('core.disabledPackages')).not.toContain(
packageName
2019-05-31 19:33:56 +03:00
);
2017-09-29 02:18:56 +03:00
await new Promise(resolve => {
2019-05-31 19:33:56 +03:00
atom.themes.onDidChangeActiveThemes(resolve);
atom.packages.disablePackage(packageName);
});
2017-09-29 02:18:56 +03:00
2019-05-31 19:33:56 +03:00
expect(atom.packages.getActivePackages()).not.toContain(pack);
expect(atom.config.get('core.themes')).not.toContain(packageName);
expect(atom.config.get('core.themes')).not.toContain(packageName);
2019-02-22 10:55:17 +03:00
expect(atom.config.get('core.disabledPackages')).not.toContain(
packageName
2019-05-31 19:33:56 +03:00
);
});
});
});
describe('::getAvailablePackageNames', () => {
it('detects a symlinked package', () => {
const packageSymLinkedSource = path.join(
__dirname,
'fixtures',
'packages',
'folder',
'package-symlinked'
);
const destination = path.join(
atom.packages.getPackageDirPaths()[0],
'package-symlinked'
);
if (!fs.isDirectorySync(destination)) {
fs.symlinkSync(packageSymLinkedSource, destination, 'junction');
}
const availablePackages = atom.packages.getAvailablePackageNames();
expect(availablePackages.includes('package-symlinked')).toBe(true);
fs.removeSync(destination);
});
});
2019-05-31 19:33:56 +03:00
});