pulsar/spec/config-spec.coffee
2014-05-16 09:00:01 -06:00

370 lines
15 KiB
CoffeeScript

path = require 'path'
temp = require 'temp'
CSON = require 'season'
fs = require 'fs-plus'
describe "Config", ->
dotAtomPath = path.join(temp.dir, 'dot-atom-dir')
dotAtomPath = null
beforeEach ->
dotAtomPath = temp.path('dot-atom-dir')
describe ".get(keyPath)", ->
it "allows a key path's value to be read", ->
expect(atom.config.set("foo.bar.baz", 42)).toBe 42
expect(atom.config.get("foo.bar.baz")).toBe 42
expect(atom.config.get("bogus.key.path")).toBeUndefined()
it "returns a deep clone of the key path's value", ->
atom.config.set('value', array: [1, b: 2, 3])
retrievedValue = atom.config.get('value')
retrievedValue.array[0] = 4
retrievedValue.array[1].b = 2.1
expect(atom.config.get('value')).toEqual(array: [1, b: 2, 3])
describe ".set(keyPath, value)", ->
it "allows a key path's value to be written", ->
expect(atom.config.set("foo.bar.baz", 42)).toBe 42
expect(atom.config.get("foo.bar.baz")).toBe 42
it "updates observers and saves when a key path is set", ->
observeHandler = jasmine.createSpy "observeHandler"
atom.config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
atom.config.set("foo.bar.baz", 42)
expect(atom.config.save).toHaveBeenCalled()
expect(observeHandler).toHaveBeenCalledWith 42, {previous: undefined}
describe "when the value equals the default value", ->
it "does not store the value", ->
atom.config.setDefaults "foo",
same: 1
changes: 1
sameArray: [1, 2, 3]
sameObject: {a: 1, b: 2}
null: null
undefined: undefined
expect(atom.config.settings.foo).toBeUndefined()
atom.config.set('foo.same', 1)
atom.config.set('foo.changes', 2)
atom.config.set('foo.sameArray', [1, 2, 3])
atom.config.set('foo.null', undefined)
atom.config.set('foo.undefined', null)
atom.config.set('foo.sameObject', {b: 2, a: 1})
expect(atom.config.settings.foo).toEqual {changes: 2}
atom.config.set('foo.changes', 1)
expect(atom.config.settings.foo).toEqual {}
describe ".getDefault(keyPath)", ->
it "returns a clone of the default value", ->
atom.config.setDefaults("foo", same: 1, changes: 1)
expect(atom.config.getDefault('foo.same')).toBe 1
expect(atom.config.getDefault('foo.changes')).toBe 1
atom.config.set('foo.same', 2)
atom.config.set('foo.changes', 3)
expect(atom.config.getDefault('foo.same')).toBe 1
expect(atom.config.getDefault('foo.changes')).toBe 1
initialDefaultValue = [1, 2, 3]
atom.config.setDefaults("foo", bar: initialDefaultValue)
expect(atom.config.getDefault('foo.bar')).toEqual initialDefaultValue
expect(atom.config.getDefault('foo.bar')).not.toBe initialDefaultValue
describe ".isDefault(keyPath)", ->
it "returns true when the value of the key path is its default value", ->
atom.config.setDefaults("foo", same: 1, changes: 1)
expect(atom.config.isDefault('foo.same')).toBe true
expect(atom.config.isDefault('foo.changes')).toBe true
atom.config.set('foo.same', 2)
atom.config.set('foo.changes', 3)
expect(atom.config.isDefault('foo.same')).toBe false
expect(atom.config.isDefault('foo.changes')).toBe false
describe ".toggle(keyPath)", ->
it "negates the boolean value of the current key path value", ->
atom.config.set('foo.a', 1)
atom.config.toggle('foo.a')
expect(atom.config.get('foo.a')).toBe false
atom.config.set('foo.a', '')
atom.config.toggle('foo.a')
expect(atom.config.get('foo.a')).toBe true
atom.config.set('foo.a', null)
atom.config.toggle('foo.a')
expect(atom.config.get('foo.a')).toBe true
atom.config.set('foo.a', true)
atom.config.toggle('foo.a')
expect(atom.config.get('foo.a')).toBe false
describe ".restoreDefault(keyPath)", ->
it "sets the value of the key path to its default", ->
atom.config.setDefaults('a', b: 3)
atom.config.set('a.b', 4)
expect(atom.config.get('a.b')).toBe 4
atom.config.restoreDefault('a.b')
expect(atom.config.get('a.b')).toBe 3
atom.config.set('a.c', 5)
expect(atom.config.get('a.c')).toBe 5
atom.config.restoreDefault('a.c')
expect(atom.config.get('a.c')).toBeUndefined()
describe ".pushAtKeyPath(keyPath, value)", ->
it "pushes the given value to the array at the key path and updates observers", ->
atom.config.set("foo.bar.baz", ["a"])
observeHandler = jasmine.createSpy "observeHandler"
atom.config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
expect(atom.config.pushAtKeyPath("foo.bar.baz", "b")).toBe 2
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"]
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['a']}
describe ".unshiftAtKeyPath(keyPath, value)", ->
it "unshifts the given value to the array at the key path and updates observers", ->
atom.config.set("foo.bar.baz", ["b"])
observeHandler = jasmine.createSpy "observeHandler"
atom.config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
expect(atom.config.unshiftAtKeyPath("foo.bar.baz", "a")).toBe 2
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"]
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['b']}
describe ".removeAtKeyPath(keyPath, value)", ->
it "removes the given value from the array at the key path and updates observers", ->
atom.config.set("foo.bar.baz", ["a", "b", "c"])
observeHandler = jasmine.createSpy "observeHandler"
atom.config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
expect(atom.config.removeAtKeyPath("foo.bar.baz", "b")).toEqual ["a", "c"]
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "c"]
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['a', 'b', 'c']}
describe ".getPositiveInt(keyPath, defaultValue)", ->
it "returns the proper current or default value", ->
atom.config.set('editor.preferredLineLength', 0)
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
atom.config.set('editor.preferredLineLength', -1234)
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
atom.config.set('editor.preferredLineLength', 'abcd')
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
atom.config.set('editor.preferredLineLength', null)
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
describe ".save()", ->
CSON = require 'season'
beforeEach ->
spyOn(CSON, 'writeFileSync')
jasmine.unspy atom.config, 'save'
describe "when ~/.atom/config.json exists", ->
it "writes any non-default properties to ~/.atom/config.json", ->
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.json")
atom.config.set("a.b.c", 1)
atom.config.set("a.b.d", 2)
atom.config.set("x.y.z", 3)
atom.config.setDefaults("a.b", e: 4, f: 5)
CSON.writeFileSync.reset()
atom.config.save()
expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json"))
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
expect(writtenConfig).toBe atom.config.settings
describe "when ~/.atom/config.json doesn't exist", ->
it "writes any non-default properties to ~/.atom/config.cson", ->
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
atom.config.set("a.b.c", 1)
atom.config.set("a.b.d", 2)
atom.config.set("x.y.z", 3)
atom.config.setDefaults("a.b", e: 4, f: 5)
CSON.writeFileSync.reset()
atom.config.save()
expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson"))
CoffeeScript = require 'coffee-script'
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
expect(writtenConfig).toEqual atom.config.settings
describe ".setDefaults(keyPath, defaults)", ->
it "assigns any previously-unassigned keys to the object at the key path", ->
atom.config.set("foo.bar.baz", a: 1)
atom.config.setDefaults("foo.bar.baz", a: 2, b: 3, c: 4)
expect(atom.config.get("foo.bar.baz.a")).toBe 1
expect(atom.config.get("foo.bar.baz.b")).toBe 3
expect(atom.config.get("foo.bar.baz.c")).toBe 4
atom.config.setDefaults("foo.quux", x: 0, y: 1)
expect(atom.config.get("foo.quux.x")).toBe 0
expect(atom.config.get("foo.quux.y")).toBe 1
it "emits an updated event", ->
updatedCallback = jasmine.createSpy('updated')
atom.config.observe('foo.bar.baz.a', callNow: false, updatedCallback)
expect(updatedCallback.callCount).toBe 0
atom.config.setDefaults("foo.bar.baz", a: 2)
expect(updatedCallback.callCount).toBe 1
describe ".observe(keyPath)", ->
[observeHandler, observeSubscription] = []
beforeEach ->
observeHandler = jasmine.createSpy("observeHandler")
atom.config.set("foo.bar.baz", "value 1")
observeSubscription = atom.config.observe "foo.bar.baz", observeHandler
it "fires the given callback with the current value at the keypath", ->
expect(observeHandler).toHaveBeenCalledWith("value 1")
it "fires the callback every time the observed value changes", ->
observeHandler.reset() # clear the initial call
atom.config.set('foo.bar.baz', "value 2")
expect(observeHandler).toHaveBeenCalledWith("value 2", {previous: 'value 1'})
observeHandler.reset()
atom.config.set('foo.bar.baz', "value 1")
expect(observeHandler).toHaveBeenCalledWith("value 1", {previous: 'value 2'})
it "fires the callback when the observed value is deleted", ->
observeHandler.reset() # clear the initial call
atom.config.set('foo.bar.baz', undefined)
expect(observeHandler).toHaveBeenCalledWith(undefined, {previous: 'value 1'})
it "fires the callback when the full key path goes into and out of existence", ->
observeHandler.reset() # clear the initial call
atom.config.set("foo.bar", undefined)
expect(observeHandler).toHaveBeenCalledWith(undefined, {previous: 'value 1'})
observeHandler.reset()
atom.config.set("foo.bar.baz", "i'm back")
expect(observeHandler).toHaveBeenCalledWith("i'm back", {previous: undefined})
it "does not fire the callback once the observe subscription is off'ed", ->
observeHandler.reset() # clear the initial call
observeSubscription.off()
atom.config.set('foo.bar.baz', "value 2")
expect(observeHandler).not.toHaveBeenCalled()
describe ".initializeConfigDirectory()", ->
beforeEach ->
if fs.existsSync(dotAtomPath)
fs.removeSync(dotAtomPath)
atom.config.configDirPath = dotAtomPath
afterEach ->
fs.removeSync(dotAtomPath)
describe "when the configDirPath doesn't exist", ->
it "copies the contents of dot-atom to ~/.atom", ->
initializationDone = false
jasmine.unspy(window, "setTimeout")
atom.config.initializeConfigDirectory ->
initializationDone = true
waitsFor -> initializationDone
runs ->
expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy()
expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'snippets.cson'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'init.coffee'))).toBeTruthy()
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'styles.less'))).toBeTruthy()
describe ".loadUserConfig()", ->
beforeEach ->
atom.config.configDirPath = dotAtomPath
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
afterEach ->
fs.removeSync(dotAtomPath)
describe "when the config file contains valid cson", ->
beforeEach ->
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
atom.config.loadUserConfig()
it "updates the config data based on the file contents", ->
expect(atom.config.get("foo.bar")).toBe 'baz'
describe "when the config file contains invalid cson", ->
beforeEach ->
spyOn(console, 'error')
fs.writeFileSync(atom.config.configFilePath, "{{{{{")
it "logs an error to the console and does not overwrite the config file on a subsequent save", ->
atom.config.loadUserConfig()
expect(console.error).toHaveBeenCalled()
atom.config.set("hair", "blonde") # trigger a save
expect(atom.config.save).not.toHaveBeenCalled()
describe "when the config file does not exist", ->
it "creates it with an empty object", ->
fs.makeTreeSync(atom.config.configDirPath)
atom.config.loadUserConfig()
expect(fs.existsSync(atom.config.configFilePath)).toBe true
expect(CSON.readFileSync(atom.config.configFilePath)).toEqual {}
describe ".observeUserConfig()", ->
updatedHandler = null
beforeEach ->
atom.config.configDirPath = dotAtomPath
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
atom.config.loadUserConfig()
atom.config.observeUserConfig()
updatedHandler = jasmine.createSpy("updatedHandler")
atom.config.on 'updated', updatedHandler
afterEach ->
atom.config.unobserveUserConfig()
fs.removeSync(dotAtomPath)
describe "when the config file changes to contain valid cson", ->
it "updates the config data", ->
fs.writeFileSync(atom.config.configFilePath, "foo: { bar: 'quux', baz: 'bar'}")
waitsFor 'update event', -> updatedHandler.callCount > 0
runs ->
expect(atom.config.get('foo.bar')).toBe 'quux'
expect(atom.config.get('foo.baz')).toBe 'bar'
describe "when the config file changes to contain invalid cson", ->
beforeEach ->
spyOn(console, 'error')
fs.writeFileSync(atom.config.configFilePath, "}}}")
waitsFor "error to be logged", -> console.error.callCount > 0
it "logs a warning and does not update config data", ->
expect(updatedHandler.callCount).toBe 0
expect(atom.config.get('foo.bar')).toBe 'baz'
atom.config.set("hair", "blonde") # trigger a save
expect(atom.config.save).not.toHaveBeenCalled()
describe "when the config file subsequently changes again to contain valid cson", ->
beforeEach ->
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
waitsFor 'update event', -> updatedHandler.callCount > 0
it "updates the config data and resumes saving", ->
atom.config.set("hair", "blonde")
expect(atom.config.save).toHaveBeenCalled()