mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Add atom.deactivatePackage(id)
It serializes the package state to the atom.packageStates hash when the package is deactivated, which means we will be able to test package serialization independent of the overall window lifecycle by just deactivating and re-activating the package.
This commit is contained in:
@@ -6,116 +6,100 @@ describe "the `atom` global", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
|
||||
describe "when a package is built and loaded", ->
|
||||
[extension, stylesheetPath] = []
|
||||
describe "package lifecycle methods", ->
|
||||
packageModule = null
|
||||
|
||||
beforeEach ->
|
||||
extension = require "package-with-module"
|
||||
stylesheetPath = fs.resolveOnLoadPath("fixtures/packages/package-with-module/stylesheets/styles.css")
|
||||
packageModule = require "package-with-module"
|
||||
|
||||
afterEach ->
|
||||
removeStylesheet(stylesheetPath)
|
||||
atom.deactivatePackages()
|
||||
|
||||
it "requires and activates the package's main module if it exists", ->
|
||||
spyOn(atom, 'activateAtomPackage').andCallThrough()
|
||||
atom.activatePackage("package-with-module")
|
||||
expect(atom.activateAtomPackage).toHaveBeenCalled()
|
||||
describe ".activatePackage(id)", ->
|
||||
stylesheetPath = null
|
||||
|
||||
it "logs warning instead of throwing an exception if a package fails to load", ->
|
||||
config.set("core.disabledPackages", [])
|
||||
spyOn(console, "warn")
|
||||
expect(-> atom.activatePackage("package-that-throws-an-exception")).not.toThrow()
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
beforeEach ->
|
||||
stylesheetPath = fs.resolveOnLoadPath("fixtures/packages/package-with-module/stylesheets/styles.css")
|
||||
|
||||
describe "keymap loading", ->
|
||||
describe "when package.json does not contain a 'keymaps' manifest", ->
|
||||
it "loads all the .cson/.json files in the keymaps directory", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
afterEach ->
|
||||
removeStylesheet(stylesheetPath)
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
it "requires and activates the package's main module if it exists", ->
|
||||
spyOn(packageModule, 'activate').andCallThrough()
|
||||
atom.activatePackage("package-with-module")
|
||||
expect(packageModule.activate).toHaveBeenCalledWith({})
|
||||
|
||||
atom.activatePackage("package-with-module")
|
||||
it "passes the package its previously serialized state if it exists", ->
|
||||
pack = atom.activatePackage("package-with-module")
|
||||
expect(pack.mainModule.someNumber).not.toBe 77
|
||||
pack.mainModule.someNumber = 77
|
||||
atom.deactivatePackage("package-with-module")
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1"
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2"
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
pack.requireMainModule() # deactivating the package nukes its main module, so we require it again to spy on it
|
||||
spyOn(pack.mainModule, 'activate').andCallThrough()
|
||||
|
||||
describe "when package.json contains a 'keymaps' manifest", ->
|
||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
atom.activatePackage("package-with-module")
|
||||
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
it "logs warning instead of throwing an exception if a package fails to load", ->
|
||||
config.set("core.disabledPackages", [])
|
||||
spyOn(console, "warn")
|
||||
expect(-> atom.activatePackage("package-that-throws-an-exception")).not.toThrow()
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
|
||||
atom.activatePackage("package-with-keymaps-manifest")
|
||||
describe "keymap loading", ->
|
||||
describe "when package.json does not contain a 'keymaps' manifest", ->
|
||||
it "loads all the .cson/.json files in the keymaps directory", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1'
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2'
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
|
||||
it "loads stylesheets associated with the package", ->
|
||||
stylesheetPath = fs.resolveOnLoadPath("fixtures/packages/package-with-module/stylesheets/styles.css")
|
||||
expect(stylesheetElementForId(stylesheetPath).length).toBe 0
|
||||
atom.activatePackage("package-with-module")
|
||||
expect(stylesheetElementForId(stylesheetPath).length).toBe 1
|
||||
atom.activatePackage("package-with-module")
|
||||
|
||||
describe "package lifecycle", ->
|
||||
describe "activation", ->
|
||||
it "calls activate on the package main with its previous state", ->
|
||||
pack = atom.activatePackage('package-with-module')
|
||||
spyOn(pack.mainModule, 'activate')
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1"
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2"
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
|
||||
serializedState = rootView.serialize()
|
||||
rootView.deactivate()
|
||||
describe "when package.json contains a 'keymaps' manifest", ->
|
||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
|
||||
RootView.deserialize(serializedState)
|
||||
atom.activatePackage('package-with-module')
|
||||
atom.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
expect(pack.mainModule.activate).toHaveBeenCalledWith(someNumber: 1)
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1'
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2'
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined()
|
||||
|
||||
describe "deactivation", ->
|
||||
it "deactivates and removes the package module from the package module map", ->
|
||||
pack = atom.activatePackage('package-with-module')
|
||||
expect(atom.activatedAtomPackages.length).toBe 1
|
||||
spyOn(pack.mainModule, "deactivate").andCallThrough()
|
||||
atom.deactivateAtomPackages()
|
||||
it "loads stylesheets associated with the package", ->
|
||||
stylesheetPath = fs.resolveOnLoadPath("fixtures/packages/package-with-module/stylesheets/styles.css")
|
||||
expect(stylesheetElementForId(stylesheetPath).length).toBe 0
|
||||
atom.activatePackage("package-with-module")
|
||||
expect(stylesheetElementForId(stylesheetPath).length).toBe 1
|
||||
|
||||
describe ".deactivatePackage(id)", ->
|
||||
it "calls `deactivate` on the package's main module and deletes the package's module reference and require cache entry", ->
|
||||
pack = atom.activatePackage("package-with-module")
|
||||
expect(atom.getActivePackage("package-with-module")).toBe pack
|
||||
spyOn(pack.mainModule, 'deactivate').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-with-module")
|
||||
expect(pack.mainModule.deactivate).toHaveBeenCalled()
|
||||
expect(atom.activatedAtomPackages.length).toBe 0
|
||||
|
||||
describe "serialization", ->
|
||||
it "uses previous serialization state on packages whose activation has been deferred", ->
|
||||
atom.atomPackageStates['package-with-activation-events'] = {previousData: 'exists'}
|
||||
unactivatedPackage = atom.activatePackage('package-with-activation-events')
|
||||
activatedPackage = atom.activatePackage('package-with-module')
|
||||
|
||||
expect(atom.serializeAtomPackages()).toEqual
|
||||
'package-with-module':
|
||||
'someNumber': 1
|
||||
'package-with-activation-events':
|
||||
'previousData': 'exists'
|
||||
|
||||
# ensure serialization occurs when the packageis activated
|
||||
unactivatedPackage.deferActivation = false
|
||||
unactivatedPackage.activate()
|
||||
expect(atom.serializeAtomPackages()).toEqual
|
||||
'package-with-module':
|
||||
'someNumber': 1
|
||||
'package-with-activation-events':
|
||||
'previousData': 'overwritten'
|
||||
expect(atom.getActivePackage("package-with-module")).toBeUndefined()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize methods", ->
|
||||
spyOn(console, 'error')
|
||||
atom.activatePackage('package-with-module', immediate: true)
|
||||
atom.activatePackage('package-with-serialize-error', immediate: true)
|
||||
|
||||
packageStates = atom.serializeAtomPackages()
|
||||
expect(packageStates['package-with-module']).toEqual someNumber: 1
|
||||
expect(packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
atom.deactivatePackages()
|
||||
expect(atom.packageStates['package-with-module']).toEqual someNumber: 1
|
||||
expect(atom.packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
describe ".getVersion(callback)", ->
|
||||
|
||||
@@ -4,8 +4,8 @@ module.exports =
|
||||
|
||||
someNumber: 0
|
||||
|
||||
activate: ->
|
||||
@someNumber = 1
|
||||
activate: ({@someNumber}) ->
|
||||
@someNumber ?= 1
|
||||
|
||||
deactivate: ->
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ beforeEach ->
|
||||
window.git = Git.open(window.project.getPath())
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.atomPackageStates = {}
|
||||
atom.packageStates = {}
|
||||
atom.loadedPackages = []
|
||||
spyOn(atom, 'saveWindowState')
|
||||
spyOn(atom, 'getSavedWindowState').andReturn(null)
|
||||
|
||||
@@ -11,6 +11,8 @@ class AtomPackage extends Package
|
||||
keymaps: null
|
||||
stylesheets: null
|
||||
grammars: null
|
||||
mainModulePath: null
|
||||
resolvedMainModulePath: false
|
||||
mainModule: null
|
||||
deferActivation: false
|
||||
|
||||
@@ -79,19 +81,33 @@ class AtomPackage extends Package
|
||||
try
|
||||
if @requireMainModule()
|
||||
config.setDefaults(@name, @mainModule.configDefaults)
|
||||
atom.activateAtomPackage(this)
|
||||
@mainModule.activate(atom.getPackageState(@name) ? {})
|
||||
catch e
|
||||
console.warn "Failed to activate package named '#{@name}'", e.stack
|
||||
|
||||
serialize: ->
|
||||
try
|
||||
@mainModule?.serialize?()
|
||||
catch e
|
||||
console.error "Error serializing package '#{@name}'", e.stack
|
||||
|
||||
deactivate: ->
|
||||
@mainModule?.deactivate?()
|
||||
|
||||
requireMainModule: ->
|
||||
return @mainModule if @mainModule
|
||||
mainPath =
|
||||
mainModulePath = @getMainModulePath()
|
||||
@mainModule = require(mainModulePath) if fs.isFile(mainModulePath)
|
||||
|
||||
getMainModulePath: ->
|
||||
return @mainModulePath if @resolvedMainModulePath
|
||||
@resolvedMainModulePath = true
|
||||
mainModulePath =
|
||||
if @metadata.main
|
||||
fs.join(@path, @metadata.main)
|
||||
else
|
||||
fs.join(@path, 'index')
|
||||
mainPath = fs.resolveExtension(mainPath, ["", _.keys(require.extensions)...])
|
||||
@mainModule = require(mainPath) if fs.isFile(mainPath)
|
||||
@mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
||||
|
||||
registerDeferredDeserializers: ->
|
||||
for deserializerName in @metadata.deferredDeserializers ? []
|
||||
|
||||
@@ -14,33 +14,23 @@ _.extend atom,
|
||||
pendingBrowserProcessCallbacks: {}
|
||||
loadedPackages: []
|
||||
activePackages: []
|
||||
activatedAtomPackages: []
|
||||
atomPackageStates: {}
|
||||
packageStates: {}
|
||||
presentingModal: false
|
||||
pendingModals: [[]]
|
||||
|
||||
getPathToOpen: ->
|
||||
@getWindowState('pathToOpen') ? window.location.params.pathToOpen
|
||||
|
||||
activateAtomPackage: (pack) ->
|
||||
@activatedAtomPackages.push(pack)
|
||||
pack.mainModule.activate(@atomPackageStates[pack.name] ? {})
|
||||
getPackageState: (name) ->
|
||||
@packageStates[name]
|
||||
|
||||
deactivateAtomPackages: ->
|
||||
pack.mainModule.deactivate?() for pack in @activatedAtomPackages
|
||||
@activatedAtomPackages = []
|
||||
setPackageState: (name, state) ->
|
||||
@packageStates[name] = state
|
||||
|
||||
serializeAtomPackages: ->
|
||||
packageStates = {}
|
||||
for pack in @loadedPackages
|
||||
if pack in @activatedAtomPackages
|
||||
try
|
||||
packageStates[pack.name] = pack.mainModule.serialize?()
|
||||
catch e
|
||||
console.error("Exception serializing '#{pack.name}' package's module\n", e.stack)
|
||||
else
|
||||
packageStates[pack.name] = @atomPackageStates[pack.name]
|
||||
packageStates
|
||||
for pack in @getActivePackages()
|
||||
@setPackageState(pack.name, state) if state = pack.serialize?()
|
||||
@packageStates
|
||||
|
||||
activatePackages: ->
|
||||
@activatePackage(pack.path) for pack in @getLoadedPackages()
|
||||
@@ -51,6 +41,21 @@ _.extend atom,
|
||||
pack.activate(options)
|
||||
pack
|
||||
|
||||
deactivatePackages: ->
|
||||
@deactivatePackage(pack.path) for pack in @getActivePackages()
|
||||
|
||||
deactivatePackage: (id) ->
|
||||
if pack = @getActivePackage(id)
|
||||
@setPackageState(pack.name, state) if state = pack.serialize?()
|
||||
pack.deactivate()
|
||||
_.remove(@activePackages, pack)
|
||||
else
|
||||
throw new Error("No active package for id '#{id}'")
|
||||
|
||||
getActivePackage: (id) ->
|
||||
if path = @resolvePackagePath(id)
|
||||
_.detect @activePackages, (pack) -> pack.path is path
|
||||
|
||||
isPackageActive: (id) ->
|
||||
if path = @resolvePackagePath(id)
|
||||
_.detect @activePackages, (pack) -> pack.path is path
|
||||
|
||||
@@ -30,7 +30,7 @@ class RootView extends View
|
||||
@subview 'panes', panes ? new PaneContainer
|
||||
|
||||
@deserialize: ({ panes, packages, projectPath }) ->
|
||||
atom.atomPackageStates = packages ? {}
|
||||
atom.packageStates = packages ? {}
|
||||
panes = deserialize(panes) if panes?.deserializer is 'PaneContainer'
|
||||
new RootView({panes})
|
||||
|
||||
@@ -95,7 +95,7 @@ class RootView extends View
|
||||
@focus() if onDom
|
||||
|
||||
deactivate: ->
|
||||
atom.deactivateAtomPackages()
|
||||
atom.deactivatePackages()
|
||||
@remove()
|
||||
|
||||
open: (path, options = {}) ->
|
||||
|
||||
@@ -36,6 +36,9 @@ class TextMatePackage extends Package
|
||||
for { selector, properties } in @scopedProperties
|
||||
syntax.addProperties(selector, properties)
|
||||
|
||||
deactivate: ->
|
||||
# we should remove grammars and unregister properties, snippets, etc
|
||||
|
||||
legalGrammarExtensions: ['plist', 'tmLanguage', 'tmlanguage', 'cson', 'json']
|
||||
|
||||
loadGrammars: (done) ->
|
||||
|
||||
@@ -76,6 +76,7 @@ describe "TreeView", ->
|
||||
rootView.deactivate()
|
||||
window.rootView = new RootView
|
||||
rootView.open('tree-view.js')
|
||||
atom.packageStates = {}
|
||||
treeView = atom.activatePackage("tree-view").mainModule.createView()
|
||||
expect(treeView.hasParent()).toBeFalsy()
|
||||
expect(treeView.root).toExist()
|
||||
|
||||
Reference in New Issue
Block a user