pulsar/spec/package-manager-spec.js

1362 lines
61 KiB
JavaScript
Raw Normal View History

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