mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-11 11:15:24 +03:00
Merge pull request #700 from pulsar-edit/package-specs-manual-decaf
Decaf Packages Spec
This commit is contained in:
commit
2b4b216830
@ -1,53 +0,0 @@
|
||||
AtomIoClient = require '../lib/atom-io-client'
|
||||
|
||||
describe "AtomIoClient", ->
|
||||
|
||||
beforeEach ->
|
||||
@client = new AtomIoClient
|
||||
|
||||
it "fetches avatar from cache if the network is unavailable", ->
|
||||
spyOn(@client, 'online').andReturn(false)
|
||||
spyOn(@client, 'fetchAndCacheAvatar')
|
||||
expect(@client.fetchAndCacheAvatar).not.toHaveBeenCalled()
|
||||
@client.avatar 'test-user', ->
|
||||
|
||||
describe "request", ->
|
||||
it "fetches api json from cache if the network is unavailable", ->
|
||||
spyOn(@client, 'online').andReturn(false)
|
||||
spyOn(@client, 'fetchFromCache').andReturn({})
|
||||
spyOn(@client, 'request')
|
||||
@client.package 'test-package', ->
|
||||
|
||||
expect(@client.fetchFromCache).toHaveBeenCalled()
|
||||
expect(@client.request).not.toHaveBeenCalled()
|
||||
|
||||
it "returns an error if the API response is not JSON", ->
|
||||
jsonParse = JSON.parse
|
||||
|
||||
waitsFor (done) ->
|
||||
spyOn(@client, 'parseJSON').andThrow()
|
||||
@client.request 'path', (error, data) ->
|
||||
expect(error).not.toBeNull()
|
||||
done()
|
||||
|
||||
runs ->
|
||||
# Tests will throw without this as cleanup requires JSON.parse to work
|
||||
JSON.parse = jsonParse
|
||||
|
||||
it "handles glob errors", ->
|
||||
spyOn(@client, 'avatarGlob').andReturn "#{__dirname}/**"
|
||||
spyOn(require('fs'), 'readdir').andCallFake (dirPath, callback) ->
|
||||
process.nextTick -> callback(new Error('readdir error'))
|
||||
|
||||
callback = jasmine.createSpy('cacheAvatar callback')
|
||||
@client.cachedAvatar 'fakeperson', callback
|
||||
|
||||
waitsFor ->
|
||||
callback.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect(callback.argsForCall[0][0].message).toBe 'readdir error'
|
||||
|
||||
xit "purges old items from cache correctly"
|
||||
# "correctly" in this case means "remove all old items but one" so that we
|
||||
# always have stale data to return if the network is gone.
|
59
packages/settings-view/spec/atom-io-client-spec.js
Normal file
59
packages/settings-view/spec/atom-io-client-spec.js
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
const AtomIoClient = require('../lib/atom-io-client');
|
||||
|
||||
describe("AtomIoClient", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
return this.client = new AtomIoClient;
|
||||
});
|
||||
|
||||
it("fetches avatar from cache if the network is unavailable", function() {
|
||||
spyOn(this.client, 'online').andReturn(false);
|
||||
spyOn(this.client, 'fetchAndCacheAvatar');
|
||||
expect(this.client.fetchAndCacheAvatar).not.toHaveBeenCalled();
|
||||
return this.client.avatar('test-user', function() {});
|
||||
});
|
||||
|
||||
describe("request", function() {
|
||||
it("fetches api json from cache if the network is unavailable", function() {
|
||||
spyOn(this.client, 'online').andReturn(false);
|
||||
spyOn(this.client, 'fetchFromCache').andReturn({});
|
||||
spyOn(this.client, 'request');
|
||||
this.client.package('test-package', function() {});
|
||||
|
||||
expect(this.client.fetchFromCache).toHaveBeenCalled();
|
||||
expect(this.client.request).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns an error if the API response is not JSON", function() {
|
||||
const jsonParse = JSON.parse;
|
||||
|
||||
waitsFor(function(done) {
|
||||
spyOn(this.client, 'parseJSON').andThrow();
|
||||
return this.client.request('path', function(error, data) {
|
||||
expect(error).not.toBeNull();
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
return runs(() => // Tests will throw without this as cleanup requires JSON.parse to work
|
||||
JSON.parse = jsonParse);
|
||||
});
|
||||
});
|
||||
|
||||
it("handles glob errors", function() {
|
||||
spyOn(this.client, 'avatarGlob').andReturn(`${__dirname}/**`);
|
||||
spyOn(require('fs'), 'readdir').andCallFake((dirPath, callback) => process.nextTick(() => callback(new Error('readdir error'))));
|
||||
|
||||
const callback = jasmine.createSpy('cacheAvatar callback');
|
||||
this.client.cachedAvatar('fakeperson', callback);
|
||||
|
||||
waitsFor(() => callback.callCount === 1);
|
||||
|
||||
return runs(() => expect(callback.argsForCall[0][0].message).toBe('readdir error'));
|
||||
});
|
||||
|
||||
return xit("purges old items from cache correctly");
|
||||
});
|
||||
// "correctly" in this case means "remove all old items but one" so that we
|
||||
// always have stale data to return if the network is gone.
|
@ -1,110 +0,0 @@
|
||||
EditorPanel = require '../lib/editor-panel'
|
||||
|
||||
describe "EditorPanel", ->
|
||||
panel = null
|
||||
|
||||
getValueForId = (id) ->
|
||||
element = panel.element.querySelector("##{id.replace(/\./g, '\\.')}")
|
||||
if element?.tagName is "INPUT"
|
||||
element.checked
|
||||
else if element?.tagName is "SELECT"
|
||||
element.value
|
||||
else if element?
|
||||
element.getModel().getText()
|
||||
else
|
||||
return
|
||||
|
||||
setValueForId = (id, value) ->
|
||||
element = panel.element.querySelector("##{id.replace(/\./g, '\\.')}")
|
||||
if element.tagName is "INPUT"
|
||||
element.checked = value
|
||||
element.dispatchEvent(new Event('change', {bubbles: true}))
|
||||
else if element.tagName is "SELECT"
|
||||
element.value = value
|
||||
element.dispatchEvent(new Event('change', {bubbles: true}))
|
||||
else
|
||||
element.getModel().setText(value?.toString())
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
|
||||
beforeEach ->
|
||||
atom.config.set('editor.boolean', true)
|
||||
atom.config.set('editor.string', 'hey')
|
||||
atom.config.set('editor.object', {boolean: true, int: 3, string: 'test'})
|
||||
atom.config.set('editor.simpleArray', ['a', 'b', 'c'])
|
||||
atom.config.set('editor.complexArray', ['a', 'b', {c: true}])
|
||||
|
||||
atom.config.setSchema('', type: 'object')
|
||||
|
||||
panel = new EditorPanel()
|
||||
|
||||
it "automatically binds named fields to their corresponding config keys", ->
|
||||
expect(getValueForId('editor.boolean')).toBeTruthy()
|
||||
expect(getValueForId('editor.string')).toBe 'hey'
|
||||
expect(getValueForId('editor.object.boolean')).toBeTruthy()
|
||||
expect(getValueForId('editor.object.int')).toBe '3'
|
||||
expect(getValueForId('editor.object.string')).toBe 'test'
|
||||
|
||||
atom.config.set('editor.boolean', false)
|
||||
atom.config.set('editor.string', 'hey again')
|
||||
atom.config.set('editor.object.boolean', false)
|
||||
atom.config.set('editor.object.int', 6)
|
||||
atom.config.set('editor.object.string', 'hi')
|
||||
|
||||
expect(getValueForId('editor.boolean')).toBeFalsy()
|
||||
expect(getValueForId('editor.string')).toBe 'hey again'
|
||||
expect(getValueForId('editor.object.boolean')).toBeFalsy()
|
||||
expect(getValueForId('editor.object.int')).toBe '6'
|
||||
expect(getValueForId('editor.object.string')).toBe 'hi'
|
||||
|
||||
setValueForId('editor.string', "oh hi")
|
||||
setValueForId('editor.boolean', true)
|
||||
setValueForId('editor.object.boolean', true)
|
||||
setValueForId('editor.object.int', 9)
|
||||
setValueForId('editor.object.string', 'yo')
|
||||
|
||||
expect(atom.config.get('editor.boolean')).toBe true
|
||||
expect(atom.config.get('editor.string')).toBe 'oh hi'
|
||||
expect(atom.config.get('editor.object.boolean')).toBe true
|
||||
expect(atom.config.get('editor.object.int')).toBe 9
|
||||
expect(atom.config.get('editor.object.string')).toBe 'yo'
|
||||
|
||||
setValueForId('editor.string', '')
|
||||
setValueForId('editor.object.int', '')
|
||||
setValueForId('editor.object.string', '')
|
||||
|
||||
expect(atom.config.get('editor.string')).toBeUndefined()
|
||||
expect(atom.config.get('editor.object.int')).toBeUndefined()
|
||||
expect(atom.config.get('editor.object.string')).toBeUndefined()
|
||||
|
||||
it "does not save the config value until it has been changed to a new value", ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
atom.config.observe "editor.simpleArray", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
setValueForId('editor.simpleArray', 2)
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
observeHandler.reset()
|
||||
|
||||
setValueForId('editor.simpleArray', 2)
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "does not update the editor text unless the value it parses to changes", ->
|
||||
setValueForId('editor.simpleArray', "a, b,")
|
||||
expect(atom.config.get('editor.simpleArray')).toEqual ['a', 'b']
|
||||
expect(getValueForId('editor.simpleArray')).toBe 'a, b,'
|
||||
|
||||
it "only adds editors for arrays when all the values in the array are strings", ->
|
||||
expect(getValueForId('editor.simpleArray')).toBe 'a, b, c'
|
||||
expect(getValueForId('editor.complexArray')).toBeUndefined()
|
||||
|
||||
setValueForId('editor.simpleArray', 'a, d')
|
||||
|
||||
expect(atom.config.get('editor.simpleArray')).toEqual ['a', 'd']
|
||||
expect(atom.config.get('editor.complexArray')).toEqual ['a', 'b', {c: true}]
|
||||
|
||||
it "shows the package settings notes for core and editor settings", ->
|
||||
expect(panel.element.querySelector('#editor-settings-note')).toExist()
|
||||
expect(panel.element.querySelector('#editor-settings-note').textContent).toContain('Check language settings')
|
122
packages/settings-view/spec/editor-panel-spec.js
Normal file
122
packages/settings-view/spec/editor-panel-spec.js
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
const EditorPanel = require('../lib/editor-panel');
|
||||
|
||||
describe("EditorPanel", function() {
|
||||
let panel = null;
|
||||
|
||||
const getValueForId = function(id) {
|
||||
const element = panel.element.querySelector(`#${id.replace(/\./g, '\\.')}`);
|
||||
if (element?.tagName === "INPUT") {
|
||||
return element.checked;
|
||||
} else if (element?.tagName === "SELECT") {
|
||||
return element.value;
|
||||
} else if (element != null) {
|
||||
return element.getModel().getText();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const setValueForId = function(id, value) {
|
||||
const element = panel.element.querySelector(`#${id.replace(/\./g, '\\.')}`);
|
||||
if (element.tagName === "INPUT") {
|
||||
element.checked = value;
|
||||
return element.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
} else if (element.tagName === "SELECT") {
|
||||
element.value = value;
|
||||
return element.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
} else {
|
||||
element.getModel().setText(value?.toString());
|
||||
return window.advanceClock(10000); // wait for contents-modified to be triggered
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
atom.config.set('editor.boolean', true);
|
||||
atom.config.set('editor.string', 'hey');
|
||||
atom.config.set('editor.object', {boolean: true, int: 3, string: 'test'});
|
||||
atom.config.set('editor.simpleArray', ['a', 'b', 'c']);
|
||||
atom.config.set('editor.complexArray', ['a', 'b', {c: true}]);
|
||||
|
||||
atom.config.setSchema('', {type: 'object'});
|
||||
|
||||
return panel = new EditorPanel();
|
||||
});
|
||||
|
||||
it("automatically binds named fields to their corresponding config keys", function() {
|
||||
expect(getValueForId('editor.boolean')).toBeTruthy();
|
||||
expect(getValueForId('editor.string')).toBe('hey');
|
||||
expect(getValueForId('editor.object.boolean')).toBeTruthy();
|
||||
expect(getValueForId('editor.object.int')).toBe('3');
|
||||
expect(getValueForId('editor.object.string')).toBe('test');
|
||||
|
||||
atom.config.set('editor.boolean', false);
|
||||
atom.config.set('editor.string', 'hey again');
|
||||
atom.config.set('editor.object.boolean', false);
|
||||
atom.config.set('editor.object.int', 6);
|
||||
atom.config.set('editor.object.string', 'hi');
|
||||
|
||||
expect(getValueForId('editor.boolean')).toBeFalsy();
|
||||
expect(getValueForId('editor.string')).toBe('hey again');
|
||||
expect(getValueForId('editor.object.boolean')).toBeFalsy();
|
||||
expect(getValueForId('editor.object.int')).toBe('6');
|
||||
expect(getValueForId('editor.object.string')).toBe('hi');
|
||||
|
||||
setValueForId('editor.string', "oh hi");
|
||||
setValueForId('editor.boolean', true);
|
||||
setValueForId('editor.object.boolean', true);
|
||||
setValueForId('editor.object.int', 9);
|
||||
setValueForId('editor.object.string', 'yo');
|
||||
|
||||
expect(atom.config.get('editor.boolean')).toBe(true);
|
||||
expect(atom.config.get('editor.string')).toBe('oh hi');
|
||||
expect(atom.config.get('editor.object.boolean')).toBe(true);
|
||||
expect(atom.config.get('editor.object.int')).toBe(9);
|
||||
expect(atom.config.get('editor.object.string')).toBe('yo');
|
||||
|
||||
setValueForId('editor.string', '');
|
||||
setValueForId('editor.object.int', '');
|
||||
setValueForId('editor.object.string', '');
|
||||
|
||||
expect(atom.config.get('editor.string')).toBeUndefined();
|
||||
expect(atom.config.get('editor.object.int')).toBeUndefined();
|
||||
expect(atom.config.get('editor.object.string')).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not save the config value until it has been changed to a new value", function() {
|
||||
const observeHandler = jasmine.createSpy("observeHandler");
|
||||
atom.config.observe("editor.simpleArray", observeHandler);
|
||||
observeHandler.reset();
|
||||
|
||||
window.advanceClock(10000); // wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled();
|
||||
|
||||
setValueForId('editor.simpleArray', 2);
|
||||
expect(observeHandler).toHaveBeenCalled();
|
||||
observeHandler.reset();
|
||||
|
||||
setValueForId('editor.simpleArray', 2);
|
||||
expect(observeHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not update the editor text unless the value it parses to changes", function() {
|
||||
setValueForId('editor.simpleArray', "a, b,");
|
||||
expect(atom.config.get('editor.simpleArray')).toEqual(['a', 'b']);
|
||||
expect(getValueForId('editor.simpleArray')).toBe('a, b,');
|
||||
});
|
||||
|
||||
it("only adds editors for arrays when all the values in the array are strings", function() {
|
||||
expect(getValueForId('editor.simpleArray')).toBe('a, b, c');
|
||||
expect(getValueForId('editor.complexArray')).toBeUndefined();
|
||||
|
||||
setValueForId('editor.simpleArray', 'a, d');
|
||||
|
||||
expect(atom.config.get('editor.simpleArray')).toEqual(['a', 'd']);
|
||||
expect(atom.config.get('editor.complexArray')).toEqual(['a', 'b', {c: true}]);
|
||||
});
|
||||
|
||||
it("shows the package settings notes for core and editor settings", function() {
|
||||
expect(panel.element.querySelector('#editor-settings-note')).toExist();
|
||||
expect(panel.element.querySelector('#editor-settings-note').textContent).toContain('Check language settings');
|
||||
});
|
||||
});
|
@ -1,90 +0,0 @@
|
||||
GeneralPanel = require '../lib/general-panel'
|
||||
|
||||
describe "GeneralPanel", ->
|
||||
panel = null
|
||||
|
||||
getValueForId = (id) ->
|
||||
element = panel.element.querySelector("##{id.replace(/\./g, '\\.')}")
|
||||
if element.tagName is "INPUT"
|
||||
element.checked
|
||||
else if element.tagName is "SELECT"
|
||||
element.value
|
||||
else
|
||||
element.getModel().getText()
|
||||
|
||||
setValueForId = (id, value) ->
|
||||
element = panel.element.querySelector("##{id.replace(/\./g, '\\.')}")
|
||||
if element.tagName is "INPUT"
|
||||
element.checked = value
|
||||
element.dispatchEvent(new Event('change', {bubbles: true}))
|
||||
else if element.tagName is "SELECT"
|
||||
element.value = value
|
||||
element.dispatchEvent(new Event('change', {bubbles: true}))
|
||||
else
|
||||
element.getModel().setText(value?.toString())
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
|
||||
beforeEach ->
|
||||
atom.config.set('core.enum', 4)
|
||||
atom.config.set('core.int', 22)
|
||||
atom.config.set('core.float', 0.1)
|
||||
|
||||
atom.config.setSchema('', type: 'object')
|
||||
atom.config.setSchema('core.enum',
|
||||
type: 'integer'
|
||||
default: 2
|
||||
enum: [2, 4, 6, 8]
|
||||
)
|
||||
|
||||
panel = new GeneralPanel()
|
||||
|
||||
it "automatically binds named fields to their corresponding config keys", ->
|
||||
expect(getValueForId('core.enum')).toBe '4'
|
||||
expect(getValueForId('core.int')).toBe '22'
|
||||
expect(getValueForId('core.float')).toBe '0.1'
|
||||
|
||||
atom.config.set('core.enum', 6)
|
||||
atom.config.set('core.int', 222)
|
||||
atom.config.set('core.float', 0.11)
|
||||
|
||||
expect(getValueForId('core.enum')).toBe '6'
|
||||
expect(getValueForId('core.int')).toBe '222'
|
||||
expect(getValueForId('core.float')).toBe '0.11'
|
||||
|
||||
setValueForId('core.enum', '2')
|
||||
setValueForId('core.int', 90)
|
||||
setValueForId('core.float', 89.2)
|
||||
|
||||
expect(atom.config.get('core.enum')).toBe 2
|
||||
expect(atom.config.get('core.int')).toBe 90
|
||||
expect(atom.config.get('core.float')).toBe 89.2
|
||||
|
||||
setValueForId('core.int', '')
|
||||
setValueForId('core.float', '')
|
||||
|
||||
expect(atom.config.get('core.int')).toBeUndefined()
|
||||
expect(atom.config.get('core.float')).toBeUndefined()
|
||||
|
||||
it "does not save the config value until it has been changed to a new value", ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
atom.config.observe "core.int", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
setValueForId('core.int', 2)
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
observeHandler.reset()
|
||||
|
||||
setValueForId('core.int', 2)
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "does not update the editor text unless the value it parses to changes", ->
|
||||
setValueForId('core.int', "2.")
|
||||
expect(atom.config.get('core.int')).toBe 2
|
||||
expect(getValueForId('core.int')).toBe '2.'
|
||||
|
||||
it "shows the package settings notes for core and editor settings", ->
|
||||
expect(panel.element.querySelector('#core-settings-note')).toExist()
|
||||
expect(panel.element.querySelector('#core-settings-note').textContent).toContain('their package card in')
|
102
packages/settings-view/spec/general-panel-spec.js
Normal file
102
packages/settings-view/spec/general-panel-spec.js
Normal file
@ -0,0 +1,102 @@
|
||||
|
||||
const GeneralPanel = require('../lib/general-panel');
|
||||
|
||||
describe("GeneralPanel", () => {
|
||||
let panel = null;
|
||||
|
||||
const getValueForId = function(id) {
|
||||
const element = panel.element.querySelector(`#${id.replace(/\./g, '\\.')}`);
|
||||
if (element.tagName === "INPUT") {
|
||||
return element.checked;
|
||||
} else if (element.tagName === "SELECT") {
|
||||
return element.value;
|
||||
} else {
|
||||
return element.getModel().getText();
|
||||
}
|
||||
};
|
||||
|
||||
const setValueForId = function(id, value) {
|
||||
const element = panel.element.querySelector(`#${id.replace(/\./g, '\\.')}`);
|
||||
if (element.tagName === "INPUT") {
|
||||
element.checked = value;
|
||||
return element.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
} else if (element.tagName === "SELECT") {
|
||||
element.value = value;
|
||||
return element.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
} else {
|
||||
element.getModel().setText(value?.toString());
|
||||
return window.advanceClock(10000); // wait for contents-modified to be triggered
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
atom.config.set('core.enum', 4);
|
||||
atom.config.set('core.int', 22);
|
||||
atom.config.set('core.float', 0.1);
|
||||
|
||||
atom.config.setSchema('', {type: 'object'});
|
||||
atom.config.setSchema('core.enum', {
|
||||
type: 'integer',
|
||||
default: 2,
|
||||
enum: [2, 4, 6, 8]
|
||||
}
|
||||
);
|
||||
|
||||
panel = new GeneralPanel();
|
||||
});
|
||||
|
||||
it("automatically binds named fields to their corresponding config keys", () => {
|
||||
expect(getValueForId('core.enum')).toBe('4');
|
||||
expect(getValueForId('core.int')).toBe('22');
|
||||
expect(getValueForId('core.float')).toBe('0.1');
|
||||
|
||||
atom.config.set('core.enum', 6);
|
||||
atom.config.set('core.int', 222);
|
||||
atom.config.set('core.float', 0.11);
|
||||
|
||||
expect(getValueForId('core.enum')).toBe('6');
|
||||
expect(getValueForId('core.int')).toBe('222');
|
||||
expect(getValueForId('core.float')).toBe('0.11');
|
||||
|
||||
setValueForId('core.enum', '2');
|
||||
setValueForId('core.int', 90);
|
||||
setValueForId('core.float', 89.2);
|
||||
|
||||
expect(atom.config.get('core.enum')).toBe(2);
|
||||
expect(atom.config.get('core.int')).toBe(90);
|
||||
expect(atom.config.get('core.float')).toBe(89.2);
|
||||
|
||||
setValueForId('core.int', '');
|
||||
setValueForId('core.float', '');
|
||||
|
||||
expect(atom.config.get('core.int')).toBeUndefined();
|
||||
expect(atom.config.get('core.float')).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not save the config value until it has been changed to a new value", () => {
|
||||
const observeHandler = jasmine.createSpy("observeHandler");
|
||||
atom.config.observe("core.int", observeHandler);
|
||||
observeHandler.reset();
|
||||
|
||||
window.advanceClock(10000); // wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled();
|
||||
|
||||
setValueForId('core.int', 2);
|
||||
expect(observeHandler).toHaveBeenCalled();
|
||||
observeHandler.reset();
|
||||
|
||||
setValueForId('core.int', 2);
|
||||
expect(observeHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not update the editor text unless the value it parses to changes", () => {
|
||||
setValueForId('core.int', "2.");
|
||||
expect(atom.config.get('core.int')).toBe(2);
|
||||
expect(getValueForId('core.int')).toBe('2.');
|
||||
});
|
||||
|
||||
it("shows the package settings notes for core and editor settings", () => {
|
||||
expect(panel.element.querySelector('#core-settings-note')).toExist();
|
||||
expect(panel.element.querySelector('#core-settings-note').textContent).toContain('their package card in');
|
||||
});
|
||||
});
|
@ -1,113 +0,0 @@
|
||||
InstallPanel = require '../lib/install-panel'
|
||||
PackageManager = require '../lib/package-manager'
|
||||
SettingsView = require '../lib/settings-view'
|
||||
|
||||
describe 'InstallPanel', ->
|
||||
beforeEach ->
|
||||
settingsView = new SettingsView()
|
||||
@packageManager = new PackageManager()
|
||||
@panel = new InstallPanel(settingsView, @packageManager)
|
||||
|
||||
describe "when the packages button is clicked", ->
|
||||
beforeEach ->
|
||||
spyOn(@panel, 'search')
|
||||
@panel.refs.searchEditor.setText('something')
|
||||
|
||||
it "performs a search for the contents of the input", ->
|
||||
@panel.refs.searchPackagesButton.click()
|
||||
expect(@panel.searchType).toBe 'packages'
|
||||
expect(@panel.search).toHaveBeenCalledWith 'something'
|
||||
expect(@panel.search.callCount).toBe 1
|
||||
|
||||
@panel.refs.searchPackagesButton.click()
|
||||
expect(@panel.searchType).toBe 'packages'
|
||||
expect(@panel.search).toHaveBeenCalledWith 'something'
|
||||
expect(@panel.search.callCount).toBe 2
|
||||
|
||||
describe "when the themes button is clicked", ->
|
||||
beforeEach ->
|
||||
spyOn(@panel, 'search')
|
||||
@panel.refs.searchEditor.setText('something')
|
||||
|
||||
it "performs a search for the contents of the input", ->
|
||||
@panel.refs.searchThemesButton.click()
|
||||
expect(@panel.searchType).toBe 'themes'
|
||||
expect(@panel.search.callCount).toBe 1
|
||||
expect(@panel.search).toHaveBeenCalledWith 'something'
|
||||
|
||||
@panel.refs.searchThemesButton.click()
|
||||
expect(@panel.searchType).toBe 'themes'
|
||||
expect(@panel.search.callCount).toBe 2
|
||||
|
||||
describe "when the buttons are toggled", ->
|
||||
beforeEach ->
|
||||
spyOn(@panel, 'search')
|
||||
@panel.refs.searchEditor.setText('something')
|
||||
|
||||
it "performs a search for the contents of the input", ->
|
||||
@panel.refs.searchThemesButton.click()
|
||||
expect(@panel.searchType).toBe 'themes'
|
||||
expect(@panel.search.callCount).toBe 1
|
||||
expect(@panel.search).toHaveBeenCalledWith 'something'
|
||||
|
||||
@panel.refs.searchPackagesButton.click()
|
||||
expect(@panel.searchType).toBe 'packages'
|
||||
expect(@panel.search.callCount).toBe 2
|
||||
|
||||
@panel.refs.searchThemesButton.click()
|
||||
expect(@panel.searchType).toBe 'themes'
|
||||
expect(@panel.search.callCount).toBe 3
|
||||
|
||||
describe "searching packages", ->
|
||||
it "displays the packages in the order returned", ->
|
||||
spyOn(@panel.client, 'search').andCallFake -> Promise.resolve([{name: 'not-first'}, {name: 'first'}])
|
||||
spyOn(@panel, 'getPackageCardView').andCallThrough()
|
||||
|
||||
waitsForPromise =>
|
||||
@panel.search('first')
|
||||
|
||||
runs ->
|
||||
expect(@panel.getPackageCardView.argsForCall[0][0].name).toEqual 'not-first'
|
||||
expect(@panel.getPackageCardView.argsForCall[1][0].name).toEqual 'first'
|
||||
|
||||
describe "searching git packages", ->
|
||||
beforeEach ->
|
||||
spyOn(@panel, 'showGitInstallPackageCard').andCallThrough()
|
||||
|
||||
it "shows a git installation card with git specific info for ssh URLs", ->
|
||||
query = 'git@github.com:user/repo.git'
|
||||
@panel.performSearchForQuery(query)
|
||||
args = @panel.showGitInstallPackageCard.argsForCall[0][0]
|
||||
expect(args.name).toEqual query
|
||||
expect(args.gitUrlInfo).toBeTruthy()
|
||||
|
||||
it "shows a git installation card with git specific info for https URLs", ->
|
||||
query = 'https://github.com/user/repo.git'
|
||||
@panel.performSearchForQuery(query)
|
||||
args = @panel.showGitInstallPackageCard.argsForCall[0][0]
|
||||
expect(args.name).toEqual query
|
||||
expect(args.gitUrlInfo).toBeTruthy()
|
||||
|
||||
it "shows a git installation card with git specific info for shortcut URLs", ->
|
||||
query = 'user/repo'
|
||||
@panel.performSearchForQuery(query)
|
||||
args = @panel.showGitInstallPackageCard.argsForCall[0][0]
|
||||
expect(args.name).toEqual query
|
||||
expect(args.gitUrlInfo).toBeTruthy()
|
||||
|
||||
it "doesn't show a git installation card for normal packages", ->
|
||||
query = 'this-package-is-so-normal'
|
||||
@panel.performSearchForQuery(query)
|
||||
expect(@panel.showGitInstallPackageCard).not.toHaveBeenCalled()
|
||||
|
||||
describe "when a package with the same gitUrlInfo property is installed", ->
|
||||
beforeEach ->
|
||||
@gitUrlInfo = jasmine.createSpy('gitUrlInfo')
|
||||
@panel.showGitInstallPackageCard(gitUrlInfo: @gitUrlInfo)
|
||||
|
||||
it "replaces the package card with the newly installed pack object", ->
|
||||
newPack =
|
||||
gitUrlInfo: @gitUrlInfo
|
||||
spyOn(@panel, 'updateGitPackageCard')
|
||||
@packageManager.emitter.emit('package-installed', {pack: newPack})
|
||||
expect(@panel.updateGitPackageCard).toHaveBeenCalledWith newPack
|
140
packages/settings-view/spec/install-panel-spec.js
Normal file
140
packages/settings-view/spec/install-panel-spec.js
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
const InstallPanel = require('../lib/install-panel');
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const SettingsView = require('../lib/settings-view');
|
||||
|
||||
let packageManager;
|
||||
let panel;
|
||||
let gitUrlInfo;
|
||||
|
||||
describe('InstallPanel', function() {
|
||||
beforeEach(function() {
|
||||
const settingsView = new SettingsView();
|
||||
packageManager = new PackageManager();
|
||||
panel = new InstallPanel(settingsView, packageManager);
|
||||
});
|
||||
|
||||
describe("when the packages button is clicked", function() {
|
||||
beforeEach(function() {
|
||||
spyOn(panel, 'search');
|
||||
panel.refs.searchEditor.setText('something');
|
||||
});
|
||||
|
||||
it("performs a search for the contents of the input", function() {
|
||||
panel.refs.searchPackagesButton.click();
|
||||
expect(panel.searchType).toBe('packages');
|
||||
expect(panel.search).toHaveBeenCalledWith('something');
|
||||
expect(panel.search.callCount).toBe(1);
|
||||
|
||||
panel.refs.searchPackagesButton.click();
|
||||
expect(panel.searchType).toBe('packages');
|
||||
expect(panel.search).toHaveBeenCalledWith('something');
|
||||
expect(panel.search.callCount).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the themes button is clicked", function() {
|
||||
beforeEach(function() {
|
||||
spyOn(panel, 'search');
|
||||
panel.refs.searchEditor.setText('something');
|
||||
});
|
||||
|
||||
it("performs a search for the contents of the input", function() {
|
||||
panel.refs.searchThemesButton.click();
|
||||
expect(panel.searchType).toBe('themes');
|
||||
expect(panel.search.callCount).toBe(1);
|
||||
expect(panel.search).toHaveBeenCalledWith('something');
|
||||
|
||||
panel.refs.searchThemesButton.click();
|
||||
expect(panel.searchType).toBe('themes');
|
||||
expect(panel.search.callCount).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the buttons are toggled", function() {
|
||||
beforeEach(function() {
|
||||
spyOn(panel, 'search');
|
||||
panel.refs.searchEditor.setText('something');
|
||||
});
|
||||
|
||||
it("performs a search for the contents of the input", function() {
|
||||
panel.refs.searchThemesButton.click();
|
||||
expect(panel.searchType).toBe('themes');
|
||||
expect(panel.search.callCount).toBe(1);
|
||||
expect(panel.search).toHaveBeenCalledWith('something');
|
||||
|
||||
panel.refs.searchPackagesButton.click();
|
||||
expect(panel.searchType).toBe('packages');
|
||||
expect(panel.search.callCount).toBe(2);
|
||||
|
||||
panel.refs.searchThemesButton.click();
|
||||
expect(panel.searchType).toBe('themes');
|
||||
expect(panel.search.callCount).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("searching packages", () => it("displays the packages in the order returned", function() {
|
||||
spyOn(panel.client, 'search').andCallFake(() => Promise.resolve([{name: 'not-first'}, {name: 'first'}]));
|
||||
spyOn(panel, 'getPackageCardView').andCallThrough();
|
||||
|
||||
waitsForPromise(() => {
|
||||
return panel.search('first');
|
||||
});
|
||||
|
||||
return runs(function() {
|
||||
expect(panel.getPackageCardView.argsForCall[0][0].name).toEqual('not-first');
|
||||
expect(panel.getPackageCardView.argsForCall[1][0].name).toEqual('first');
|
||||
});
|
||||
}));
|
||||
|
||||
describe("searching git packages", function() {
|
||||
beforeEach(() => {
|
||||
return spyOn(panel, 'showGitInstallPackageCard').andCallThrough();
|
||||
});
|
||||
|
||||
it("shows a git installation card with git specific info for ssh URLs", function() {
|
||||
const query = 'git@github.com:user/repo.git';
|
||||
panel.performSearchForQuery(query);
|
||||
const args = panel.showGitInstallPackageCard.argsForCall[0][0];
|
||||
expect(args.name).toEqual(query);
|
||||
expect(args.gitUrlInfo).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows a git installation card with git specific info for https URLs", function() {
|
||||
const query = 'https://github.com/user/repo.git';
|
||||
panel.performSearchForQuery(query);
|
||||
const args = panel.showGitInstallPackageCard.argsForCall[0][0];
|
||||
expect(args.name).toEqual(query);
|
||||
expect(args.gitUrlInfo).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows a git installation card with git specific info for shortcut URLs", function() {
|
||||
const query = 'user/repo';
|
||||
panel.performSearchForQuery(query);
|
||||
const args = panel.showGitInstallPackageCard.argsForCall[0][0];
|
||||
expect(args.name).toEqual(query);
|
||||
expect(args.gitUrlInfo).toBeTruthy();
|
||||
});
|
||||
|
||||
it("doesn't show a git installation card for normal packages", function() {
|
||||
const query = 'this-package-is-so-normal';
|
||||
panel.performSearchForQuery(query);
|
||||
expect(panel.showGitInstallPackageCard).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when a package with the same gitUrlInfo property is installed", function() {
|
||||
beforeEach(function() {
|
||||
gitUrlInfo = jasmine.createSpy('gitUrlInfo');
|
||||
return panel.showGitInstallPackageCard({gitUrlInfo: gitUrlInfo});
|
||||
});
|
||||
|
||||
it("replaces the package card with the newly installed pack object", function() {
|
||||
const newPack =
|
||||
{gitUrlInfo: gitUrlInfo};
|
||||
spyOn(panel, 'updateGitPackageCard');
|
||||
packageManager.emitter.emit('package-installed', {pack: newPack});
|
||||
expect(panel.updateGitPackageCard).toHaveBeenCalledWith(newPack);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,193 +0,0 @@
|
||||
path = require 'path'
|
||||
|
||||
fs = require 'fs-plus'
|
||||
InstalledPackagesPanel = require '../lib/installed-packages-panel'
|
||||
PackageManager = require '../lib/package-manager'
|
||||
PackageCard = require '../lib/package-card'
|
||||
SettingsView = require '../lib/settings-view'
|
||||
|
||||
describe 'InstalledPackagesPanel', ->
|
||||
describe 'when the packages are loading', ->
|
||||
it 'filters packages by name once they have loaded', ->
|
||||
settingsView = new SettingsView
|
||||
@packageManager = new PackageManager
|
||||
@installed = JSON.parse fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json'))
|
||||
spyOn(@packageManager, 'getOutdated').andReturn new Promise ->
|
||||
spyOn(@packageManager, 'loadCompatiblePackageVersion').andCallFake ->
|
||||
spyOn(@packageManager, 'getInstalled').andReturn Promise.resolve(@installed)
|
||||
@panel = new InstalledPackagesPanel(settingsView, @packageManager)
|
||||
@panel.refs.filterEditor.setText('user-')
|
||||
window.advanceClock(@panel.refs.filterEditor.getBuffer().stoppedChangingDelay)
|
||||
|
||||
waitsFor ->
|
||||
@packageManager.getInstalled.callCount is 1 and @panel.refs.communityCount.textContent.indexOf('…') < 0
|
||||
|
||||
runs ->
|
||||
expect(@panel.refs.communityCount.textContent.trim()).toBe '1/1'
|
||||
expect(@panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
expect(@panel.refs.coreCount.textContent.trim()).toBe '0/1'
|
||||
expect(@panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 0
|
||||
|
||||
expect(@panel.refs.devCount.textContent.trim()).toBe '0/1'
|
||||
expect(@panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 0
|
||||
|
||||
describe 'when the packages have finished loading', ->
|
||||
beforeEach ->
|
||||
settingsView = new SettingsView
|
||||
@packageManager = new PackageManager
|
||||
@installed = JSON.parse fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json'))
|
||||
spyOn(@packageManager, 'getOutdated').andReturn new Promise ->
|
||||
spyOn(@packageManager, 'loadCompatiblePackageVersion').andCallFake ->
|
||||
spyOn(@packageManager, 'getInstalled').andReturn Promise.resolve(@installed)
|
||||
@panel = new InstalledPackagesPanel(settingsView, @packageManager)
|
||||
|
||||
waitsFor ->
|
||||
@packageManager.getInstalled.callCount is 1 and @panel.refs.communityCount.textContent.indexOf('…') < 0
|
||||
|
||||
it 'shows packages', ->
|
||||
expect(@panel.refs.communityCount.textContent.trim()).toBe '1'
|
||||
expect(@panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
expect(@panel.refs.coreCount.textContent.trim()).toBe '1'
|
||||
expect(@panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
expect(@panel.refs.devCount.textContent.trim()).toBe '1'
|
||||
expect(@panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
it 'filters packages by name', ->
|
||||
@panel.refs.filterEditor.setText('user-')
|
||||
window.advanceClock(@panel.refs.filterEditor.getBuffer().stoppedChangingDelay)
|
||||
expect(@panel.refs.communityCount.textContent.trim()).toBe '1/1'
|
||||
expect(@panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
expect(@panel.refs.coreCount.textContent.trim()).toBe '0/1'
|
||||
expect(@panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 0
|
||||
|
||||
expect(@panel.refs.devCount.textContent.trim()).toBe '0/1'
|
||||
expect(@panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 0
|
||||
|
||||
it 'adds newly installed packages to the list', ->
|
||||
[installCallback] = []
|
||||
spyOn(@packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
installCallback = callback
|
||||
onWillThrowError: ->
|
||||
spyOn(atom.packages, 'activatePackage').andCallFake (name) =>
|
||||
@installed.user.push {name}
|
||||
|
||||
expect(@panel.refs.communityCount.textContent.trim()).toBe '1'
|
||||
expect(@panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
@packageManager.install({name: 'another-user-package'})
|
||||
installCallback(0, '', '')
|
||||
|
||||
advanceClock InstalledPackagesPanel.loadPackagesDelay()
|
||||
waits 1
|
||||
runs ->
|
||||
expect(@panel.refs.communityCount.textContent.trim()).toBe '2'
|
||||
expect(@panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 2
|
||||
|
||||
it 'removes uninstalled packages from the list', ->
|
||||
[uninstallCallback] = []
|
||||
spyOn(@packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
uninstallCallback = callback
|
||||
onWillThrowError: ->
|
||||
spyOn(@packageManager, 'unload').andCallFake (name) =>
|
||||
@installed.user = []
|
||||
|
||||
expect(@panel.refs.communityCount.textContent.trim()).toBe '1'
|
||||
expect(@panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
@packageManager.uninstall({name: 'user-package'})
|
||||
uninstallCallback(0, '', '')
|
||||
|
||||
advanceClock InstalledPackagesPanel.loadPackagesDelay()
|
||||
waits 1
|
||||
runs ->
|
||||
expect(@panel.refs.communityCount.textContent.trim()).toBe '0'
|
||||
expect(@panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 0
|
||||
|
||||
describe 'expanding and collapsing sub-sections', ->
|
||||
beforeEach ->
|
||||
settingsView = new SettingsView
|
||||
@packageManager = new PackageManager
|
||||
@installed = JSON.parse fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json'))
|
||||
spyOn(@packageManager, 'getOutdated').andReturn new Promise ->
|
||||
spyOn(@packageManager, 'loadCompatiblePackageVersion').andCallFake ->
|
||||
spyOn(@packageManager, 'getInstalled').andReturn Promise.resolve(@installed)
|
||||
@panel = new InstalledPackagesPanel(settingsView, @packageManager)
|
||||
|
||||
waitsFor ->
|
||||
@packageManager.getInstalled.callCount is 1 and @panel.refs.communityCount.textContent.indexOf('…') < 0
|
||||
|
||||
it 'collapses and expands a sub-section if its header is clicked', ->
|
||||
@panel.element.querySelector('.sub-section.installed-packages .sub-section-heading').click()
|
||||
expect(@panel.element.querySelector('.sub-section.installed-packages')).toHaveClass 'collapsed'
|
||||
|
||||
expect(@panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass 'collapsed'
|
||||
expect(@panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
@panel.element.querySelector('.sub-section.installed-packages .sub-section-heading').click()
|
||||
expect(@panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
it 'can collapse and expand any of the sub-sections', ->
|
||||
expect(@panel.element.querySelectorAll('.sub-section-heading.has-items').length).toBe 3
|
||||
|
||||
for element in @panel.element.querySelectorAll('.sub-section-heading.has-items')
|
||||
element.click()
|
||||
|
||||
expect(@panel.element.querySelector('.sub-section.installed-packages')).toHaveClass 'collapsed'
|
||||
expect(@panel.element.querySelector('.sub-section.core-packages')).toHaveClass 'collapsed'
|
||||
expect(@panel.element.querySelector('.sub-section.dev-packages')).toHaveClass 'collapsed'
|
||||
|
||||
for element in @panel.element.querySelectorAll('.sub-section-heading.has-items')
|
||||
element.click()
|
||||
|
||||
expect(@panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass 'collapsed'
|
||||
expect(@panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass 'collapsed'
|
||||
expect(@panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
it 'can collapse sub-sections when filtering', ->
|
||||
@panel.refs.filterEditor.setText('user-')
|
||||
window.advanceClock(@panel.refs.filterEditor.getBuffer().stoppedChangingDelay)
|
||||
|
||||
hasItems = @panel.element.querySelectorAll('.sub-section-heading.has-items')
|
||||
expect(hasItems.length).toBe 1
|
||||
expect(hasItems[0].textContent).toMatch /Community Packages/
|
||||
|
||||
describe 'when there are no packages', ->
|
||||
beforeEach ->
|
||||
settingsView = new SettingsView
|
||||
@packageManager = new PackageManager
|
||||
@installed =
|
||||
dev: []
|
||||
user: []
|
||||
core: []
|
||||
spyOn(@packageManager, 'getOutdated').andReturn new Promise ->
|
||||
spyOn(@packageManager, 'loadCompatiblePackageVersion').andCallFake ->
|
||||
spyOn(@packageManager, 'getInstalled').andReturn Promise.resolve(@installed)
|
||||
@panel = new InstalledPackagesPanel(settingsView, @packageManager)
|
||||
|
||||
waitsFor ->
|
||||
@packageManager.getInstalled.callCount is 1 and @panel.refs.communityCount.textContent.indexOf('…') < 0
|
||||
|
||||
it 'has a count of zero in all headings', ->
|
||||
expect(@panel.element.querySelector('.section-heading-count').textContent).toMatch /^0+$/
|
||||
expect(@panel.element.querySelectorAll('.sub-section .icon-package').length).toBe 4
|
||||
expect(@panel.element.querySelectorAll('.sub-section .icon-package.has-items').length).toBe 0
|
||||
|
||||
it 'can not collapse and expand any of the sub-sections', ->
|
||||
for element in @panel.element.querySelectorAll('.sub-section .icon-package')
|
||||
element.click()
|
||||
|
||||
expect(@panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass 'collapsed'
|
||||
expect(@panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass 'collapsed'
|
||||
expect(@panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
it 'does not allow collapsing on any section when filtering', ->
|
||||
@panel.refs.filterEditor.setText('user-')
|
||||
window.advanceClock(@panel.refs.filterEditor.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(@panel.element.querySelector('.section-heading-count').textContent).toMatch /^(0\/0)+$/
|
||||
expect(@panel.element.querySelectorAll('.sub-section .icon-package').length).toBe 4
|
||||
expect(@panel.element.querySelectorAll('.sub-section .icon-paintcan.has-items').length).toBe 0
|
228
packages/settings-view/spec/installed-packages-panel-spec.js
Normal file
228
packages/settings-view/spec/installed-packages-panel-spec.js
Normal file
@ -0,0 +1,228 @@
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const fs = require('fs-plus');
|
||||
const InstalledPackagesPanel = require('../lib/installed-packages-panel');
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const PackageCard = require('../lib/package-card');
|
||||
const SettingsView = require('../lib/settings-view');
|
||||
|
||||
describe('InstalledPackagesPanel', function() {
|
||||
describe('when the packages are loading', () => it('filters packages by name once they have loaded', function() {
|
||||
const settingsView = new SettingsView;
|
||||
this.packageManager = new PackageManager;
|
||||
this.installed = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json')));
|
||||
spyOn(this.packageManager, 'getOutdated').andReturn(new Promise(function() {}));
|
||||
spyOn(this.packageManager, 'loadCompatiblePackageVersion').andCallFake(function() {});
|
||||
spyOn(this.packageManager, 'getInstalled').andReturn(Promise.resolve(this.installed));
|
||||
this.panel = new InstalledPackagesPanel(settingsView, this.packageManager);
|
||||
this.panel.refs.filterEditor.setText('user-');
|
||||
window.advanceClock(this.panel.refs.filterEditor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
waitsFor(function() {
|
||||
return (this.packageManager.getInstalled.callCount === 1) && (this.panel.refs.communityCount.textContent.indexOf('…') < 0);
|
||||
});
|
||||
|
||||
runs(function() {
|
||||
expect(this.panel.refs.communityCount.textContent.trim()).toBe('1/1');
|
||||
expect(this.panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
expect(this.panel.refs.coreCount.textContent.trim()).toBe('0/1');
|
||||
expect(this.panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(0);
|
||||
|
||||
expect(this.panel.refs.devCount.textContent.trim()).toBe('0/1');
|
||||
expect(this.panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(0);
|
||||
});
|
||||
}));
|
||||
|
||||
describe('when the packages have finished loading', function() {
|
||||
beforeEach(function() {
|
||||
const settingsView = new SettingsView;
|
||||
this.packageManager = new PackageManager;
|
||||
this.installed = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json')));
|
||||
spyOn(this.packageManager, 'getOutdated').andReturn(new Promise(function() {}));
|
||||
spyOn(this.packageManager, 'loadCompatiblePackageVersion').andCallFake(function() {});
|
||||
spyOn(this.packageManager, 'getInstalled').andReturn(Promise.resolve(this.installed));
|
||||
this.panel = new InstalledPackagesPanel(settingsView, this.packageManager);
|
||||
|
||||
waitsFor(function() {
|
||||
return (this.packageManager.getInstalled.callCount === 1) && (this.panel.refs.communityCount.textContent.indexOf('…') < 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows packages', function() {
|
||||
expect(this.panel.refs.communityCount.textContent.trim()).toBe('1');
|
||||
expect(this.panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
expect(this.panel.refs.coreCount.textContent.trim()).toBe('1');
|
||||
expect(this.panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
expect(this.panel.refs.devCount.textContent.trim()).toBe('1');
|
||||
expect(this.panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
});
|
||||
|
||||
it('filters packages by name', function() {
|
||||
this.panel.refs.filterEditor.setText('user-');
|
||||
window.advanceClock(this.panel.refs.filterEditor.getBuffer().stoppedChangingDelay);
|
||||
expect(this.panel.refs.communityCount.textContent.trim()).toBe('1/1');
|
||||
expect(this.panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
expect(this.panel.refs.coreCount.textContent.trim()).toBe('0/1');
|
||||
expect(this.panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(0);
|
||||
|
||||
expect(this.panel.refs.devCount.textContent.trim()).toBe('0/1');
|
||||
expect(this.panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(0);
|
||||
});
|
||||
|
||||
it('adds newly installed packages to the list', function() {
|
||||
let [installCallback] = [];
|
||||
spyOn(this.packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
installCallback = callback;
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
spyOn(atom.packages, 'activatePackage').andCallFake(name => {
|
||||
return this.installed.user.push({name});
|
||||
});
|
||||
|
||||
expect(this.panel.refs.communityCount.textContent.trim()).toBe('1');
|
||||
expect(this.panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
this.packageManager.install({name: 'another-user-package'});
|
||||
installCallback(0, '', '');
|
||||
|
||||
advanceClock(InstalledPackagesPanel.loadPackagesDelay());
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(this.panel.refs.communityCount.textContent.trim()).toBe('2');
|
||||
expect(this.panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('removes uninstalled packages from the list', function() {
|
||||
let [uninstallCallback] = [];
|
||||
spyOn(this.packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
uninstallCallback = callback;
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
spyOn(this.packageManager, 'unload').andCallFake(name => {
|
||||
return this.installed.user = [];
|
||||
});
|
||||
|
||||
expect(this.panel.refs.communityCount.textContent.trim()).toBe('1');
|
||||
expect(this.panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
this.packageManager.uninstall({name: 'user-package'});
|
||||
uninstallCallback(0, '', '');
|
||||
|
||||
advanceClock(InstalledPackagesPanel.loadPackagesDelay());
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(this.panel.refs.communityCount.textContent.trim()).toBe('0');
|
||||
expect(this.panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('expanding and collapsing sub-sections', function() {
|
||||
beforeEach(function() {
|
||||
const settingsView = new SettingsView;
|
||||
this.packageManager = new PackageManager;
|
||||
this.installed = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json')));
|
||||
spyOn(this.packageManager, 'getOutdated').andReturn(new Promise(function() {}));
|
||||
spyOn(this.packageManager, 'loadCompatiblePackageVersion').andCallFake(function() {});
|
||||
spyOn(this.packageManager, 'getInstalled').andReturn(Promise.resolve(this.installed));
|
||||
this.panel = new InstalledPackagesPanel(settingsView, this.packageManager);
|
||||
|
||||
waitsFor(function() {
|
||||
return (this.packageManager.getInstalled.callCount === 1) && (this.panel.refs.communityCount.textContent.indexOf('…') < 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('collapses and expands a sub-section if its header is clicked', function() {
|
||||
this.panel.element.querySelector('.sub-section.installed-packages .sub-section-heading').click();
|
||||
expect(this.panel.element.querySelector('.sub-section.installed-packages')).toHaveClass('collapsed');
|
||||
|
||||
expect(this.panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass('collapsed');
|
||||
expect(this.panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass('collapsed');
|
||||
|
||||
this.panel.element.querySelector('.sub-section.installed-packages .sub-section-heading').click();
|
||||
expect(this.panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass('collapsed');
|
||||
});
|
||||
|
||||
it('can collapse and expand any of the sub-sections', function() {
|
||||
let element;
|
||||
expect(this.panel.element.querySelectorAll('.sub-section-heading.has-items').length).toBe(3);
|
||||
|
||||
for (element of Array.from(this.panel.element.querySelectorAll('.sub-section-heading.has-items'))) {
|
||||
element.click();
|
||||
}
|
||||
|
||||
expect(this.panel.element.querySelector('.sub-section.installed-packages')).toHaveClass('collapsed');
|
||||
expect(this.panel.element.querySelector('.sub-section.core-packages')).toHaveClass('collapsed');
|
||||
expect(this.panel.element.querySelector('.sub-section.dev-packages')).toHaveClass('collapsed');
|
||||
|
||||
for (element of Array.from(this.panel.element.querySelectorAll('.sub-section-heading.has-items'))) {
|
||||
element.click();
|
||||
}
|
||||
|
||||
expect(this.panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass('collapsed');
|
||||
expect(this.panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass('collapsed');
|
||||
expect(this.panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass('collapsed');
|
||||
});
|
||||
|
||||
it('can collapse sub-sections when filtering', function() {
|
||||
this.panel.refs.filterEditor.setText('user-');
|
||||
window.advanceClock(this.panel.refs.filterEditor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
const hasItems = this.panel.element.querySelectorAll('.sub-section-heading.has-items');
|
||||
expect(hasItems.length).toBe(1);
|
||||
expect(hasItems[0].textContent).toMatch(/Community Packages/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are no packages', function() {
|
||||
beforeEach(function() {
|
||||
const settingsView = new SettingsView;
|
||||
this.packageManager = new PackageManager;
|
||||
this.installed = {
|
||||
dev: [],
|
||||
user: [],
|
||||
core: []
|
||||
};
|
||||
spyOn(this.packageManager, 'getOutdated').andReturn(new Promise(function() {}));
|
||||
spyOn(this.packageManager, 'loadCompatiblePackageVersion').andCallFake(function() {});
|
||||
spyOn(this.packageManager, 'getInstalled').andReturn(Promise.resolve(this.installed));
|
||||
this.panel = new InstalledPackagesPanel(settingsView, this.packageManager);
|
||||
|
||||
waitsFor(function() {
|
||||
return (this.packageManager.getInstalled.callCount === 1) && (this.panel.refs.communityCount.textContent.indexOf('…') < 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('has a count of zero in all headings', function() {
|
||||
expect(this.panel.element.querySelector('.section-heading-count').textContent).toMatch(/^0+$/);
|
||||
expect(this.panel.element.querySelectorAll('.sub-section .icon-package').length).toBe(4);
|
||||
expect(this.panel.element.querySelectorAll('.sub-section .icon-package.has-items').length).toBe(0);
|
||||
});
|
||||
|
||||
it('can not collapse and expand any of the sub-sections', function() {
|
||||
let element;
|
||||
for (element of Array.from(this.panel.element.querySelectorAll('.sub-section .icon-package'))) {
|
||||
element.click();
|
||||
}
|
||||
|
||||
expect(this.panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass('collapsed');
|
||||
expect(this.panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass('collapsed');
|
||||
expect(this.panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass('collapsed');
|
||||
});
|
||||
|
||||
it('does not allow collapsing on any section when filtering', function() {
|
||||
this.panel.refs.filterEditor.setText('user-');
|
||||
window.advanceClock(this.panel.refs.filterEditor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
expect(this.panel.element.querySelector('.section-heading-count').textContent).toMatch(/^(0\/0)+$/);
|
||||
expect(this.panel.element.querySelectorAll('.sub-section .icon-package').length).toBe(4);
|
||||
expect(this.panel.element.querySelectorAll('.sub-section .icon-paintcan.has-items').length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,132 +0,0 @@
|
||||
path = require 'path'
|
||||
KeybindingsPanel = require '../lib/keybindings-panel'
|
||||
|
||||
describe "KeybindingsPanel", ->
|
||||
[keyBindings, panel] = []
|
||||
|
||||
beforeEach ->
|
||||
expect(atom.keymaps).toBeDefined()
|
||||
keySource = "#{atom.getLoadSettings().resourcePath}#{path.sep}keymaps"
|
||||
keyBindings = [
|
||||
{
|
||||
source: keySource
|
||||
keystrokes: 'ctrl-a'
|
||||
command: 'core:select-all'
|
||||
selector: '.editor, .platform-test'
|
||||
}
|
||||
{
|
||||
source: keySource
|
||||
keystrokes: 'ctrl-u'
|
||||
command: 'core:undo'
|
||||
selector: ".platform-test"
|
||||
}
|
||||
{
|
||||
source: keySource
|
||||
keystrokes: 'ctrl-u'
|
||||
command: 'core:undo'
|
||||
selector: ".platform-a, .platform-b"
|
||||
}
|
||||
{
|
||||
source: keySource
|
||||
keystrokes: 'shift-\\ \\'
|
||||
command: 'core:undo'
|
||||
selector: '.editor'
|
||||
}
|
||||
{
|
||||
source: keySource
|
||||
keystrokes: 'ctrl-z\''
|
||||
command: 'core:toggle'
|
||||
selector: 'atom-text-editor[data-grammar~=\'css\']'
|
||||
}
|
||||
]
|
||||
spyOn(atom.keymaps, 'getKeyBindings').andReturn(keyBindings)
|
||||
panel = new KeybindingsPanel
|
||||
|
||||
it "loads and displays core key bindings", ->
|
||||
expect(panel.refs.keybindingRows.children.length).toBe 3
|
||||
|
||||
row = panel.refs.keybindingRows.children[0]
|
||||
expect(row.querySelector('.keystroke').textContent).toBe 'ctrl-a'
|
||||
expect(row.querySelector('.command').textContent).toBe 'core:select-all'
|
||||
expect(row.querySelector('.source').textContent).toBe 'Core'
|
||||
expect(row.querySelector('.selector').textContent).toBe '.editor, .platform-test'
|
||||
|
||||
describe "when a keybinding is copied", ->
|
||||
describe "when the keybinding file ends in .cson", ->
|
||||
it "writes a CSON snippet to the clipboard", ->
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.cson'
|
||||
panel.element.querySelector('.copy-icon').click()
|
||||
expect(atom.clipboard.read()).toBe """
|
||||
'.editor, .platform-test':
|
||||
'ctrl-a': 'core:select-all'
|
||||
"""
|
||||
|
||||
describe "when the keybinding file ends in .json", ->
|
||||
it "writes a JSON snippet to the clipboard", ->
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.json'
|
||||
panel.element.querySelector('.copy-icon').click()
|
||||
expect(atom.clipboard.read()).toBe """
|
||||
".editor, .platform-test": {
|
||||
"ctrl-a": "core:select-all"
|
||||
}
|
||||
"""
|
||||
|
||||
describe "when the keybinding contains special characters", ->
|
||||
it "escapes the backslashes before copying", ->
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.cson'
|
||||
panel.element.querySelectorAll('.copy-icon')[2].click()
|
||||
expect(atom.clipboard.read()).toBe """
|
||||
'.editor':
|
||||
'shift-\\\\ \\\\': 'core:undo'
|
||||
"""
|
||||
|
||||
it "escapes the single quotes before copying", ->
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn 'keymap.cson'
|
||||
panel.element.querySelectorAll('.copy-icon')[1].click()
|
||||
expect(atom.clipboard.read()).toBe """
|
||||
'atom-text-editor[data-grammar~=\\'css\\']':
|
||||
'ctrl-z\\'': 'core:toggle'
|
||||
"""
|
||||
|
||||
describe "when the key bindings change", ->
|
||||
it "reloads the key bindings", ->
|
||||
keyBindings.push
|
||||
source: atom.keymaps.getUserKeymapPath(), keystrokes: 'ctrl-b', command: 'core:undo', selector: '.editor'
|
||||
atom.keymaps.emitter.emit 'did-reload-keymap'
|
||||
|
||||
waitsFor "the new keybinding to show up in the keybinding panel", ->
|
||||
panel.refs.keybindingRows.children.length is 4
|
||||
|
||||
runs ->
|
||||
row = panel.refs.keybindingRows.children[1]
|
||||
expect(row.querySelector('.keystroke').textContent).toBe 'ctrl-b'
|
||||
expect(row.querySelector('.command').textContent).toBe 'core:undo'
|
||||
expect(row.querySelector('.source').textContent).toBe 'User'
|
||||
expect(row.querySelector('.selector').textContent).toBe '.editor'
|
||||
|
||||
describe "when searching key bindings", ->
|
||||
it "find case-insensitive results", ->
|
||||
keyBindings.push
|
||||
source: "#{atom.getLoadSettings().resourcePath}#{path.sep}keymaps", keystrokes: 'F11', command: 'window:toggle-full-screen', selector: 'body'
|
||||
atom.keymaps.emitter.emit 'did-reload-keymap'
|
||||
|
||||
panel.filterKeyBindings keyBindings, 'f11'
|
||||
|
||||
expect(panel.refs.keybindingRows.children.length).toBe 1
|
||||
|
||||
row = panel.refs.keybindingRows.children[0]
|
||||
expect(row.querySelector('.keystroke').textContent).toBe 'F11'
|
||||
expect(row.querySelector('.command').textContent).toBe 'window:toggle-full-screen'
|
||||
expect(row.querySelector('.source').textContent).toBe 'Core'
|
||||
expect(row.querySelector('.selector').textContent).toBe 'body'
|
||||
|
||||
it "perform a fuzzy match for each keyword", ->
|
||||
panel.filterKeyBindings keyBindings, 'core ctrl-a'
|
||||
|
||||
expect(panel.refs.keybindingRows.children.length).toBe 1
|
||||
|
||||
row = panel.refs.keybindingRows.children[0]
|
||||
expect(row.querySelector('.keystroke').textContent).toBe 'ctrl-a'
|
||||
expect(row.querySelector('.command').textContent).toBe 'core:select-all'
|
||||
expect(row.querySelector('.source').textContent).toBe 'Core'
|
||||
expect(row.querySelector('.selector').textContent).toBe '.editor, .platform-test'
|
147
packages/settings-view/spec/keybindings-panel-spec.js
Normal file
147
packages/settings-view/spec/keybindings-panel-spec.js
Normal file
@ -0,0 +1,147 @@
|
||||
|
||||
const path = require('path');
|
||||
const KeybindingsPanel = require('../lib/keybindings-panel');
|
||||
|
||||
describe("KeybindingsPanel", function() {
|
||||
let [keyBindings, panel] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
expect(atom.keymaps).toBeDefined();
|
||||
const keySource = `${atom.getLoadSettings().resourcePath}${path.sep}keymaps`;
|
||||
keyBindings = [
|
||||
{
|
||||
source: keySource,
|
||||
keystrokes: 'ctrl-a',
|
||||
command: 'core:select-all',
|
||||
selector: '.editor, .platform-test'
|
||||
},
|
||||
{
|
||||
source: keySource,
|
||||
keystrokes: 'ctrl-u',
|
||||
command: 'core:undo',
|
||||
selector: ".platform-test"
|
||||
},
|
||||
{
|
||||
source: keySource,
|
||||
keystrokes: 'ctrl-u',
|
||||
command: 'core:undo',
|
||||
selector: ".platform-a, .platform-b"
|
||||
},
|
||||
{
|
||||
source: keySource,
|
||||
keystrokes: 'shift-\\ \\',
|
||||
command: 'core:undo',
|
||||
selector: '.editor'
|
||||
},
|
||||
{
|
||||
source: keySource,
|
||||
keystrokes: 'ctrl-z\'',
|
||||
command: 'core:toggle',
|
||||
selector: 'atom-text-editor[data-grammar~=\'css\']'
|
||||
}
|
||||
];
|
||||
spyOn(atom.keymaps, 'getKeyBindings').andReturn(keyBindings);
|
||||
return panel = new KeybindingsPanel;
|
||||
});
|
||||
|
||||
it("loads and displays core key bindings", function() {
|
||||
expect(panel.refs.keybindingRows.children.length).toBe(3);
|
||||
|
||||
const row = panel.refs.keybindingRows.children[0];
|
||||
expect(row.querySelector('.keystroke').textContent).toBe('ctrl-a');
|
||||
expect(row.querySelector('.command').textContent).toBe('core:select-all');
|
||||
expect(row.querySelector('.source').textContent).toBe('Core');
|
||||
expect(row.querySelector('.selector').textContent).toBe('.editor, .platform-test');
|
||||
});
|
||||
|
||||
describe("when a keybinding is copied", function() {
|
||||
describe("when the keybinding file ends in .cson", () => it("writes a CSON snippet to the clipboard", function() {
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn('keymap.cson');
|
||||
panel.element.querySelector('.copy-icon').click();
|
||||
expect(atom.clipboard.read()).toBe(`\
|
||||
'.editor, .platform-test':
|
||||
'ctrl-a': 'core:select-all'\
|
||||
`
|
||||
);
|
||||
}));
|
||||
|
||||
describe("when the keybinding file ends in .json", () => it("writes a JSON snippet to the clipboard", function() {
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn('keymap.json');
|
||||
panel.element.querySelector('.copy-icon').click();
|
||||
expect(atom.clipboard.read()).toBe(`\
|
||||
".editor, .platform-test": {
|
||||
"ctrl-a": "core:select-all"
|
||||
}\
|
||||
`
|
||||
);
|
||||
}));
|
||||
|
||||
describe("when the keybinding contains special characters", function() {
|
||||
it("escapes the backslashes before copying", function() {
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn('keymap.cson');
|
||||
panel.element.querySelectorAll('.copy-icon')[2].click();
|
||||
expect(atom.clipboard.read()).toBe(`\
|
||||
'.editor':
|
||||
'shift-\\\\ \\\\': 'core:undo'\
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it("escapes the single quotes before copying", function() {
|
||||
spyOn(atom.keymaps, 'getUserKeymapPath').andReturn('keymap.cson');
|
||||
panel.element.querySelectorAll('.copy-icon')[1].click();
|
||||
expect(atom.clipboard.read()).toBe(`\
|
||||
'atom-text-editor[data-grammar~=\\'css\\']':
|
||||
'ctrl-z\\'': 'core:toggle'\
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the key bindings change", () => it("reloads the key bindings", function() {
|
||||
keyBindings.push({
|
||||
source: atom.keymaps.getUserKeymapPath(), keystrokes: 'ctrl-b', command: 'core:undo', selector: '.editor'});
|
||||
atom.keymaps.emitter.emit('did-reload-keymap');
|
||||
|
||||
waitsFor("the new keybinding to show up in the keybinding panel", () => panel.refs.keybindingRows.children.length === 4);
|
||||
|
||||
runs(function() {
|
||||
const row = panel.refs.keybindingRows.children[1];
|
||||
expect(row.querySelector('.keystroke').textContent).toBe('ctrl-b');
|
||||
expect(row.querySelector('.command').textContent).toBe('core:undo');
|
||||
expect(row.querySelector('.source').textContent).toBe('User');
|
||||
expect(row.querySelector('.selector').textContent).toBe('.editor');
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when searching key bindings", function() {
|
||||
it("find case-insensitive results", function() {
|
||||
keyBindings.push({
|
||||
source: `${atom.getLoadSettings().resourcePath}${path.sep}keymaps`, keystrokes: 'F11', command: 'window:toggle-full-screen', selector: 'body'});
|
||||
atom.keymaps.emitter.emit('did-reload-keymap');
|
||||
|
||||
panel.filterKeyBindings(keyBindings, 'f11');
|
||||
|
||||
expect(panel.refs.keybindingRows.children.length).toBe(1);
|
||||
|
||||
const row = panel.refs.keybindingRows.children[0];
|
||||
expect(row.querySelector('.keystroke').textContent).toBe('F11');
|
||||
expect(row.querySelector('.command').textContent).toBe('window:toggle-full-screen');
|
||||
expect(row.querySelector('.source').textContent).toBe('Core');
|
||||
expect(row.querySelector('.selector').textContent).toBe('body');
|
||||
});
|
||||
|
||||
it("perform a fuzzy match for each keyword", function() {
|
||||
panel.filterKeyBindings(keyBindings, 'core ctrl-a');
|
||||
|
||||
expect(panel.refs.keybindingRows.children.length).toBe(1);
|
||||
|
||||
const row = panel.refs.keybindingRows.children[0];
|
||||
expect(row.querySelector('.keystroke').textContent).toBe('ctrl-a');
|
||||
expect(row.querySelector('.command').textContent).toBe('core:select-all');
|
||||
expect(row.querySelector('.source').textContent).toBe('Core');
|
||||
expect(row.querySelector('.selector').textContent).toBe('.editor, .platform-test');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,35 +0,0 @@
|
||||
List = require '../lib/list'
|
||||
|
||||
describe 'List', ->
|
||||
list = null
|
||||
|
||||
beforeEach ->
|
||||
list = new List('name')
|
||||
|
||||
it 'emits add and remove events when setting items', ->
|
||||
addHandler = jasmine.createSpy()
|
||||
removeHandler = jasmine.createSpy()
|
||||
list.onDidAddItem(addHandler)
|
||||
list.onDidRemoveItem(removeHandler)
|
||||
|
||||
items = [{name: 'one', text: 'a'}, {name: 'two', text: 'b'}]
|
||||
list.setItems(items)
|
||||
expect(addHandler.callCount).toBe 2
|
||||
expect(removeHandler.callCount).toBe 0
|
||||
|
||||
addHandler.reset()
|
||||
removeHandler.reset()
|
||||
|
||||
items = [{name: 'three', text: 'c'}, {name: 'two', text: 'b'}]
|
||||
list.setItems(items)
|
||||
expect(addHandler.callCount).toBe 1
|
||||
expect(removeHandler.callCount).toBe 1
|
||||
expect(addHandler.mostRecentCall.args[0]).toEqual {name: 'three', text: 'c'}
|
||||
expect(removeHandler.mostRecentCall.args[0]).toEqual {name: 'one', text: 'a'}
|
||||
expect(list.getItems()).toEqual items
|
||||
|
||||
addHandler.reset()
|
||||
removeHandler.reset()
|
||||
items.push {name: 'four'}
|
||||
list.setItems(items)
|
||||
expect(addHandler.callCount).toBe 1
|
37
packages/settings-view/spec/list-spec.js
Normal file
37
packages/settings-view/spec/list-spec.js
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
const List = require('../lib/list');
|
||||
|
||||
describe('List', () => {
|
||||
let list = null;
|
||||
|
||||
beforeEach(() => list = new List('name'));
|
||||
|
||||
it('emits add and remove events when setting items', () => {
|
||||
const addHandler = jasmine.createSpy();
|
||||
const removeHandler = jasmine.createSpy();
|
||||
list.onDidAddItem(addHandler);
|
||||
list.onDidRemoveItem(removeHandler);
|
||||
|
||||
let items = [{name: 'one', text: 'a'}, {name: 'two', text: 'b'}];
|
||||
list.setItems(items);
|
||||
expect(addHandler.callCount).toBe(2);
|
||||
expect(removeHandler.callCount).toBe(0);
|
||||
|
||||
addHandler.reset();
|
||||
removeHandler.reset();
|
||||
|
||||
items = [{name: 'three', text: 'c'}, {name: 'two', text: 'b'}];
|
||||
list.setItems(items);
|
||||
expect(addHandler.callCount).toBe(1);
|
||||
expect(removeHandler.callCount).toBe(1);
|
||||
expect(addHandler.mostRecentCall.args[0]).toEqual({name: 'three', text: 'c'});
|
||||
expect(removeHandler.mostRecentCall.args[0]).toEqual({name: 'one', text: 'a'});
|
||||
expect(list.getItems()).toEqual(items);
|
||||
|
||||
addHandler.reset();
|
||||
removeHandler.reset();
|
||||
items.push({name: 'four'});
|
||||
list.setItems(items);
|
||||
expect(addHandler.callCount).toBe(1);
|
||||
});
|
||||
});
|
@ -1,67 +0,0 @@
|
||||
List = require '../lib/list'
|
||||
ListView = require '../lib/list-view'
|
||||
|
||||
describe 'ListView', ->
|
||||
[list, view, container] = []
|
||||
|
||||
beforeEach ->
|
||||
list = new List('name')
|
||||
container = document.createElement('div')
|
||||
view = new ListView list, container, (item) ->
|
||||
element = document.createElement('div')
|
||||
element.classList.add(item.name)
|
||||
element.textContent = "#{item.name}|#{item.text}"
|
||||
{element, destroy: -> element.remove()}
|
||||
|
||||
it 'updates the list when the items are changed', ->
|
||||
expect(container.children.length).toBe 0
|
||||
|
||||
items = [{name: 'one', text: 'a'}, {name: 'two', text: 'b'}]
|
||||
list.setItems(items)
|
||||
expect(container.children.length).toBe 2
|
||||
expect(container.querySelector('.one').textContent).toBe 'one|a'
|
||||
expect(container.querySelector('.two').textContent).toBe 'two|b'
|
||||
|
||||
items = [{name: 'three', text: 'c'}, {name: 'two', text: 'b'}]
|
||||
list.setItems(items)
|
||||
expect(container.children.length).toBe 2
|
||||
expect(container.querySelector('.one')).not.toExist()
|
||||
expect(container.querySelector('.two').textContent).toBe 'two|b'
|
||||
expect(container.querySelector('.three').textContent).toBe 'three|c'
|
||||
|
||||
it 'filters views', ->
|
||||
items = [
|
||||
{name: 'one', text: '', filterText: 'x'},
|
||||
{name: 'two', text: '', filterText: 'y'}
|
||||
{name: 'three', text: '', filterText: 'x'}
|
||||
{name: 'four', text: '', filterText: 'z'}
|
||||
]
|
||||
|
||||
list.setItems(items)
|
||||
views = view.filterViews (item) -> item.filterText is 'x'
|
||||
|
||||
expect(views).toHaveLength 2
|
||||
expect(views[0].element.textContent).toBe 'one|'
|
||||
expect(views[1].element.textContent).toBe 'three|'
|
||||
|
||||
it 'filters views after an update', ->
|
||||
items = [
|
||||
{name: 'one', text: '', filterText: 'x'},
|
||||
{name: 'two', text: '', filterText: 'y'}
|
||||
{name: 'three', text: '', filterText: 'x'}
|
||||
{name: 'four', text: '', filterText: 'z'}
|
||||
]
|
||||
list.setItems(items)
|
||||
|
||||
items = [
|
||||
{name: 'one', text: '', filterText: 'x'},
|
||||
{name: 'two', text: '', filterText: 'y'}
|
||||
{name: 'three', text: '', filterText: 'x'}
|
||||
{name: 'four', text: '', filterText: 'z'}
|
||||
]
|
||||
list.setItems(items)
|
||||
views = view.filterViews (item) -> item.filterText is 'x'
|
||||
|
||||
expect(views).toHaveLength 2
|
||||
expect(views[0].element.textContent).toBe 'one|'
|
||||
expect(views[1].element.textContent).toBe 'three|'
|
73
packages/settings-view/spec/list-view-spec.js
Normal file
73
packages/settings-view/spec/list-view-spec.js
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
const List = require('../lib/list');
|
||||
const ListView = require('../lib/list-view');
|
||||
|
||||
describe('ListView', () => {
|
||||
let [list, view, container] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
list = new List('name');
|
||||
container = document.createElement('div');
|
||||
return view = new ListView(list, container, function(item) {
|
||||
const element = document.createElement('div');
|
||||
element.classList.add(item.name);
|
||||
element.textContent = `${item.name}|${item.text}`;
|
||||
return {element, destroy() { return element.remove(); }};
|
||||
});});
|
||||
|
||||
it('updates the list when the items are changed', () => {
|
||||
expect(container.children.length).toBe(0);
|
||||
|
||||
let items = [{name: 'one', text: 'a'}, {name: 'two', text: 'b'}];
|
||||
list.setItems(items);
|
||||
expect(container.children.length).toBe(2);
|
||||
expect(container.querySelector('.one').textContent).toBe('one|a');
|
||||
expect(container.querySelector('.two').textContent).toBe('two|b');
|
||||
|
||||
items = [{name: 'three', text: 'c'}, {name: 'two', text: 'b'}];
|
||||
list.setItems(items);
|
||||
expect(container.children.length).toBe(2);
|
||||
expect(container.querySelector('.one')).not.toExist();
|
||||
expect(container.querySelector('.two').textContent).toBe('two|b');
|
||||
expect(container.querySelector('.three').textContent).toBe('three|c');
|
||||
});
|
||||
|
||||
it('filters views', () => {
|
||||
const items = [
|
||||
{name: 'one', text: '', filterText: 'x'},
|
||||
{name: 'two', text: '', filterText: 'y'},
|
||||
{name: 'three', text: '', filterText: 'x'},
|
||||
{name: 'four', text: '', filterText: 'z'}
|
||||
];
|
||||
|
||||
list.setItems(items);
|
||||
const views = view.filterViews(item => item.filterText === 'x');
|
||||
|
||||
expect(views).toHaveLength(2);
|
||||
expect(views[0].element.textContent).toBe('one|');
|
||||
expect(views[1].element.textContent).toBe('three|');
|
||||
});
|
||||
|
||||
it('filters views after an update', () => {
|
||||
let items = [
|
||||
{name: 'one', text: '', filterText: 'x'},
|
||||
{name: 'two', text: '', filterText: 'y'},
|
||||
{name: 'three', text: '', filterText: 'x'},
|
||||
{name: 'four', text: '', filterText: 'z'}
|
||||
];
|
||||
list.setItems(items);
|
||||
|
||||
items = [
|
||||
{name: 'one', text: '', filterText: 'x'},
|
||||
{name: 'two', text: '', filterText: 'y'},
|
||||
{name: 'three', text: '', filterText: 'x'},
|
||||
{name: 'four', text: '', filterText: 'z'}
|
||||
];
|
||||
list.setItems(items);
|
||||
const views = view.filterViews(item => item.filterText === 'x');
|
||||
|
||||
expect(views).toHaveLength(2);
|
||||
expect(views[0].element.textContent).toBe('one|');
|
||||
expect(views[1].element.textContent).toBe('three|');
|
||||
});
|
||||
});
|
@ -1,384 +0,0 @@
|
||||
path = require 'path'
|
||||
PackageCard = require '../lib/package-card'
|
||||
PackageManager = require '../lib/package-manager'
|
||||
SettingsView = require '../lib/settings-view'
|
||||
|
||||
describe "PackageCard", ->
|
||||
setPackageStatusSpies = (opts) ->
|
||||
spyOn(PackageCard.prototype, 'isInstalled').andReturn(opts.installed)
|
||||
spyOn(PackageCard.prototype, 'isDisabled').andReturn(opts.disabled)
|
||||
spyOn(PackageCard.prototype, 'hasSettings').andReturn(opts.hasSettings)
|
||||
|
||||
[card, packageManager] = []
|
||||
|
||||
beforeEach ->
|
||||
packageManager = new PackageManager()
|
||||
spyOn(packageManager, 'runCommand')
|
||||
|
||||
it "doesn't show the disable control for a theme", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false}
|
||||
card = new PackageCard({theme: 'syntax', name: 'test-theme'}, new SettingsView(), packageManager)
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.enablementButton).not.toBeVisible()
|
||||
|
||||
it "doesn't show the status indicator for a theme", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false}
|
||||
card = new PackageCard {theme: 'syntax', name: 'test-theme'}, new SettingsView(), packageManager
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.statusIndicatorButton).not.toBeVisible()
|
||||
|
||||
it "doesn't show the settings button for a theme", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false}
|
||||
card = new PackageCard {theme: 'syntax', name: 'test-theme'}, new SettingsView(), packageManager
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.settingsButton).not.toBeVisible()
|
||||
|
||||
it "doesn't show the settings button on the settings view", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false, hasSettings: true}
|
||||
card = new PackageCard {name: 'test-package'}, new SettingsView(), packageManager, {onSettingsView: true}
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.settingsButton).not.toBeVisible()
|
||||
|
||||
it "removes the settings button if a package has no settings", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false, hasSettings: false}
|
||||
card = new PackageCard {name: 'test-package'}, new SettingsView(), packageManager
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.settingsButton).not.toBeVisible()
|
||||
|
||||
it "removes the uninstall button if a package has is a bundled package", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false, hasSettings: true}
|
||||
card = new PackageCard {name: 'find-and-replace'}, new SettingsView(), packageManager
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.uninstallButton).not.toBeVisible()
|
||||
|
||||
it "displays the new version in the update button", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false, hasSettings: true}
|
||||
card = new PackageCard {name: 'find-and-replace', version: '1.0.0', latestVersion: '1.2.0'}, new SettingsView(), packageManager
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.updateButton).toBeVisible()
|
||||
expect(card.refs.updateButton.textContent).toContain 'Update to 1.2.0'
|
||||
|
||||
it "displays the new version in the update button when the package is disabled", ->
|
||||
setPackageStatusSpies {installed: true, disabled: true, hasSettings: true}
|
||||
card = new PackageCard {name: 'find-and-replace', version: '1.0.0', latestVersion: '1.2.0'}, new SettingsView(), packageManager
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.updateButton).toBeVisible()
|
||||
expect(card.refs.updateButton.textContent).toContain 'Update to 1.2.0'
|
||||
|
||||
it "shows the author details", ->
|
||||
authorName = "authorName"
|
||||
pack =
|
||||
name: 'some-package'
|
||||
version: '0.1.0'
|
||||
repository: "https://github.com/#{authorName}/some-package"
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
|
||||
jasmine.attachToDOM(card.element)
|
||||
|
||||
expect(card.refs.loginLink.textContent).toBe(authorName)
|
||||
|
||||
describe "when the package is not installed", ->
|
||||
it "shows the settings, uninstall, and disable buttons", ->
|
||||
pack =
|
||||
name: 'some-package'
|
||||
version: '0.1.0'
|
||||
repository: 'http://github.com/omgwow/some-package'
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
|
||||
jasmine.attachToDOM(card.element)
|
||||
|
||||
expect(card.refs.installButtonGroup).toBeVisible()
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible()
|
||||
expect(card.refs.packageActionButtonGroup).not.toBeVisible()
|
||||
|
||||
it "can be installed if currently not installed", ->
|
||||
setPackageStatusSpies {installed: false, disabled: false}
|
||||
spyOn(packageManager, 'install')
|
||||
|
||||
card = new PackageCard {name: 'test-package'}, new SettingsView(), packageManager
|
||||
expect(card.refs.installButton.style.display).not.toBe('none')
|
||||
expect(card.refs.uninstallButton.style.display).toBe('none')
|
||||
card.refs.installButton.click()
|
||||
expect(packageManager.install).toHaveBeenCalled()
|
||||
|
||||
it "can be installed if currently not installed and package latest release engine match atom version", ->
|
||||
spyOn(packageManager, 'install')
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake (packageName, callback) ->
|
||||
pack =
|
||||
name: packageName
|
||||
version: '0.1.0'
|
||||
engines:
|
||||
atom: '>0.50.0'
|
||||
|
||||
callback(null, pack)
|
||||
|
||||
setPackageStatusSpies {installed: false, disabled: false}
|
||||
|
||||
card = new PackageCard {
|
||||
name: 'test-package'
|
||||
version: '0.1.0'
|
||||
engines:
|
||||
atom: '>0.50.0'
|
||||
}, new SettingsView(), packageManager
|
||||
|
||||
# In that case there's no need to make a request to get all the versions
|
||||
expect(packageManager.loadCompatiblePackageVersion).not.toHaveBeenCalled()
|
||||
|
||||
expect(card.refs.installButton.style.display).not.toBe('none')
|
||||
expect(card.refs.uninstallButton.style.display).toBe('none')
|
||||
card.refs.installButton.click()
|
||||
expect(packageManager.install).toHaveBeenCalled()
|
||||
expect(packageManager.install.mostRecentCall.args[0]).toEqual({
|
||||
name: 'test-package'
|
||||
version: '0.1.0'
|
||||
engines:
|
||||
atom: '>0.50.0'
|
||||
})
|
||||
|
||||
it "can be installed with a previous version whose engine match the current atom version", ->
|
||||
spyOn(packageManager, 'install')
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake (packageName, callback) ->
|
||||
pack =
|
||||
name: packageName
|
||||
version: '0.0.1'
|
||||
engines:
|
||||
atom: '>0.50.0'
|
||||
|
||||
callback(null, pack)
|
||||
|
||||
setPackageStatusSpies {installed: false, disabled: false}
|
||||
|
||||
card = new PackageCard {
|
||||
name: 'test-package'
|
||||
version: '0.1.0'
|
||||
engines:
|
||||
atom: '>99.0.0'
|
||||
}, new SettingsView(), packageManager
|
||||
|
||||
expect(card.refs.installButton.style.display).not.toBe('none')
|
||||
expect(card.refs.uninstallButton.style.display).toBe('none')
|
||||
expect(card.refs.versionValue.textContent).toBe('0.0.1')
|
||||
expect(card.refs.versionValue).toHaveClass('text-warning')
|
||||
expect(card.refs.packageMessage).toHaveClass('text-warning')
|
||||
card.refs.installButton.click()
|
||||
expect(packageManager.install).toHaveBeenCalled()
|
||||
expect(packageManager.install.mostRecentCall.args[0]).toEqual({
|
||||
name: 'test-package'
|
||||
version: '0.0.1'
|
||||
engines:
|
||||
atom: '>0.50.0'
|
||||
})
|
||||
|
||||
it "can't be installed if there is no version compatible with the current atom version", ->
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake (packageName, callback) ->
|
||||
pack =
|
||||
name: packageName
|
||||
|
||||
callback(null, pack)
|
||||
|
||||
setPackageStatusSpies {installed: false, disabled: false}
|
||||
|
||||
pack =
|
||||
name: 'test-package'
|
||||
engines:
|
||||
atom: '>=99.0.0'
|
||||
card = new PackageCard(pack , new SettingsView(), packageManager)
|
||||
jasmine.attachToDOM(card.element)
|
||||
|
||||
expect(card.refs.installButtonGroup).not.toBeVisible()
|
||||
expect(card.refs.packageActionButtonGroup).not.toBeVisible()
|
||||
expect(card.refs.versionValue).toHaveClass('text-error')
|
||||
expect(card.refs.packageMessage).toHaveClass('text-error')
|
||||
|
||||
describe "when the package is installed", ->
|
||||
beforeEach ->
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-config'))
|
||||
waitsFor ->
|
||||
atom.packages.isPackageLoaded('package-with-config') is true
|
||||
|
||||
it "can be disabled if installed", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false}
|
||||
spyOn(atom.packages, 'disablePackage').andReturn(true)
|
||||
|
||||
card = new PackageCard {name: 'test-package'}, new SettingsView(), packageManager
|
||||
expect(card.refs.enablementButton.querySelector('.disable-text').textContent).toBe('Disable')
|
||||
card.refs.enablementButton.click()
|
||||
expect(atom.packages.disablePackage).toHaveBeenCalled()
|
||||
|
||||
it "can be updated", ->
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
pack.latestVersion = '1.1.0'
|
||||
packageUpdated = false
|
||||
|
||||
packageManager.on 'package-updated', -> packageUpdated = true
|
||||
packageManager.runCommand.andCallFake (args, callback) ->
|
||||
callback(0, '', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
originalLoadPackage = atom.packages.loadPackage
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake ->
|
||||
originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config'))
|
||||
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.updateButton).toBeVisible()
|
||||
|
||||
card.update()
|
||||
|
||||
waitsFor ->
|
||||
packageUpdated
|
||||
|
||||
runs ->
|
||||
expect(card.refs.updateButton).not.toBeVisible()
|
||||
|
||||
it 'keeps the update button visible if the update failed', ->
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
pack.latestVersion = '1.1.0'
|
||||
updateFailed = false
|
||||
|
||||
packageManager.on 'package-update-failed', -> updateFailed = true
|
||||
packageManager.runCommand.andCallFake (args, callback) ->
|
||||
callback(1, '', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
originalLoadPackage = atom.packages.loadPackage
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake ->
|
||||
originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config'))
|
||||
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.updateButton).toBeVisible()
|
||||
|
||||
card.update()
|
||||
|
||||
waitsFor ->
|
||||
updateFailed
|
||||
|
||||
runs ->
|
||||
expect(card.refs.updateButton).toBeVisible()
|
||||
|
||||
it 'does not error when attempting to update without any update available', ->
|
||||
# While this cannot be done through the package card UI,
|
||||
# updates can still be triggered through the Updates panel's Update All button
|
||||
# https://github.com/atom/settings-view/issues/879
|
||||
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
|
||||
originalLoadPackage = atom.packages.loadPackage
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake ->
|
||||
originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config'))
|
||||
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
jasmine.attachToDOM(card.element)
|
||||
expect(card.refs.updateButton).not.toBeVisible()
|
||||
|
||||
waitsForPromise -> card.update()
|
||||
|
||||
runs ->
|
||||
expect(card.refs.updateButton).not.toBeVisible()
|
||||
|
||||
it "will stay disabled after an update", ->
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
pack.latestVersion = '1.1.0'
|
||||
packageUpdated = false
|
||||
|
||||
packageManager.on 'package-updated', -> packageUpdated = true
|
||||
packageManager.runCommand.andCallFake (args, callback) ->
|
||||
callback(0, '', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
originalLoadPackage = atom.packages.loadPackage
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake ->
|
||||
originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config'))
|
||||
|
||||
pack.disable()
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
expect(atom.packages.isPackageDisabled('package-with-config')).toBe true
|
||||
card.update()
|
||||
|
||||
waitsFor ->
|
||||
packageUpdated
|
||||
|
||||
runs ->
|
||||
expect(atom.packages.isPackageDisabled('package-with-config')).toBe true
|
||||
|
||||
it "is uninstalled when the uninstallButton is clicked", ->
|
||||
setPackageStatusSpies {installed: true, disabled: false}
|
||||
|
||||
[uninstallCallback] = []
|
||||
packageManager.runCommand.andCallFake (args, callback) ->
|
||||
if args[0] is 'uninstall'
|
||||
uninstallCallback = callback
|
||||
onWillThrowError: ->
|
||||
|
||||
spyOn(packageManager, 'install').andCallThrough()
|
||||
spyOn(packageManager, 'uninstall').andCallThrough()
|
||||
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
jasmine.attachToDOM(card.element)
|
||||
|
||||
expect(card.refs.uninstallButton).toBeVisible()
|
||||
expect(card.refs.enablementButton).toBeVisible()
|
||||
card.refs.uninstallButton.click()
|
||||
|
||||
expect(card.refs.uninstallButton.disabled).toBe true
|
||||
expect(card.refs.enablementButton.disabled).toBe true
|
||||
expect(card.refs.uninstallButton).toHaveClass('is-uninstalling')
|
||||
|
||||
expect(packageManager.uninstall).toHaveBeenCalled()
|
||||
expect(packageManager.uninstall.mostRecentCall.args[0].name).toEqual('package-with-config')
|
||||
|
||||
jasmine.unspy(PackageCard::, 'isInstalled')
|
||||
spyOn(PackageCard.prototype, 'isInstalled').andReturn false
|
||||
uninstallCallback(0, '', '')
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(card.refs.uninstallButton.disabled).toBe false
|
||||
expect(card.refs.uninstallButton).not.toHaveClass('is-uninstalling')
|
||||
expect(card.refs.installButtonGroup).toBeVisible()
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible()
|
||||
expect(card.refs.packageActionButtonGroup).not.toBeVisible()
|
||||
|
||||
it "shows the settings, uninstall, and enable buttons when disabled", ->
|
||||
atom.config.set('package-with-config.setting', 'something')
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
spyOn(atom.packages, 'isPackageDisabled').andReturn(true)
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
jasmine.attachToDOM(card.element)
|
||||
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible()
|
||||
expect(card.refs.installButtonGroup).not.toBeVisible()
|
||||
|
||||
expect(card.refs.settingsButton).toBeVisible()
|
||||
expect(card.refs.uninstallButton).toBeVisible()
|
||||
expect(card.refs.enablementButton).toBeVisible()
|
||||
expect(card.refs.enablementButton.textContent).toBe 'Enable'
|
||||
|
||||
it "shows the settings, uninstall, and disable buttons", ->
|
||||
atom.config.set('package-with-config.setting', 'something')
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
|
||||
jasmine.attachToDOM(card.element)
|
||||
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible()
|
||||
expect(card.refs.installButtonGroup).not.toBeVisible()
|
||||
|
||||
expect(card.refs.settingsButton).toBeVisible()
|
||||
expect(card.refs.uninstallButton).toBeVisible()
|
||||
expect(card.refs.enablementButton).toBeVisible()
|
||||
expect(card.refs.enablementButton.textContent).toBe 'Disable'
|
||||
|
||||
it "does not show the settings button when there are no settings", ->
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
spyOn(PackageCard::, 'hasSettings').andReturn(false)
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager)
|
||||
|
||||
jasmine.attachToDOM(card.element)
|
||||
|
||||
expect(card.refs.settingsButton).not.toBeVisible()
|
||||
expect(card.refs.uninstallButton).toBeVisible()
|
||||
expect(card.refs.enablementButton).toBeVisible()
|
||||
expect(card.refs.enablementButton.textContent).toBe 'Disable'
|
423
packages/settings-view/spec/package-card-spec.js
Normal file
423
packages/settings-view/spec/package-card-spec.js
Normal file
@ -0,0 +1,423 @@
|
||||
|
||||
const path = require('path');
|
||||
const PackageCard = require('../lib/package-card');
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const SettingsView = require('../lib/settings-view');
|
||||
|
||||
describe("PackageCard", function() {
|
||||
const setPackageStatusSpies = function(opts) {
|
||||
spyOn(PackageCard.prototype, 'isInstalled').andReturn(opts.installed);
|
||||
spyOn(PackageCard.prototype, 'isDisabled').andReturn(opts.disabled);
|
||||
spyOn(PackageCard.prototype, 'hasSettings').andReturn(opts.hasSettings);
|
||||
};
|
||||
|
||||
let [card, packageManager] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
packageManager = new PackageManager();
|
||||
spyOn(packageManager, 'runCommand');
|
||||
});
|
||||
|
||||
it("doesn't show the disable control for a theme", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false});
|
||||
card = new PackageCard({theme: 'syntax', name: 'test-theme'}, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.enablementButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("doesn't show the status indicator for a theme", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false});
|
||||
card = new PackageCard({theme: 'syntax', name: 'test-theme'}, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.statusIndicatorButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("doesn't show the settings button for a theme", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false});
|
||||
card = new PackageCard({theme: 'syntax', name: 'test-theme'}, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.settingsButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("doesn't show the settings button on the settings view", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false, hasSettings: true});
|
||||
card = new PackageCard({name: 'test-package'}, new SettingsView(), packageManager, {onSettingsView: true});
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.settingsButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("removes the settings button if a package has no settings", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false, hasSettings: false});
|
||||
card = new PackageCard({name: 'test-package'}, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.settingsButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("removes the uninstall button if a package has is a bundled package", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false, hasSettings: true});
|
||||
card = new PackageCard({name: 'find-and-replace'}, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.uninstallButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("displays the new version in the update button", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false, hasSettings: true});
|
||||
card = new PackageCard({name: 'find-and-replace', version: '1.0.0', latestVersion: '1.2.0'}, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.updateButton).toBeVisible();
|
||||
expect(card.refs.updateButton.textContent).toContain('Update to 1.2.0');
|
||||
});
|
||||
|
||||
it("displays the new version in the update button when the package is disabled", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: true, hasSettings: true});
|
||||
card = new PackageCard({name: 'find-and-replace', version: '1.0.0', latestVersion: '1.2.0'}, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.updateButton).toBeVisible();
|
||||
expect(card.refs.updateButton.textContent).toContain('Update to 1.2.0');
|
||||
});
|
||||
|
||||
it("shows the author details", function() {
|
||||
const authorName = "authorName";
|
||||
const pack = {
|
||||
name: 'some-package',
|
||||
version: '0.1.0',
|
||||
repository: `https://github.com/${authorName}/some-package`
|
||||
};
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
|
||||
jasmine.attachToDOM(card.element);
|
||||
|
||||
expect(card.refs.loginLink.textContent).toBe(authorName);
|
||||
});
|
||||
|
||||
describe("when the package is not installed", function() {
|
||||
it("shows the settings, uninstall, and disable buttons", function() {
|
||||
const pack = {
|
||||
name: 'some-package',
|
||||
version: '0.1.0',
|
||||
repository: 'http://github.com/omgwow/some-package'
|
||||
};
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
|
||||
jasmine.attachToDOM(card.element);
|
||||
|
||||
expect(card.refs.installButtonGroup).toBeVisible();
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible();
|
||||
expect(card.refs.packageActionButtonGroup).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("can be installed if currently not installed", function() {
|
||||
setPackageStatusSpies({installed: false, disabled: false});
|
||||
spyOn(packageManager, 'install');
|
||||
|
||||
card = new PackageCard({name: 'test-package'}, new SettingsView(), packageManager);
|
||||
expect(card.refs.installButton.style.display).not.toBe('none');
|
||||
expect(card.refs.uninstallButton.style.display).toBe('none');
|
||||
card.refs.installButton.click();
|
||||
expect(packageManager.install).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("can be installed if currently not installed and package latest release engine match atom version", function() {
|
||||
spyOn(packageManager, 'install');
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake(function(packageName, callback) {
|
||||
const pack = {
|
||||
name: packageName,
|
||||
version: '0.1.0',
|
||||
engines: {
|
||||
atom: '>0.50.0'
|
||||
}
|
||||
};
|
||||
|
||||
return callback(null, pack);
|
||||
});
|
||||
|
||||
setPackageStatusSpies({installed: false, disabled: false});
|
||||
|
||||
card = new PackageCard({
|
||||
name: 'test-package',
|
||||
version: '0.1.0',
|
||||
engines: {
|
||||
atom: '>0.50.0'
|
||||
}
|
||||
}, new SettingsView(), packageManager);
|
||||
|
||||
// In that case there's no need to make a request to get all the versions
|
||||
expect(packageManager.loadCompatiblePackageVersion).not.toHaveBeenCalled();
|
||||
|
||||
expect(card.refs.installButton.style.display).not.toBe('none');
|
||||
expect(card.refs.uninstallButton.style.display).toBe('none');
|
||||
card.refs.installButton.click();
|
||||
expect(packageManager.install).toHaveBeenCalled();
|
||||
expect(packageManager.install.mostRecentCall.args[0]).toEqual({
|
||||
name: 'test-package',
|
||||
version: '0.1.0',
|
||||
engines: {
|
||||
atom: '>0.50.0'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can be installed with a previous version whose engine match the current atom version", function() {
|
||||
spyOn(packageManager, 'install');
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake(function(packageName, callback) {
|
||||
const pack = {
|
||||
name: packageName,
|
||||
version: '0.0.1',
|
||||
engines: {
|
||||
atom: '>0.50.0'
|
||||
}
|
||||
};
|
||||
|
||||
return callback(null, pack);
|
||||
});
|
||||
|
||||
setPackageStatusSpies({installed: false, disabled: false});
|
||||
|
||||
card = new PackageCard({
|
||||
name: 'test-package',
|
||||
version: '0.1.0',
|
||||
engines: {
|
||||
atom: '>99.0.0'
|
||||
}
|
||||
}, new SettingsView(), packageManager);
|
||||
|
||||
expect(card.refs.installButton.style.display).not.toBe('none');
|
||||
expect(card.refs.uninstallButton.style.display).toBe('none');
|
||||
expect(card.refs.versionValue.textContent).toBe('0.0.1');
|
||||
expect(card.refs.versionValue).toHaveClass('text-warning');
|
||||
expect(card.refs.packageMessage).toHaveClass('text-warning');
|
||||
card.refs.installButton.click();
|
||||
expect(packageManager.install).toHaveBeenCalled();
|
||||
expect(packageManager.install.mostRecentCall.args[0]).toEqual({
|
||||
name: 'test-package',
|
||||
version: '0.0.1',
|
||||
engines: {
|
||||
atom: '>0.50.0'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can't be installed if there is no version compatible with the current atom version", function() {
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake(function(packageName, callback) {
|
||||
const pack =
|
||||
{name: packageName};
|
||||
|
||||
return callback(null, pack);
|
||||
});
|
||||
|
||||
setPackageStatusSpies({installed: false, disabled: false});
|
||||
|
||||
const pack = {
|
||||
name: 'test-package',
|
||||
engines: {
|
||||
atom: '>=99.0.0'
|
||||
}
|
||||
};
|
||||
card = new PackageCard(pack , new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
|
||||
expect(card.refs.installButtonGroup).not.toBeVisible();
|
||||
expect(card.refs.packageActionButtonGroup).not.toBeVisible();
|
||||
expect(card.refs.versionValue).toHaveClass('text-error');
|
||||
expect(card.refs.packageMessage).toHaveClass('text-error');
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the package is installed", function() {
|
||||
beforeEach(function() {
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-config'));
|
||||
return waitsFor(() => atom.packages.isPackageLoaded('package-with-config') === true);
|
||||
});
|
||||
|
||||
it("can be disabled if installed", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false});
|
||||
spyOn(atom.packages, 'disablePackage').andReturn(true);
|
||||
|
||||
card = new PackageCard({name: 'test-package'}, new SettingsView(), packageManager);
|
||||
expect(card.refs.enablementButton.querySelector('.disable-text').textContent).toBe('Disable');
|
||||
card.refs.enablementButton.click();
|
||||
expect(atom.packages.disablePackage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("can be updated", function() {
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
pack.latestVersion = '1.1.0';
|
||||
let packageUpdated = false;
|
||||
|
||||
packageManager.on('package-updated', () => packageUpdated = true);
|
||||
packageManager.runCommand.andCallFake(function(args, callback) {
|
||||
callback(0, '', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
const originalLoadPackage = atom.packages.loadPackage;
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake(() => originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config')));
|
||||
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.updateButton).toBeVisible();
|
||||
|
||||
card.update();
|
||||
|
||||
waitsFor(() => packageUpdated);
|
||||
|
||||
runs(() => expect(card.refs.updateButton).not.toBeVisible());
|
||||
});
|
||||
|
||||
it('keeps the update button visible if the update failed', function() {
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
pack.latestVersion = '1.1.0';
|
||||
let updateFailed = false;
|
||||
|
||||
packageManager.on('package-update-failed', () => updateFailed = true);
|
||||
packageManager.runCommand.andCallFake(function(args, callback) {
|
||||
callback(1, '', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
const originalLoadPackage = atom.packages.loadPackage;
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake(() => originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config')));
|
||||
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.updateButton).toBeVisible();
|
||||
|
||||
card.update();
|
||||
|
||||
waitsFor(() => updateFailed);
|
||||
|
||||
runs(() => expect(card.refs.updateButton).toBeVisible());
|
||||
});
|
||||
|
||||
it('does not error when attempting to update without any update available', function() {
|
||||
// While this cannot be done through the package card UI,
|
||||
// updates can still be triggered through the Updates panel's Update All button
|
||||
// https://github.com/atom/settings-view/issues/879
|
||||
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
|
||||
const originalLoadPackage = atom.packages.loadPackage;
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake(() => originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config')));
|
||||
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
expect(card.refs.updateButton).not.toBeVisible();
|
||||
|
||||
waitsForPromise(() => card.update());
|
||||
|
||||
runs(() => expect(card.refs.updateButton).not.toBeVisible());
|
||||
});
|
||||
|
||||
it("will stay disabled after an update", function() {
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
pack.latestVersion = '1.1.0';
|
||||
let packageUpdated = false;
|
||||
|
||||
packageManager.on('package-updated', () => packageUpdated = true);
|
||||
packageManager.runCommand.andCallFake(function(args, callback) {
|
||||
callback(0, '', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
const originalLoadPackage = atom.packages.loadPackage;
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake(() => originalLoadPackage.call(atom.packages, path.join(__dirname, 'fixtures', 'package-with-config')));
|
||||
|
||||
pack.disable();
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
expect(atom.packages.isPackageDisabled('package-with-config')).toBe(true);
|
||||
card.update();
|
||||
|
||||
waitsFor(() => packageUpdated);
|
||||
|
||||
runs(() => expect(atom.packages.isPackageDisabled('package-with-config')).toBe(true));
|
||||
});
|
||||
|
||||
it("is uninstalled when the uninstallButton is clicked", function() {
|
||||
setPackageStatusSpies({installed: true, disabled: false});
|
||||
|
||||
let [uninstallCallback] = [];
|
||||
packageManager.runCommand.andCallFake(function(args, callback) {
|
||||
if (args[0] === 'uninstall') {
|
||||
uninstallCallback = callback;
|
||||
}
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
spyOn(packageManager, 'install').andCallThrough();
|
||||
spyOn(packageManager, 'uninstall').andCallThrough();
|
||||
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
|
||||
expect(card.refs.uninstallButton).toBeVisible();
|
||||
expect(card.refs.enablementButton).toBeVisible();
|
||||
card.refs.uninstallButton.click();
|
||||
|
||||
expect(card.refs.uninstallButton.disabled).toBe(true);
|
||||
expect(card.refs.enablementButton.disabled).toBe(true);
|
||||
expect(card.refs.uninstallButton).toHaveClass('is-uninstalling');
|
||||
|
||||
expect(packageManager.uninstall).toHaveBeenCalled();
|
||||
expect(packageManager.uninstall.mostRecentCall.args[0].name).toEqual('package-with-config');
|
||||
|
||||
jasmine.unspy(PackageCard.prototype, 'isInstalled');
|
||||
spyOn(PackageCard.prototype, 'isInstalled').andReturn(false);
|
||||
uninstallCallback(0, '', '');
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(card.refs.uninstallButton.disabled).toBe(false);
|
||||
expect(card.refs.uninstallButton).not.toHaveClass('is-uninstalling');
|
||||
expect(card.refs.installButtonGroup).toBeVisible();
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible();
|
||||
expect(card.refs.packageActionButtonGroup).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows the settings, uninstall, and enable buttons when disabled", function() {
|
||||
atom.config.set('package-with-config.setting', 'something');
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
spyOn(atom.packages, 'isPackageDisabled').andReturn(true);
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
jasmine.attachToDOM(card.element);
|
||||
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible();
|
||||
expect(card.refs.installButtonGroup).not.toBeVisible();
|
||||
|
||||
expect(card.refs.settingsButton).toBeVisible();
|
||||
expect(card.refs.uninstallButton).toBeVisible();
|
||||
expect(card.refs.enablementButton).toBeVisible();
|
||||
expect(card.refs.enablementButton.textContent).toBe('Enable');
|
||||
});
|
||||
|
||||
it("shows the settings, uninstall, and disable buttons", function() {
|
||||
atom.config.set('package-with-config.setting', 'something');
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
|
||||
jasmine.attachToDOM(card.element);
|
||||
|
||||
expect(card.refs.updateButtonGroup).not.toBeVisible();
|
||||
expect(card.refs.installButtonGroup).not.toBeVisible();
|
||||
|
||||
expect(card.refs.settingsButton).toBeVisible();
|
||||
expect(card.refs.uninstallButton).toBeVisible();
|
||||
expect(card.refs.enablementButton).toBeVisible();
|
||||
expect(card.refs.enablementButton.textContent).toBe('Disable');
|
||||
});
|
||||
|
||||
it("does not show the settings button when there are no settings", function() {
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
spyOn(PackageCard.prototype, 'hasSettings').andReturn(false);
|
||||
card = new PackageCard(pack, new SettingsView(), packageManager);
|
||||
|
||||
jasmine.attachToDOM(card.element);
|
||||
|
||||
expect(card.refs.settingsButton).not.toBeVisible();
|
||||
expect(card.refs.uninstallButton).toBeVisible();
|
||||
expect(card.refs.enablementButton).toBeVisible();
|
||||
expect(card.refs.enablementButton.textContent).toBe('Disable');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,155 +0,0 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
{shell} = require 'electron'
|
||||
|
||||
PackageDetailView = require '../lib/package-detail-view'
|
||||
PackageManager = require '../lib/package-manager'
|
||||
SettingsView = require '../lib/settings-view'
|
||||
AtomIoClient = require '../lib/atom-io-client'
|
||||
SnippetsProvider =
|
||||
getSnippets: -> {}
|
||||
|
||||
describe "PackageDetailView", ->
|
||||
packageManager = null
|
||||
view = null
|
||||
|
||||
createClientSpy = ->
|
||||
jasmine.createSpyObj('client', ['package', 'avatar'])
|
||||
|
||||
beforeEach ->
|
||||
packageManager = new PackageManager
|
||||
view = null
|
||||
|
||||
loadPackageFromRemote = (packageName, opts) ->
|
||||
opts ?= {}
|
||||
packageManager.client = createClientSpy()
|
||||
packageManager.client.package.andCallFake (name, cb) ->
|
||||
packageData = require(path.join(__dirname, 'fixtures', packageName, 'package.json'))
|
||||
packageData.readme = fs.readFileSync(path.join(__dirname, 'fixtures', packageName, 'README.md'), 'utf8')
|
||||
cb(null, packageData)
|
||||
view = new PackageDetailView({name: packageName}, new SettingsView(), packageManager, SnippetsProvider)
|
||||
view.beforeShow(opts)
|
||||
|
||||
loadCustomPackageFromRemote = (packageName, opts) ->
|
||||
opts ?= {}
|
||||
packageManager.client = createClientSpy()
|
||||
packageManager.client.package.andCallFake (name, cb) ->
|
||||
packageData = require(path.join(__dirname, 'fixtures', packageName, 'package.json'))
|
||||
cb(null, packageData)
|
||||
view = new PackageDetailView({name: packageName}, new SettingsView(), packageManager, SnippetsProvider)
|
||||
view.beforeShow(opts)
|
||||
|
||||
it "renders a package when provided in `initialize`", ->
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-config'))
|
||||
pack = atom.packages.getLoadedPackage('package-with-config')
|
||||
view = new PackageDetailView(pack, new SettingsView(), packageManager, SnippetsProvider)
|
||||
|
||||
# Perhaps there are more things to assert here.
|
||||
expect(view.refs.title.textContent).toBe('Package With Config')
|
||||
|
||||
it "does not call the atom.io api for package metadata when present", ->
|
||||
packageManager.client = createClientSpy()
|
||||
view = new PackageDetailView({name: 'package-with-config'}, new SettingsView(), packageManager, SnippetsProvider)
|
||||
|
||||
# PackageCard is a subview, and it calls AtomIoClient::package once to load
|
||||
# metadata from the cache.
|
||||
expect(packageManager.client.package.callCount).toBe(1)
|
||||
|
||||
it "shows a loading message and calls out to atom.io when package metadata is missing", ->
|
||||
loadPackageFromRemote('package-with-readme')
|
||||
expect(view.refs.loadingMessage).not.toBe(null)
|
||||
expect(view.refs.loadingMessage.classList.contains('hidden')).not.toBe(true)
|
||||
expect(packageManager.client.package).toHaveBeenCalled()
|
||||
|
||||
it "shows an error when package metadata cannot be loaded via the API", ->
|
||||
packageManager.client = createClientSpy()
|
||||
packageManager.client.package.andCallFake (name, cb) ->
|
||||
error = new Error('API error')
|
||||
cb(error, null)
|
||||
|
||||
view = new PackageDetailView({name: 'nonexistent-package'}, new SettingsView(), packageManager, SnippetsProvider)
|
||||
|
||||
expect(view.refs.errorMessage.classList.contains('hidden')).not.toBe(true)
|
||||
expect(view.refs.loadingMessage.classList.contains('hidden')).toBe(true)
|
||||
expect(view.element.querySelectorAll('.package-card').length).toBe(0)
|
||||
|
||||
it "shows an error when package metadata cannot be loaded from the cache and the network is unavailable", ->
|
||||
localStorage.removeItem('settings-view:packages/some-package')
|
||||
|
||||
spyOn(AtomIoClient.prototype, 'online').andReturn(false)
|
||||
spyOn(AtomIoClient.prototype, 'request').andCallFake (path, callback) ->
|
||||
callback(new Error('getaddrinfo ENOENT atom.io:443'))
|
||||
spyOn(AtomIoClient.prototype, 'fetchFromCache').andCallThrough()
|
||||
|
||||
view = new PackageDetailView({name: 'some-package'}, new SettingsView(), packageManager, SnippetsProvider)
|
||||
|
||||
expect(AtomIoClient.prototype.fetchFromCache).toHaveBeenCalled()
|
||||
|
||||
expect(view.refs.errorMessage.classList.contains('hidden')).not.toBe(true)
|
||||
expect(view.refs.loadingMessage.classList.contains('hidden')).toBe(true)
|
||||
expect(view.element.querySelectorAll('.package-card').length).toBe(0)
|
||||
|
||||
it "renders the README successfully after a call to the atom.io api", ->
|
||||
loadPackageFromRemote('package-with-readme')
|
||||
expect(view.packageCard).toBeDefined()
|
||||
expect(view.packageCard.refs.packageName.textContent).toBe('package-with-readme')
|
||||
expect(view.element.querySelectorAll('.package-readme').length).toBe(1)
|
||||
|
||||
it "renders the README successfully with sanitized html", ->
|
||||
loadPackageFromRemote('package-with-readme')
|
||||
expect(view.element.querySelectorAll('.package-readme script').length).toBe(0)
|
||||
expect(view.element.querySelectorAll('.package-readme iframe').length).toBe(0)
|
||||
expect(view.element.querySelectorAll('.package-readme input[type="checkbox"][disabled]').length).toBe(2)
|
||||
expect(view.element.querySelector('img[alt="AbsoluteImage"]').getAttribute('src')).toBe('https://example.com/static/image.jpg')
|
||||
expect(view.element.querySelector('img[alt="RelativeImage"]').getAttribute('src')).toBe('https://github.com/example/package-with-readme/blob/master/static/image.jpg')
|
||||
expect(view.element.querySelector('img[alt="Base64Image"]').getAttribute('src')).toBe('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==')
|
||||
|
||||
it "renders the README when the package path is undefined", ->
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-readme'))
|
||||
pack = atom.packages.getLoadedPackage('package-with-readme')
|
||||
delete pack.path
|
||||
view = new PackageDetailView(pack, new SettingsView(), packageManager, SnippetsProvider)
|
||||
|
||||
expect(view.packageCard).toBeDefined()
|
||||
expect(view.packageCard.refs.packageName.textContent).toBe('package-with-readme')
|
||||
expect(view.element.querySelectorAll('.package-readme').length).toBe(1)
|
||||
|
||||
it "triggers a report issue button click and checks that the fallback repository issue tracker URL was opened", ->
|
||||
loadCustomPackageFromRemote('package-without-bugs-property')
|
||||
spyOn(shell, 'openExternal')
|
||||
view.refs.issueButton.click()
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com/example/package-without-bugs-property/issues/new')
|
||||
|
||||
it "triggers a report issue button click and checks that the bugs URL string was opened", ->
|
||||
loadCustomPackageFromRemote('package-with-bugs-property-url-string')
|
||||
spyOn(shell, 'openExternal')
|
||||
view.refs.issueButton.click()
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://example.com/custom-issue-tracker/new')
|
||||
|
||||
it "triggers a report issue button click and checks that the bugs URL was opened", ->
|
||||
loadCustomPackageFromRemote('package-with-bugs-property-url')
|
||||
spyOn(shell, 'openExternal')
|
||||
view.refs.issueButton.click()
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://example.com/custom-issue-tracker/new')
|
||||
|
||||
it "triggers a report issue button click and checks that the bugs email link was opened", ->
|
||||
loadCustomPackageFromRemote('package-with-bugs-property-email')
|
||||
spyOn(shell, 'openExternal')
|
||||
view.refs.issueButton.click()
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('mailto:issues@example.com')
|
||||
|
||||
it "should show 'Install' as the first breadcrumb by default", ->
|
||||
loadPackageFromRemote('package-with-readme')
|
||||
expect(view.refs.breadcrumb.textContent).toBe('Install')
|
||||
|
||||
it "should open repository url", ->
|
||||
loadPackageFromRemote('package-with-readme')
|
||||
spyOn(shell, 'openExternal')
|
||||
view.refs.packageRepo.click()
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com/example/package-with-readme')
|
||||
|
||||
it "should open internal package repository url", ->
|
||||
loadPackageFromRemote('package-internal')
|
||||
spyOn(shell, 'openExternal')
|
||||
view.refs.packageRepo.click()
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com/pulsar-edit/pulsar/tree/master/packages/package-internal')
|
176
packages/settings-view/spec/package-detail-view-spec.js
Normal file
176
packages/settings-view/spec/package-detail-view-spec.js
Normal file
@ -0,0 +1,176 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {shell} = require('electron');
|
||||
|
||||
const PackageDetailView = require('../lib/package-detail-view');
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const SettingsView = require('../lib/settings-view');
|
||||
const AtomIoClient = require('../lib/atom-io-client');
|
||||
const SnippetsProvider =
|
||||
{getSnippets() { return {}; }};
|
||||
|
||||
describe("PackageDetailView", function() {
|
||||
let packageManager = null;
|
||||
let view = null;
|
||||
|
||||
const createClientSpy = () => jasmine.createSpyObj('client', ['package', 'avatar']);
|
||||
|
||||
beforeEach(function() {
|
||||
packageManager = new PackageManager;
|
||||
view = null;
|
||||
});
|
||||
|
||||
const loadPackageFromRemote = function(packageName, opts) {
|
||||
if (opts == null) { opts = {}; }
|
||||
packageManager.client = createClientSpy();
|
||||
packageManager.client.package.andCallFake(function(name, cb) {
|
||||
const packageData = require(path.join(__dirname, 'fixtures', packageName, 'package.json'));
|
||||
packageData.readme = fs.readFileSync(path.join(__dirname, 'fixtures', packageName, 'README.md'), 'utf8');
|
||||
return cb(null, packageData);
|
||||
});
|
||||
view = new PackageDetailView({name: packageName}, new SettingsView(), packageManager, SnippetsProvider);
|
||||
return view.beforeShow(opts);
|
||||
};
|
||||
|
||||
const loadCustomPackageFromRemote = function(packageName, opts) {
|
||||
if (opts == null) { opts = {}; }
|
||||
packageManager.client = createClientSpy();
|
||||
packageManager.client.package.andCallFake(function(name, cb) {
|
||||
const packageData = require(path.join(__dirname, 'fixtures', packageName, 'package.json'));
|
||||
return cb(null, packageData);
|
||||
});
|
||||
view = new PackageDetailView({name: packageName}, new SettingsView(), packageManager, SnippetsProvider);
|
||||
return view.beforeShow(opts);
|
||||
};
|
||||
|
||||
it("renders a package when provided in `initialize`", function() {
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-config'));
|
||||
const pack = atom.packages.getLoadedPackage('package-with-config');
|
||||
view = new PackageDetailView(pack, new SettingsView(), packageManager, SnippetsProvider);
|
||||
|
||||
// Perhaps there are more things to assert here.
|
||||
expect(view.refs.title.textContent).toBe('Package With Config');
|
||||
});
|
||||
|
||||
it("does not call the atom.io api for package metadata when present", function() {
|
||||
packageManager.client = createClientSpy();
|
||||
view = new PackageDetailView({name: 'package-with-config'}, new SettingsView(), packageManager, SnippetsProvider);
|
||||
|
||||
// PackageCard is a subview, and it calls AtomIoClient::package once to load
|
||||
// metadata from the cache.
|
||||
expect(packageManager.client.package.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it("shows a loading message and calls out to atom.io when package metadata is missing", function() {
|
||||
loadPackageFromRemote('package-with-readme');
|
||||
expect(view.refs.loadingMessage).not.toBe(null);
|
||||
expect(view.refs.loadingMessage.classList.contains('hidden')).not.toBe(true);
|
||||
expect(packageManager.client.package).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows an error when package metadata cannot be loaded via the API", function() {
|
||||
packageManager.client = createClientSpy();
|
||||
packageManager.client.package.andCallFake(function(name, cb) {
|
||||
const error = new Error('API error');
|
||||
return cb(error, null);
|
||||
});
|
||||
|
||||
view = new PackageDetailView({name: 'nonexistent-package'}, new SettingsView(), packageManager, SnippetsProvider);
|
||||
|
||||
expect(view.refs.errorMessage.classList.contains('hidden')).not.toBe(true);
|
||||
expect(view.refs.loadingMessage.classList.contains('hidden')).toBe(true);
|
||||
expect(view.element.querySelectorAll('.package-card').length).toBe(0);
|
||||
});
|
||||
|
||||
it("shows an error when package metadata cannot be loaded from the cache and the network is unavailable", function() {
|
||||
localStorage.removeItem('settings-view:packages/some-package');
|
||||
|
||||
spyOn(AtomIoClient.prototype, 'online').andReturn(false);
|
||||
spyOn(AtomIoClient.prototype, 'request').andCallFake((path, callback) => callback(new Error('getaddrinfo ENOENT atom.io:443')));
|
||||
spyOn(AtomIoClient.prototype, 'fetchFromCache').andCallThrough();
|
||||
|
||||
view = new PackageDetailView({name: 'some-package'}, new SettingsView(), packageManager, SnippetsProvider);
|
||||
|
||||
expect(AtomIoClient.prototype.fetchFromCache).toHaveBeenCalled();
|
||||
|
||||
expect(view.refs.errorMessage.classList.contains('hidden')).not.toBe(true);
|
||||
expect(view.refs.loadingMessage.classList.contains('hidden')).toBe(true);
|
||||
expect(view.element.querySelectorAll('.package-card').length).toBe(0);
|
||||
});
|
||||
|
||||
it("renders the README successfully after a call to the atom.io api", function() {
|
||||
loadPackageFromRemote('package-with-readme');
|
||||
expect(view.packageCard).toBeDefined();
|
||||
expect(view.packageCard.refs.packageName.textContent).toBe('package-with-readme');
|
||||
expect(view.element.querySelectorAll('.package-readme').length).toBe(1);
|
||||
});
|
||||
|
||||
it("renders the README successfully with sanitized html", function() {
|
||||
loadPackageFromRemote('package-with-readme');
|
||||
expect(view.element.querySelectorAll('.package-readme script').length).toBe(0);
|
||||
expect(view.element.querySelectorAll('.package-readme iframe').length).toBe(0);
|
||||
expect(view.element.querySelectorAll('.package-readme input[type="checkbox"][disabled]').length).toBe(2);
|
||||
expect(view.element.querySelector('img[alt="AbsoluteImage"]').getAttribute('src')).toBe('https://example.com/static/image.jpg');
|
||||
expect(view.element.querySelector('img[alt="RelativeImage"]').getAttribute('src')).toBe('https://github.com/example/package-with-readme/blob/master/static/image.jpg');
|
||||
expect(view.element.querySelector('img[alt="Base64Image"]').getAttribute('src')).toBe('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
|
||||
});
|
||||
|
||||
it("renders the README when the package path is undefined", function() {
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-readme'));
|
||||
const pack = atom.packages.getLoadedPackage('package-with-readme');
|
||||
delete pack.path;
|
||||
view = new PackageDetailView(pack, new SettingsView(), packageManager, SnippetsProvider);
|
||||
|
||||
expect(view.packageCard).toBeDefined();
|
||||
expect(view.packageCard.refs.packageName.textContent).toBe('package-with-readme');
|
||||
expect(view.element.querySelectorAll('.package-readme').length).toBe(1);
|
||||
});
|
||||
|
||||
it("triggers a report issue button click and checks that the fallback repository issue tracker URL was opened", function() {
|
||||
loadCustomPackageFromRemote('package-without-bugs-property');
|
||||
spyOn(shell, 'openExternal');
|
||||
view.refs.issueButton.click();
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com/example/package-without-bugs-property/issues/new');
|
||||
});
|
||||
|
||||
it("triggers a report issue button click and checks that the bugs URL string was opened", function() {
|
||||
loadCustomPackageFromRemote('package-with-bugs-property-url-string');
|
||||
spyOn(shell, 'openExternal');
|
||||
view.refs.issueButton.click();
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://example.com/custom-issue-tracker/new');
|
||||
});
|
||||
|
||||
it("triggers a report issue button click and checks that the bugs URL was opened", function() {
|
||||
loadCustomPackageFromRemote('package-with-bugs-property-url');
|
||||
spyOn(shell, 'openExternal');
|
||||
view.refs.issueButton.click();
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://example.com/custom-issue-tracker/new');
|
||||
});
|
||||
|
||||
it("triggers a report issue button click and checks that the bugs email link was opened", function() {
|
||||
loadCustomPackageFromRemote('package-with-bugs-property-email');
|
||||
spyOn(shell, 'openExternal');
|
||||
view.refs.issueButton.click();
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('mailto:issues@example.com');
|
||||
});
|
||||
|
||||
it("should show 'Install' as the first breadcrumb by default", function() {
|
||||
loadPackageFromRemote('package-with-readme');
|
||||
expect(view.refs.breadcrumb.textContent).toBe('Install');
|
||||
});
|
||||
|
||||
it("should open repository url", function() {
|
||||
loadPackageFromRemote('package-with-readme');
|
||||
spyOn(shell, 'openExternal');
|
||||
view.refs.packageRepo.click();
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com/example/package-with-readme');
|
||||
});
|
||||
|
||||
it("should open internal package repository url", function() {
|
||||
loadPackageFromRemote('package-internal');
|
||||
spyOn(shell, 'openExternal');
|
||||
view.refs.packageRepo.click();
|
||||
expect(shell.openExternal).toHaveBeenCalledWith('https://github.com/pulsar-edit/pulsar/tree/master/packages/package-internal');
|
||||
});
|
||||
});
|
@ -1,305 +0,0 @@
|
||||
path = require 'path'
|
||||
process = require 'process'
|
||||
PackageManager = require '../lib/package-manager'
|
||||
|
||||
describe "PackageManager", ->
|
||||
[packageManager] = []
|
||||
|
||||
beforeEach ->
|
||||
spyOn(atom.packages, 'getApmPath').andReturn('/an/invalid/apm/command/to/run')
|
||||
atom.config.set('core.useProxySettingsWhenCallingApm', false)
|
||||
packageManager = new PackageManager()
|
||||
|
||||
it "handle errors spawning apm", ->
|
||||
noSuchCommandError = if process.platform is 'win32' then ' cannot find the path ' else 'ENOENT'
|
||||
waitsForPromise shouldReject: true, -> packageManager.getInstalled()
|
||||
waitsForPromise shouldReject: true, -> packageManager.getOutdated()
|
||||
waitsForPromise shouldReject: true, -> packageManager.getFeatured()
|
||||
waitsForPromise shouldReject: true, -> packageManager.getPackage('foo')
|
||||
|
||||
installCallback = jasmine.createSpy('installCallback')
|
||||
uninstallCallback = jasmine.createSpy('uninstallCallback')
|
||||
updateCallback = jasmine.createSpy('updateCallback')
|
||||
|
||||
runs ->
|
||||
packageManager.install {name: 'foo', version: '1.0.0'}, installCallback
|
||||
|
||||
waitsFor ->
|
||||
installCallback.callCount is 1
|
||||
|
||||
runs ->
|
||||
installArg = installCallback.argsForCall[0][0]
|
||||
expect(installArg.message).toBe "Installing \u201Cfoo@1.0.0\u201D failed."
|
||||
expect(installArg.packageInstallError).toBe true
|
||||
expect(installArg.stderr).toContain noSuchCommandError
|
||||
|
||||
packageManager.uninstall {name: 'foo'}, uninstallCallback
|
||||
|
||||
waitsFor ->
|
||||
uninstallCallback.callCount is 1
|
||||
|
||||
runs ->
|
||||
uninstallArg = uninstallCallback.argsForCall[0][0]
|
||||
expect(uninstallArg.message).toBe "Uninstalling \u201Cfoo\u201D failed."
|
||||
expect(uninstallArg.stderr).toContain noSuchCommandError
|
||||
|
||||
packageManager.update {name: 'foo'}, '1.0.0', updateCallback
|
||||
|
||||
waitsFor ->
|
||||
updateCallback.callCount is 1
|
||||
|
||||
runs ->
|
||||
updateArg = updateCallback.argsForCall[0][0]
|
||||
expect(updateArg.message).toBe "Updating to \u201Cfoo@1.0.0\u201D failed."
|
||||
expect(updateArg.packageInstallError).toBe true
|
||||
expect(updateArg.stderr).toContain noSuchCommandError
|
||||
|
||||
describe "::isPackageInstalled()", ->
|
||||
it "returns false a package is not installed", ->
|
||||
expect(packageManager.isPackageInstalled('some-package')).toBe false
|
||||
|
||||
it "returns true when a package is loaded", ->
|
||||
spyOn(atom.packages, 'isPackageLoaded').andReturn true
|
||||
expect(packageManager.isPackageInstalled('some-package')).toBe true
|
||||
|
||||
it "returns true when a package is disabled", ->
|
||||
spyOn(atom.packages, 'getAvailablePackageNames').andReturn ['some-package']
|
||||
expect(packageManager.isPackageInstalled('some-package')).toBe true
|
||||
|
||||
describe "::install()", ->
|
||||
[runArgs, runCallback] = []
|
||||
|
||||
beforeEach ->
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
runArgs = args
|
||||
runCallback = callback
|
||||
onWillThrowError: ->
|
||||
|
||||
it "installs the latest version when a package version is not specified", ->
|
||||
packageManager.install {name: 'something'}, ->
|
||||
expect(packageManager.runCommand).toHaveBeenCalled()
|
||||
expect(runArgs).toEqual ['install', 'something', '--json']
|
||||
|
||||
it "installs the package@version when a version is specified", ->
|
||||
packageManager.install {name: 'something', version: '0.2.3'}, ->
|
||||
expect(packageManager.runCommand).toHaveBeenCalled()
|
||||
expect(runArgs).toEqual ['install', 'something@0.2.3', '--json']
|
||||
|
||||
describe "git url installation", ->
|
||||
it 'installs https:// urls', ->
|
||||
url = "https://github.com/user/repo.git"
|
||||
packageManager.install {name: url}
|
||||
expect(packageManager.runCommand).toHaveBeenCalled()
|
||||
expect(runArgs).toEqual ['install', 'https://github.com/user/repo.git', '--json']
|
||||
|
||||
it 'installs git@ urls', ->
|
||||
url = "git@github.com:user/repo.git"
|
||||
packageManager.install {name: url}
|
||||
expect(packageManager.runCommand).toHaveBeenCalled()
|
||||
expect(runArgs).toEqual ['install', 'git@github.com:user/repo.git', '--json']
|
||||
|
||||
it 'installs user/repo url shortcuts', ->
|
||||
url = "user/repo"
|
||||
packageManager.install {name: url}
|
||||
expect(packageManager.runCommand).toHaveBeenCalled()
|
||||
expect(runArgs).toEqual ['install', 'user/repo', '--json']
|
||||
|
||||
it 'installs and activates git pacakges with names different from the repo name', ->
|
||||
spyOn(atom.packages, 'activatePackage')
|
||||
packageManager.install(name: 'git-repo-name')
|
||||
json =
|
||||
metadata:
|
||||
name: 'real-package-name'
|
||||
runCallback(0, JSON.stringify([json]), '')
|
||||
expect(atom.packages.activatePackage).toHaveBeenCalledWith json.metadata.name
|
||||
|
||||
it 'emits an installed event with a copy of the pack including the full package metadata', ->
|
||||
spyOn(packageManager, 'emitPackageEvent')
|
||||
originalPackObject = name: 'git-repo-name', otherData: {will: 'beCopied'}
|
||||
packageManager.install(originalPackObject)
|
||||
json =
|
||||
metadata:
|
||||
name: 'real-package-name'
|
||||
moreInfo: 'yep'
|
||||
runCallback(0, JSON.stringify([json]), '')
|
||||
|
||||
installEmittedCount = 0
|
||||
for call in packageManager.emitPackageEvent.calls
|
||||
if call.args[0] is "installed"
|
||||
expect(call.args[1]).not.toEqual originalPackObject
|
||||
expect(call.args[1].moreInfo).toEqual "yep"
|
||||
expect(call.args[1].otherData).toBe originalPackObject.otherData
|
||||
installEmittedCount++
|
||||
expect(installEmittedCount).toBe 1
|
||||
|
||||
describe "::uninstall()", ->
|
||||
[runCallback] = []
|
||||
|
||||
beforeEach ->
|
||||
spyOn(packageManager, 'unload')
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
runCallback = callback
|
||||
onWillThrowError: ->
|
||||
|
||||
it "removes the package from the core.disabledPackages list", ->
|
||||
atom.config.set('core.disabledPackages', ['something'])
|
||||
|
||||
packageManager.uninstall {name: 'something'}, ->
|
||||
|
||||
expect(atom.config.get('core.disabledPackages')).toContain('something')
|
||||
runCallback(0, '', '')
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain('something')
|
||||
|
||||
describe "::packageHasSettings", ->
|
||||
it "returns true when the pacakge has config", ->
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-config'))
|
||||
expect(packageManager.packageHasSettings('package-with-config')).toBe true
|
||||
|
||||
it "returns false when the pacakge does not have config and doesn't define language grammars", ->
|
||||
expect(packageManager.packageHasSettings('random-package')).toBe false
|
||||
|
||||
it "returns true when the pacakge does not have config, but does define language grammars", ->
|
||||
packageName = 'language-test'
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage(path.join(__dirname, 'fixtures', packageName))
|
||||
|
||||
runs ->
|
||||
expect(packageManager.packageHasSettings(packageName)).toBe true
|
||||
|
||||
describe "::loadOutdated", ->
|
||||
it "caches results", ->
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
expect(packageManager.apmCache.loadOutdated.value).toMatch([{"name": "boop"}])
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
expect(packageManager.runCommand.calls.length).toBe(1)
|
||||
|
||||
it "expires results after a timeout", ->
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
now = Date.now()
|
||||
spyOn(Date, 'now') unless Date.now.andReturn
|
||||
Date.now.andReturn((-> now + packageManager.CACHE_EXPIRY + 1)())
|
||||
packageManager.loadOutdated false, ->
|
||||
|
||||
expect(packageManager.runCommand.calls.length).toBe(2)
|
||||
|
||||
it "expires results after a package updated/installed", ->
|
||||
packageManager.apmCache.loadOutdated =
|
||||
value: ['hi']
|
||||
expiry: Date.now() + 999999999
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
# Just prevent this stuff from calling through, it doesn't matter for this test
|
||||
spyOn(atom.packages, 'deactivatePackage').andReturn(true)
|
||||
spyOn(atom.packages, 'activatePackage').andReturn(true)
|
||||
spyOn(atom.packages, 'unloadPackage').andReturn(true)
|
||||
spyOn(atom.packages, 'loadPackage').andReturn(true)
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
expect(packageManager.runCommand.calls.length).toBe(0)
|
||||
|
||||
packageManager.update {}, {}, -> # +1 runCommand call to update the package
|
||||
packageManager.loadOutdated false, -> # +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(2)
|
||||
|
||||
packageManager.install {}, -> # +1 runCommand call to install the package
|
||||
packageManager.loadOutdated false, -> # +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(4)
|
||||
|
||||
packageManager.loadOutdated false, -> # +0 runCommand call, should be cached
|
||||
expect(packageManager.runCommand.calls.length).toBe(4)
|
||||
|
||||
it "expires results if it is called with clearCache set to true", ->
|
||||
packageManager.apmCache.loadOutdated =
|
||||
value: ['hi']
|
||||
expiry: Date.now() + 999999999
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
packageManager.loadOutdated true, ->
|
||||
expect(packageManager.runCommand.calls.length).toBe(1)
|
||||
expect(packageManager.apmCache.loadOutdated.value).toEqual [{"name": "boop"}]
|
||||
|
||||
describe "when there is a version pinned package", ->
|
||||
beforeEach ->
|
||||
atom.config.set('core.versionPinnedPackages', ['beep'])
|
||||
|
||||
it "caches results", ->
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
expect(packageManager.apmCache.loadOutdated.value).toMatch([{"name": "boop"}])
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
expect(packageManager.runCommand.calls.length).toBe(1)
|
||||
|
||||
it "expires results after a timeout", ->
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
now = Date.now()
|
||||
spyOn(Date, 'now') unless Date.now.andReturn
|
||||
Date.now.andReturn((-> now + packageManager.CACHE_EXPIRY + 1)())
|
||||
packageManager.loadOutdated false, ->
|
||||
|
||||
expect(packageManager.runCommand.calls.length).toBe(2)
|
||||
|
||||
it "expires results after a package updated/installed", ->
|
||||
packageManager.apmCache.loadOutdated =
|
||||
value: ['hi']
|
||||
expiry: Date.now() + 999999999
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
# Just prevent this stuff from calling through, it doesn't matter for this test
|
||||
spyOn(atom.packages, 'deactivatePackage').andReturn(true)
|
||||
spyOn(atom.packages, 'activatePackage').andReturn(true)
|
||||
spyOn(atom.packages, 'unloadPackage').andReturn(true)
|
||||
spyOn(atom.packages, 'loadPackage').andReturn(true)
|
||||
|
||||
packageManager.loadOutdated false, ->
|
||||
expect(packageManager.runCommand.calls.length).toBe(0)
|
||||
|
||||
packageManager.update {}, {}, -> # +1 runCommand call to update the package
|
||||
packageManager.loadOutdated false, -> # +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(2)
|
||||
|
||||
packageManager.install {}, -> # +1 runCommand call to install the package
|
||||
packageManager.loadOutdated false, -> # +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(4)
|
||||
|
||||
packageManager.loadOutdated false, -> # +0 runCommand call, should be cached
|
||||
expect(packageManager.runCommand.calls.length).toBe(4)
|
||||
|
||||
it "expires results if it is called with clearCache set to true", ->
|
||||
packageManager.apmCache.loadOutdated =
|
||||
value: ['hi']
|
||||
expiry: Date.now() + 999999999
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '')
|
||||
onWillThrowError: ->
|
||||
|
||||
packageManager.loadOutdated true, ->
|
||||
expect(packageManager.runCommand.calls.length).toBe(1)
|
||||
expect(packageManager.apmCache.loadOutdated.value).toEqual [{"name": "boop"}]
|
350
packages/settings-view/spec/package-manager-spec.js
Normal file
350
packages/settings-view/spec/package-manager-spec.js
Normal file
@ -0,0 +1,350 @@
|
||||
|
||||
const path = require('path');
|
||||
const process = require('process');
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
|
||||
describe("PackageManager", function() {
|
||||
let [packageManager] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(atom.packages, 'getApmPath').andReturn('/an/invalid/apm/command/to/run');
|
||||
atom.config.set('core.useProxySettingsWhenCallingApm', false);
|
||||
packageManager = new PackageManager();
|
||||
});
|
||||
|
||||
it("handle errors spawning apm", function() {
|
||||
const noSuchCommandError = process.platform === 'win32' ? ' cannot find the path ' : 'ENOENT';
|
||||
waitsForPromise({shouldReject: true}, () => packageManager.getInstalled());
|
||||
waitsForPromise({shouldReject: true}, () => packageManager.getOutdated());
|
||||
waitsForPromise({shouldReject: true}, () => packageManager.getFeatured());
|
||||
waitsForPromise({shouldReject: true}, () => packageManager.getPackage('foo'));
|
||||
|
||||
const installCallback = jasmine.createSpy('installCallback');
|
||||
const uninstallCallback = jasmine.createSpy('uninstallCallback');
|
||||
const updateCallback = jasmine.createSpy('updateCallback');
|
||||
|
||||
runs(() => packageManager.install({name: 'foo', version: '1.0.0'}, installCallback));
|
||||
|
||||
waitsFor(() => installCallback.callCount === 1);
|
||||
|
||||
runs(function() {
|
||||
const installArg = installCallback.argsForCall[0][0];
|
||||
expect(installArg.message).toBe("Installing \u201Cfoo@1.0.0\u201D failed.");
|
||||
expect(installArg.packageInstallError).toBe(true);
|
||||
expect(installArg.stderr).toContain(noSuchCommandError);
|
||||
|
||||
return packageManager.uninstall({name: 'foo'}, uninstallCallback);
|
||||
});
|
||||
|
||||
waitsFor(() => uninstallCallback.callCount === 1);
|
||||
|
||||
runs(function() {
|
||||
const uninstallArg = uninstallCallback.argsForCall[0][0];
|
||||
expect(uninstallArg.message).toBe("Uninstalling \u201Cfoo\u201D failed.");
|
||||
expect(uninstallArg.stderr).toContain(noSuchCommandError);
|
||||
|
||||
return packageManager.update({name: 'foo'}, '1.0.0', updateCallback);
|
||||
});
|
||||
|
||||
waitsFor(() => updateCallback.callCount === 1);
|
||||
|
||||
return runs(function() {
|
||||
const updateArg = updateCallback.argsForCall[0][0];
|
||||
expect(updateArg.message).toBe("Updating to \u201Cfoo@1.0.0\u201D failed.");
|
||||
expect(updateArg.packageInstallError).toBe(true);
|
||||
expect(updateArg.stderr).toContain(noSuchCommandError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("::isPackageInstalled()", function() {
|
||||
it("returns false a package is not installed", () => expect(packageManager.isPackageInstalled('some-package')).toBe(false));
|
||||
|
||||
it("returns true when a package is loaded", function() {
|
||||
spyOn(atom.packages, 'isPackageLoaded').andReturn(true);
|
||||
expect(packageManager.isPackageInstalled('some-package')).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true when a package is disabled", function() {
|
||||
spyOn(atom.packages, 'getAvailablePackageNames').andReturn(['some-package']);
|
||||
expect(packageManager.isPackageInstalled('some-package')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("::install()", function() {
|
||||
let [runArgs, runCallback] = [];
|
||||
|
||||
beforeEach(() => spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
runArgs = args;
|
||||
runCallback = callback;
|
||||
return {onWillThrowError() {}};
|
||||
}));
|
||||
|
||||
it("installs the latest version when a package version is not specified", function() {
|
||||
packageManager.install({name: 'something'}, function() {});
|
||||
expect(packageManager.runCommand).toHaveBeenCalled();
|
||||
expect(runArgs).toEqual(['install', 'something', '--json']);
|
||||
});
|
||||
|
||||
it("installs the package@version when a version is specified", function() {
|
||||
packageManager.install({name: 'something', version: '0.2.3'}, function() {});
|
||||
expect(packageManager.runCommand).toHaveBeenCalled();
|
||||
expect(runArgs).toEqual(['install', 'something@0.2.3', '--json']);
|
||||
});
|
||||
|
||||
describe("git url installation", function() {
|
||||
it('installs https:// urls', function() {
|
||||
const url = "https://github.com/user/repo.git";
|
||||
packageManager.install({name: url});
|
||||
expect(packageManager.runCommand).toHaveBeenCalled();
|
||||
expect(runArgs).toEqual(['install', 'https://github.com/user/repo.git', '--json']);
|
||||
});
|
||||
|
||||
it('installs git@ urls', function() {
|
||||
const url = "git@github.com:user/repo.git";
|
||||
packageManager.install({name: url});
|
||||
expect(packageManager.runCommand).toHaveBeenCalled();
|
||||
expect(runArgs).toEqual(['install', 'git@github.com:user/repo.git', '--json']);
|
||||
});
|
||||
|
||||
it('installs user/repo url shortcuts', function() {
|
||||
const url = "user/repo";
|
||||
packageManager.install({name: url});
|
||||
expect(packageManager.runCommand).toHaveBeenCalled();
|
||||
expect(runArgs).toEqual(['install', 'user/repo', '--json']);
|
||||
});
|
||||
|
||||
it('installs and activates git pacakges with names different from the repo name', function() {
|
||||
spyOn(atom.packages, 'activatePackage');
|
||||
packageManager.install({name: 'git-repo-name'});
|
||||
const json = {
|
||||
metadata: {
|
||||
name: 'real-package-name'
|
||||
}
|
||||
};
|
||||
runCallback(0, JSON.stringify([json]), '');
|
||||
expect(atom.packages.activatePackage).toHaveBeenCalledWith(json.metadata.name);
|
||||
});
|
||||
|
||||
it('emits an installed event with a copy of the pack including the full package metadata', function() {
|
||||
spyOn(packageManager, 'emitPackageEvent');
|
||||
const originalPackObject = {name: 'git-repo-name', otherData: {will: 'beCopied'}};
|
||||
packageManager.install(originalPackObject);
|
||||
const json = {
|
||||
metadata: {
|
||||
name: 'real-package-name',
|
||||
moreInfo: 'yep'
|
||||
}
|
||||
};
|
||||
runCallback(0, JSON.stringify([json]), '');
|
||||
|
||||
let installEmittedCount = 0;
|
||||
for (let call of Array.from(packageManager.emitPackageEvent.calls)) {
|
||||
if (call.args[0] === "installed") {
|
||||
expect(call.args[1]).not.toEqual(originalPackObject);
|
||||
expect(call.args[1].moreInfo).toEqual("yep");
|
||||
expect(call.args[1].otherData).toBe(originalPackObject.otherData);
|
||||
installEmittedCount++;
|
||||
}
|
||||
}
|
||||
expect(installEmittedCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("::uninstall()", function() {
|
||||
let [runCallback] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(packageManager, 'unload');
|
||||
return spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
runCallback = callback;
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
});
|
||||
|
||||
it("removes the package from the core.disabledPackages list", function() {
|
||||
atom.config.set('core.disabledPackages', ['something']);
|
||||
|
||||
packageManager.uninstall({name: 'something'}, function() {});
|
||||
|
||||
expect(atom.config.get('core.disabledPackages')).toContain('something');
|
||||
runCallback(0, '', '');
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain('something');
|
||||
});
|
||||
});
|
||||
|
||||
describe("::packageHasSettings", function() {
|
||||
it("returns true when the pacakge has config", function() {
|
||||
atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'package-with-config'));
|
||||
expect(packageManager.packageHasSettings('package-with-config')).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false when the pacakge does not have config and doesn't define language grammars", () => expect(packageManager.packageHasSettings('random-package')).toBe(false));
|
||||
|
||||
it("returns true when the pacakge does not have config, but does define language grammars", function() {
|
||||
const packageName = 'language-test';
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage(path.join(__dirname, 'fixtures', packageName)));
|
||||
|
||||
return runs(() => expect(packageManager.packageHasSettings(packageName)).toBe(true));
|
||||
});
|
||||
});
|
||||
|
||||
describe("::loadOutdated", function() {
|
||||
it("caches results", function() {
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
expect(packageManager.apmCache.loadOutdated.value).toMatch([{"name": "boop"}]);
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
expect(packageManager.runCommand.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it("expires results after a timeout", function() {
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
const now = Date.now();
|
||||
if (!Date.now.andReturn) { spyOn(Date, 'now'); }
|
||||
Date.now.andReturn(((() => now + packageManager.CACHE_EXPIRY + 1))());
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
|
||||
expect(packageManager.runCommand.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it("expires results after a package updated/installed", function() {
|
||||
packageManager.apmCache.loadOutdated = {
|
||||
value: ['hi'],
|
||||
expiry: Date.now() + 999999999
|
||||
};
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
// Just prevent this stuff from calling through, it doesn't matter for this test
|
||||
spyOn(atom.packages, 'deactivatePackage').andReturn(true);
|
||||
spyOn(atom.packages, 'activatePackage').andReturn(true);
|
||||
spyOn(atom.packages, 'unloadPackage').andReturn(true);
|
||||
spyOn(atom.packages, 'loadPackage').andReturn(true);
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
expect(packageManager.runCommand.calls.length).toBe(0);
|
||||
|
||||
packageManager.update({}, {}, function() {}); // +1 runCommand call to update the package
|
||||
packageManager.loadOutdated(false, function() {}); // +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(2);
|
||||
|
||||
packageManager.install({}, function() {}); // +1 runCommand call to install the package
|
||||
packageManager.loadOutdated(false, function() {}); // +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(4);
|
||||
|
||||
packageManager.loadOutdated(false, function() {}); // +0 runCommand call, should be cached
|
||||
expect(packageManager.runCommand.calls.length).toBe(4);
|
||||
});
|
||||
|
||||
it("expires results if it is called with clearCache set to true", function() {
|
||||
packageManager.apmCache.loadOutdated = {
|
||||
value: ['hi'],
|
||||
expiry: Date.now() + 999999999
|
||||
};
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
packageManager.loadOutdated(true, function() {});
|
||||
expect(packageManager.runCommand.calls.length).toBe(1);
|
||||
expect(packageManager.apmCache.loadOutdated.value).toEqual([{"name": "boop"}]);
|
||||
});
|
||||
|
||||
describe("when there is a version pinned package", function() {
|
||||
beforeEach(() => atom.config.set('core.versionPinnedPackages', ['beep']));
|
||||
|
||||
it("caches results", function() {
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
expect(packageManager.apmCache.loadOutdated.value).toMatch([{"name": "boop"}]);
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
expect(packageManager.runCommand.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it("expires results after a timeout", function() {
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
const now = Date.now();
|
||||
if (!Date.now.andReturn) { spyOn(Date, 'now'); }
|
||||
Date.now.andReturn(((() => now + packageManager.CACHE_EXPIRY + 1))());
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
|
||||
expect(packageManager.runCommand.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it("expires results after a package updated/installed", function() {
|
||||
packageManager.apmCache.loadOutdated = {
|
||||
value: ['hi'],
|
||||
expiry: Date.now() + 999999999
|
||||
};
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
// Just prevent this stuff from calling through, it doesn't matter for this test
|
||||
spyOn(atom.packages, 'deactivatePackage').andReturn(true);
|
||||
spyOn(atom.packages, 'activatePackage').andReturn(true);
|
||||
spyOn(atom.packages, 'unloadPackage').andReturn(true);
|
||||
spyOn(atom.packages, 'loadPackage').andReturn(true);
|
||||
|
||||
packageManager.loadOutdated(false, function() {});
|
||||
expect(packageManager.runCommand.calls.length).toBe(0);
|
||||
|
||||
packageManager.update({}, {}, function() {}); // +1 runCommand call to update the package
|
||||
packageManager.loadOutdated(false, function() {}); // +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(2);
|
||||
|
||||
packageManager.install({}, function() {}); // +1 runCommand call to install the package
|
||||
packageManager.loadOutdated(false, function() {}); // +1 runCommand call to load outdated because the cache should be wiped
|
||||
expect(packageManager.runCommand.calls.length).toBe(4);
|
||||
|
||||
packageManager.loadOutdated(false, function() {}); // +0 runCommand call, should be cached
|
||||
expect(packageManager.runCommand.calls.length).toBe(4);
|
||||
});
|
||||
|
||||
it("expires results if it is called with clearCache set to true", function() {
|
||||
packageManager.apmCache.loadOutdated = {
|
||||
value: ['hi'],
|
||||
expiry: Date.now() + 999999999
|
||||
};
|
||||
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
callback(0, '[{"name": "boop"}, {"name": "beep"}]', '');
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
|
||||
packageManager.loadOutdated(true, function() {});
|
||||
expect(packageManager.runCommand.calls.length).toBe(1);
|
||||
expect(packageManager.apmCache.loadOutdated.value).toEqual([{"name": "boop"}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,152 +0,0 @@
|
||||
PackageManager = require '../lib/package-manager'
|
||||
PackageUpdatesStatusView = require '../lib/package-updates-status-view'
|
||||
|
||||
describe "PackageUpdatesStatusView", ->
|
||||
[statusBar, statusView, packageManager] = []
|
||||
|
||||
outdatedPackage1 =
|
||||
name: 'out-dated-1'
|
||||
outdatedPackage2 =
|
||||
name: 'out-dated-2'
|
||||
installedPackage =
|
||||
name: 'user-package'
|
||||
|
||||
beforeEach ->
|
||||
spyOn(PackageManager.prototype, 'loadCompatiblePackageVersion').andCallFake ->
|
||||
spyOn(PackageManager.prototype, 'getInstalled').andCallFake -> Promise.resolve([installedPackage])
|
||||
spyOn(PackageManager.prototype, 'getOutdated').andCallFake -> Promise.resolve([outdatedPackage1, outdatedPackage2])
|
||||
spyOn(PackageUpdatesStatusView.prototype, 'initialize').andCallThrough()
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace))
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('status-bar')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('settings-view')
|
||||
|
||||
runs ->
|
||||
atom.packages.emitter.emit('did-activate-all')
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).toExist()
|
||||
|
||||
packageManager = PackageUpdatesStatusView.prototype.initialize.mostRecentCall.args[1]
|
||||
|
||||
describe "when packages are outdated", ->
|
||||
it "adds a tile to the status bar", ->
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '2 updates'
|
||||
|
||||
describe "when the tile is clicked", ->
|
||||
it "opens the Available Updates panel", ->
|
||||
spyOn(atom.commands, 'dispatch').andCallFake ->
|
||||
|
||||
document.querySelector('status-bar .package-updates-status-view').click()
|
||||
expect(atom.commands.dispatch).toHaveBeenCalledWith(atom.views.getView(atom.workspace), 'settings-view:check-for-package-updates')
|
||||
|
||||
it "does not destroy the tile", ->
|
||||
document.querySelector('status-bar .package-updates-status-view').click()
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).toExist()
|
||||
|
||||
describe "when a package is updating", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1/2 updating'
|
||||
|
||||
describe "when a package finishes updating", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1 update'
|
||||
|
||||
describe "when a package is updated without a prior updating event", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1 update'
|
||||
|
||||
describe "when multiple packages are updating and one finishes", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage2)
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1/1 updating'
|
||||
|
||||
describe "when a package fails to update", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '2 updates (1 failed)'
|
||||
|
||||
describe "when a package that previously failed to update starts updating again", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1/2 updating'
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '2 updates (1 failed)'
|
||||
|
||||
describe "when a package update that previously failed succeeds on a subsequent try", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1 update'
|
||||
|
||||
describe "when multiple events are happening at the same time", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('update-available', installedPackage)
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage2)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1/3 updating (1 failed)'
|
||||
|
||||
describe "when there are no more updates", ->
|
||||
it "destroys the tile", ->
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage2)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).not.toExist()
|
||||
|
||||
describe "when a new update becomes available and the tile is destroyed", ->
|
||||
it "recreates the tile", ->
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage2)
|
||||
packageManager.emitPackageEvent('update-available', installedPackage)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).toExist()
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1 update'
|
||||
|
||||
describe "when an update becomes available for a package", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('update-available', installedPackage)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '3 updates'
|
||||
|
||||
describe "when updates are checked for multiple times and no new updates are available", ->
|
||||
it "does not keep updating the tile", ->
|
||||
packageManager.emitPackageEvent('update-available', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('update-available', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('update-available', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '2 updates'
|
||||
|
||||
# There are more fields in an actual package object,
|
||||
# so make sure only name is tested and not object equality
|
||||
packageManager.emitPackageEvent('update-available', {name: 'out-dated-1', date: Date.now()})
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '2 updates'
|
||||
|
||||
describe "when the same update fails multiple times", ->
|
||||
it "does not keep updating the tile", ->
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '2 updates (1 failed)'
|
||||
|
||||
describe "when a package that can be updated is uninstalled", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('uninstalled', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1 update'
|
||||
|
||||
describe "when a package that is updating is uninstalled", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('uninstalled', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1 update'
|
||||
|
||||
describe "when a package that failed to update is uninstalled", ->
|
||||
it "updates the tile", ->
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1)
|
||||
packageManager.emitPackageEvent('uninstalled', outdatedPackage1)
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe '1 update'
|
153
packages/settings-view/spec/package-updates-status-view-spec.js
Normal file
153
packages/settings-view/spec/package-updates-status-view-spec.js
Normal file
@ -0,0 +1,153 @@
|
||||
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const PackageUpdatesStatusView = require('../lib/package-updates-status-view');
|
||||
|
||||
describe("PackageUpdatesStatusView", function() {
|
||||
let [statusBar, statusView, packageManager] = [];
|
||||
|
||||
const outdatedPackage1 =
|
||||
{name: 'out-dated-1'};
|
||||
const outdatedPackage2 =
|
||||
{name: 'out-dated-2'};
|
||||
const installedPackage =
|
||||
{name: 'user-package'};
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(PackageManager.prototype, 'loadCompatiblePackageVersion').andCallFake(function() {});
|
||||
spyOn(PackageManager.prototype, 'getInstalled').andCallFake(() => Promise.resolve([installedPackage]));
|
||||
spyOn(PackageManager.prototype, 'getOutdated').andCallFake(() => Promise.resolve([outdatedPackage1, outdatedPackage2]));
|
||||
spyOn(PackageUpdatesStatusView.prototype, 'initialize').andCallThrough();
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace));
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage('status-bar'));
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage('settings-view'));
|
||||
|
||||
runs(function() {
|
||||
atom.packages.emitter.emit('did-activate-all');
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).toExist();
|
||||
|
||||
return packageManager = PackageUpdatesStatusView.prototype.initialize.mostRecentCall.args[1];});});
|
||||
|
||||
describe("when packages are outdated", () => it("adds a tile to the status bar", () => expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('2 updates')));
|
||||
|
||||
describe("when the tile is clicked", function() {
|
||||
it("opens the Available Updates panel", function() {
|
||||
spyOn(atom.commands, 'dispatch').andCallFake(function() {});
|
||||
|
||||
document.querySelector('status-bar .package-updates-status-view').click();
|
||||
expect(atom.commands.dispatch).toHaveBeenCalledWith(atom.views.getView(atom.workspace), 'settings-view:check-for-package-updates');
|
||||
});
|
||||
|
||||
it("does not destroy the tile", function() {
|
||||
document.querySelector('status-bar .package-updates-status-view').click();
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when a package is updating", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1/2 updating');
|
||||
}));
|
||||
|
||||
describe("when a package finishes updating", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1 update');
|
||||
}));
|
||||
|
||||
describe("when a package is updated without a prior updating event", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1 update');
|
||||
}));
|
||||
|
||||
describe("when multiple packages are updating and one finishes", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage2);
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1/1 updating');
|
||||
}));
|
||||
|
||||
describe("when a package fails to update", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('2 updates (1 failed)');
|
||||
}));
|
||||
|
||||
describe("when a package that previously failed to update starts updating again", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1/2 updating');
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('2 updates (1 failed)');
|
||||
}));
|
||||
|
||||
describe("when a package update that previously failed succeeds on a subsequent try", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1 update');
|
||||
}));
|
||||
|
||||
describe("when multiple events are happening at the same time", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('update-available', installedPackage);
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage2);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1/3 updating (1 failed)');
|
||||
}));
|
||||
|
||||
describe("when there are no more updates", () => it("destroys the tile", function() {
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage2);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).not.toExist();
|
||||
}));
|
||||
|
||||
describe("when a new update becomes available and the tile is destroyed", () => it("recreates the tile", function() {
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('updated', outdatedPackage2);
|
||||
packageManager.emitPackageEvent('update-available', installedPackage);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view')).toExist();
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1 update');
|
||||
}));
|
||||
|
||||
describe("when an update becomes available for a package", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('update-available', installedPackage);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('3 updates');
|
||||
}));
|
||||
|
||||
describe("when updates are checked for multiple times and no new updates are available", () => it("does not keep updating the tile", function() {
|
||||
packageManager.emitPackageEvent('update-available', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('update-available', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('update-available', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('2 updates');
|
||||
|
||||
// There are more fields in an actual package object,
|
||||
// so make sure only name is tested and not object equality
|
||||
packageManager.emitPackageEvent('update-available', {name: 'out-dated-1', date: Date.now()});
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('2 updates');
|
||||
}));
|
||||
|
||||
describe("when the same update fails multiple times", () => it("does not keep updating the tile", function() {
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('2 updates (1 failed)');
|
||||
}));
|
||||
|
||||
describe("when a package that can be updated is uninstalled", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('uninstalled', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1 update');
|
||||
}));
|
||||
|
||||
describe("when a package that is updating is uninstalled", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('updating', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('uninstalled', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1 update');
|
||||
}));
|
||||
|
||||
describe("when a package that failed to update is uninstalled", () => it("updates the tile", function() {
|
||||
packageManager.emitPackageEvent('update-failed', outdatedPackage1);
|
||||
packageManager.emitPackageEvent('uninstalled', outdatedPackage1);
|
||||
expect(document.querySelector('status-bar .package-updates-status-view').textContent).toBe('1 update');
|
||||
}));
|
||||
});
|
@ -1,159 +0,0 @@
|
||||
{getSettingDescription} = require '../lib/rich-description'
|
||||
|
||||
describe "Rich descriptions", ->
|
||||
beforeEach ->
|
||||
config =
|
||||
type: 'object'
|
||||
properties:
|
||||
plainText:
|
||||
description: 'Plain text description'
|
||||
type: 'string'
|
||||
default: ''
|
||||
italics:
|
||||
description: 'Description *with* italics'
|
||||
type: 'string'
|
||||
default: ''
|
||||
bold:
|
||||
description: 'Description **with** bold'
|
||||
type: 'string'
|
||||
default: ''
|
||||
link:
|
||||
description: 'Description [with](http://www.example.com) link'
|
||||
type: 'string'
|
||||
default: ''
|
||||
inlineCode:
|
||||
description: 'Description `with` inline code'
|
||||
type: 'string'
|
||||
default: ''
|
||||
lineBreak:
|
||||
description: 'Description with<br/> line break'
|
||||
type: 'string'
|
||||
default: ''
|
||||
strikethrough:
|
||||
description: 'Description ~~with~~ strikethrough'
|
||||
type: 'string'
|
||||
default: ''
|
||||
image:
|
||||
description: 'Description without ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png "Logo Title Text 1") image'
|
||||
type: 'string'
|
||||
default: ''
|
||||
fencedBlockCode:
|
||||
description: '''Description without fenced block code
|
||||
```
|
||||
Test
|
||||
```
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
indentedBlockCode:
|
||||
description: '''
|
||||
Description without indented block code
|
||||
|
||||
Test
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
blockquote:
|
||||
description: '''
|
||||
Description without blockquote
|
||||
|
||||
> Test
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
html:
|
||||
description: '''
|
||||
Description without html
|
||||
|
||||
<html>Test</html>
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
heading:
|
||||
description: '''
|
||||
Description without heading
|
||||
|
||||
## Test
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
orderedList:
|
||||
description: '''
|
||||
Description without ordered list
|
||||
|
||||
1. Test
|
||||
2. Test
|
||||
3. Test
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
unorderedList:
|
||||
description: '''
|
||||
Description without unordered list
|
||||
|
||||
* Test
|
||||
* Test
|
||||
* Test
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
table:
|
||||
description: '''
|
||||
Description without table
|
||||
|
||||
<table><tr><td>Test</td></tr></table>
|
||||
'''
|
||||
type: 'string'
|
||||
default: ''
|
||||
|
||||
atom.config.setSchema("foo", config)
|
||||
|
||||
describe 'supported Markdown', ->
|
||||
it 'handles plain text', ->
|
||||
expect(getSettingDescription('foo.plainText')).toEqual 'Plain text description'
|
||||
|
||||
it 'handles italics', ->
|
||||
expect(getSettingDescription('foo.italics')).toEqual 'Description <em>with</em> italics'
|
||||
|
||||
it 'handles bold', ->
|
||||
expect(getSettingDescription('foo.bold')).toEqual 'Description <strong>with</strong> bold'
|
||||
|
||||
it 'handles links', ->
|
||||
expect(getSettingDescription('foo.link')).toEqual 'Description <a href="http://www.example.com">with</a> link'
|
||||
|
||||
it 'handles inline code', ->
|
||||
expect(getSettingDescription('foo.inlineCode')).toEqual 'Description <code>with</code> inline code'
|
||||
|
||||
it 'handles line breaks', ->
|
||||
expect(getSettingDescription('foo.lineBreak')).toEqual 'Description with<br/> line break'
|
||||
|
||||
it 'handles strikethrough', ->
|
||||
expect(getSettingDescription('foo.strikethrough')).toEqual 'Description <del>with</del> strikethrough'
|
||||
|
||||
describe 'unsupported Markdown', ->
|
||||
it 'strips images', ->
|
||||
expect(getSettingDescription('foo.image')).toEqual 'Description without image'
|
||||
|
||||
it 'strips fenced code blocks', ->
|
||||
expect(getSettingDescription('foo.fencedBlockCode')).toEqual 'Description without fenced block code'
|
||||
|
||||
it 'strips indented code blocks', ->
|
||||
expect(getSettingDescription('foo.indentedBlockCode')).toEqual 'Description without indented block code'
|
||||
|
||||
it 'strips blockquotes', ->
|
||||
expect(getSettingDescription('foo.blockquote')).toEqual 'Description without blockquote'
|
||||
|
||||
it 'strips html elements', ->
|
||||
expect(getSettingDescription('foo.html')).toEqual 'Description without html'
|
||||
|
||||
it 'strips headings', ->
|
||||
expect(getSettingDescription('foo.heading')).toEqual 'Description without heading'
|
||||
|
||||
it 'strips ordered lists', ->
|
||||
expect(getSettingDescription('foo.orderedList')).toEqual 'Description without ordered list'
|
||||
|
||||
it 'strips unordered lists', ->
|
||||
expect(getSettingDescription('foo.unorderedList')).toEqual 'Description without unordered list'
|
||||
|
||||
it 'strips tables', ->
|
||||
expect(getSettingDescription('foo.table')).toEqual 'Description without table'
|
166
packages/settings-view/spec/rich-description-spec.js
Normal file
166
packages/settings-view/spec/rich-description-spec.js
Normal file
@ -0,0 +1,166 @@
|
||||
|
||||
const {getSettingDescription} = require('../lib/rich-description');
|
||||
|
||||
describe("Rich descriptions", () => {
|
||||
beforeEach(() => {
|
||||
const config = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
plainText: {
|
||||
description: 'Plain text description',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
italics: {
|
||||
description: 'Description *with* italics',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
bold: {
|
||||
description: 'Description **with** bold',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
link: {
|
||||
description: 'Description [with](http://www.example.com) link',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
inlineCode: {
|
||||
description: 'Description `with` inline code',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
lineBreak: {
|
||||
description: 'Description with<br/> line break',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
strikethrough: {
|
||||
description: 'Description ~~with~~ strikethrough',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
image: {
|
||||
description: 'Description without ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png "Logo Title Text 1") image',
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
fencedBlockCode: {
|
||||
description: `Description without fenced block code
|
||||
\`\`\`
|
||||
Test
|
||||
\`\`\`\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
indentedBlockCode: {
|
||||
description: `\
|
||||
Description without indented block code
|
||||
|
||||
Test\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
blockquote: {
|
||||
description: `\
|
||||
Description without blockquote
|
||||
|
||||
> Test\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
html: {
|
||||
description: `\
|
||||
Description without html
|
||||
|
||||
<html>Test</html>\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
heading: {
|
||||
description: `\
|
||||
Description without heading
|
||||
|
||||
## Test\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
orderedList: {
|
||||
description: `\
|
||||
Description without ordered list
|
||||
|
||||
1. Test
|
||||
2. Test
|
||||
3. Test\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
unorderedList: {
|
||||
description: `\
|
||||
Description without unordered list
|
||||
|
||||
* Test
|
||||
* Test
|
||||
* Test\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
},
|
||||
table: {
|
||||
description: `\
|
||||
Description without table
|
||||
|
||||
<table><tr><td>Test</td></tr></table>\
|
||||
`,
|
||||
type: 'string',
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
atom.config.setSchema("foo", config);
|
||||
});
|
||||
|
||||
describe('supported Markdown', () => {
|
||||
it('handles plain text', () => expect(getSettingDescription('foo.plainText')).toEqual('Plain text description'));
|
||||
|
||||
it('handles italics', () => expect(getSettingDescription('foo.italics')).toEqual('Description <em>with</em> italics'));
|
||||
|
||||
it('handles bold', () => expect(getSettingDescription('foo.bold')).toEqual('Description <strong>with</strong> bold'));
|
||||
|
||||
it('handles links', () => expect(getSettingDescription('foo.link')).toEqual('Description <a href="http://www.example.com">with</a> link'));
|
||||
|
||||
it('handles inline code', () => expect(getSettingDescription('foo.inlineCode')).toEqual('Description <code>with</code> inline code'));
|
||||
|
||||
it('handles line breaks', () => expect(getSettingDescription('foo.lineBreak')).toEqual('Description with<br/> line break'));
|
||||
|
||||
it('handles strikethrough', () => expect(getSettingDescription('foo.strikethrough')).toEqual('Description <del>with</del> strikethrough'));
|
||||
});
|
||||
|
||||
describe('unsupported Markdown', () => {
|
||||
it('strips images', () => expect(getSettingDescription('foo.image')).toEqual('Description without image'));
|
||||
|
||||
it('strips fenced code blocks', () => expect(getSettingDescription('foo.fencedBlockCode')).toEqual('Description without fenced block code'));
|
||||
|
||||
it('strips indented code blocks', () => expect(getSettingDescription('foo.indentedBlockCode')).toEqual('Description without indented block code'));
|
||||
|
||||
it('strips blockquotes', () => expect(getSettingDescription('foo.blockquote')).toEqual('Description without blockquote'));
|
||||
|
||||
it('strips html elements', () => expect(getSettingDescription('foo.html')).toEqual('Description without html'));
|
||||
|
||||
it('strips headings', () => expect(getSettingDescription('foo.heading')).toEqual('Description without heading'));
|
||||
|
||||
it('strips ordered lists', () => expect(getSettingDescription('foo.orderedList')).toEqual('Description without ordered list'));
|
||||
|
||||
it('strips unordered lists', () => expect(getSettingDescription('foo.unorderedList')).toEqual('Description without unordered list'));
|
||||
|
||||
it('strips tables', () => expect(getSettingDescription('foo.table')).toEqual('Description without table'));
|
||||
});
|
||||
});
|
@ -1,391 +0,0 @@
|
||||
SettingsPanel = require '../lib/settings-panel'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
describe "SettingsPanel", ->
|
||||
settingsPanel = null
|
||||
|
||||
describe "sorted settings", ->
|
||||
beforeEach ->
|
||||
config =
|
||||
type: 'object'
|
||||
properties:
|
||||
bar:
|
||||
title: 'Bar'
|
||||
description: 'The bar setting'
|
||||
type: 'boolean'
|
||||
default: true
|
||||
haz:
|
||||
title: 'Haz'
|
||||
description: 'The haz setting'
|
||||
type: 'string'
|
||||
default: 'haz'
|
||||
zing:
|
||||
title: 'Zing'
|
||||
description: 'The zing setting'
|
||||
type: 'string'
|
||||
default: 'zing'
|
||||
order: 1
|
||||
zang:
|
||||
title: 'Zang'
|
||||
description: 'The baz setting'
|
||||
type: 'string'
|
||||
default: 'zang'
|
||||
order: 100
|
||||
enum:
|
||||
title: 'An enum'
|
||||
type: 'string'
|
||||
default: 'one'
|
||||
enum: [
|
||||
{value: 'one', description: 'One'}
|
||||
'Two'
|
||||
]
|
||||
radio:
|
||||
title: 'An enum with radio buttons'
|
||||
radio: true
|
||||
type: 'string'
|
||||
default: 'Two'
|
||||
enum: [
|
||||
{value: 'one', description: 'One'}
|
||||
'Two'
|
||||
]
|
||||
atom.config.setSchema("foo", config)
|
||||
atom.config.setDefaults("foo", gong: 'gong')
|
||||
expect(_.size(atom.config.get('foo'))).toBe 7
|
||||
settingsPanel = new SettingsPanel({namespace: "foo", includeTitle: false})
|
||||
|
||||
it "sorts settings by order and then alphabetically by the key", ->
|
||||
settings = atom.config.get('foo')
|
||||
expect(_.size(settings)).toBe 7
|
||||
sortedSettings = settingsPanel.sortSettings("foo", settings)
|
||||
expect(sortedSettings[0]).toBe 'zing'
|
||||
expect(sortedSettings[1]).toBe 'zang'
|
||||
expect(sortedSettings[2]).toBe 'bar'
|
||||
expect(sortedSettings[3]).toBe 'enum'
|
||||
expect(sortedSettings[4]).toBe 'gong'
|
||||
expect(sortedSettings[5]).toBe 'haz'
|
||||
expect(sortedSettings[6]).toBe 'radio'
|
||||
|
||||
it "gracefully deals with a null settings object", ->
|
||||
sortedSettings = settingsPanel.sortSettings("foo", null)
|
||||
expect(sortedSettings).not.toBeNull
|
||||
expect(_.size(sortedSettings)).toBe 0
|
||||
|
||||
it "presents enum options with their descriptions", ->
|
||||
select = settingsPanel.element.querySelector('#foo\\.enum')
|
||||
pairs = ([opt.value, opt.innerText] for opt in select.children)
|
||||
expect(pairs).toEqual([['one', 'One'], ['Two', 'Two']])
|
||||
|
||||
it "presents radio options with their descriptions", ->
|
||||
radio = settingsPanel.element.querySelector('#foo\\.radio')
|
||||
options = for label in radio.querySelectorAll 'label'
|
||||
button = label.querySelector('input[type=radio][name="foo.radio"]')
|
||||
[button.id, button.value, label.innerText]
|
||||
expect(options).toEqual([['foo.radio[one]', 'one', 'One'], ['foo.radio[Two]', 'Two', 'Two']])
|
||||
|
||||
describe 'default settings', ->
|
||||
beforeEach ->
|
||||
config =
|
||||
type: 'object'
|
||||
properties:
|
||||
haz:
|
||||
name: 'haz'
|
||||
title: 'Haz'
|
||||
description: 'The haz setting'
|
||||
type: 'string'
|
||||
default: 'haz'
|
||||
qux:
|
||||
name: 'qux'
|
||||
title: 'Qux'
|
||||
description: 'The qux setting'
|
||||
type: 'string'
|
||||
default: 'a'
|
||||
enum: [
|
||||
{value: 'a', description: 'Alice'},
|
||||
{value: 'b', description: 'Bob'}
|
||||
]
|
||||
testZero:
|
||||
name: 'testZero'
|
||||
title: 'Test Zero'
|
||||
description: 'Setting for testing zero as a default'
|
||||
type: 'integer'
|
||||
default: 0
|
||||
radio:
|
||||
title: 'An enum with radio buttons'
|
||||
radio: true
|
||||
type: 'string'
|
||||
default: 'Two'
|
||||
enum: [
|
||||
{value: 'one', description: 'One'}
|
||||
'Two'
|
||||
'Three'
|
||||
]
|
||||
atom.config.setSchema("foo", config)
|
||||
atom.config.setDefaults("foo", gong: 'gong')
|
||||
expect(_.size(atom.config.get('foo'))).toBe 5
|
||||
settingsPanel = new SettingsPanel({namespace: "foo", includeTitle: false})
|
||||
|
||||
it 'ensures default stays default', ->
|
||||
expect(settingsPanel.getDefault('foo.haz')).toBe 'haz'
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe true
|
||||
settingsPanel.set('foo.haz', 'haz')
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe true
|
||||
|
||||
it 'can be overwritten', ->
|
||||
expect(settingsPanel.getDefault('foo.haz')).toBe 'haz'
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe true
|
||||
settingsPanel.set('foo.haz', 'newhaz')
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe false
|
||||
expect(atom.config.get('foo.haz')).toBe 'newhaz'
|
||||
|
||||
it "ignores project-specific overrides", ->
|
||||
atom.project.replace(
|
||||
originPath: 'TEST'
|
||||
config:
|
||||
foo:
|
||||
haz: 'newhaz'
|
||||
)
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe true
|
||||
expect(atom.config.get('foo.haz')).toBe 'newhaz'
|
||||
|
||||
it 'has a tooltip showing the default value', ->
|
||||
hazEditor = settingsPanel.element.querySelector('[id="foo.haz"]')
|
||||
tooltips = atom.tooltips.findTooltips(hazEditor)
|
||||
expect(tooltips).toHaveLength 1
|
||||
title = tooltips[0].options.title
|
||||
expect(title).toBe "Default: haz"
|
||||
|
||||
it 'has a tooltip showing the description of the default value', ->
|
||||
quxEditor = settingsPanel.element.querySelector('[id="foo.qux"]')
|
||||
tooltips = atom.tooltips.findTooltips(quxEditor)
|
||||
expect(tooltips).toHaveLength 1
|
||||
title = tooltips[0].options.title
|
||||
expect(title).toBe "Default: Alice"
|
||||
|
||||
# Regression test for #783
|
||||
it 'allows 0 to be a default', ->
|
||||
zeroEditor = settingsPanel.element.querySelector('[id="foo.testZero"]')
|
||||
expect(zeroEditor.getModel().getText()).toBe('')
|
||||
expect(zeroEditor.getModel().getPlaceholderText()).toBe('Default: 0')
|
||||
|
||||
expect(settingsPanel.getDefault('foo.testZero')).toBe 0
|
||||
expect(settingsPanel.isDefault('foo.testZero')).toBe true
|
||||
|
||||
settingsPanel.set('foo.testZero', 15)
|
||||
expect(settingsPanel.isDefault('foo.testZero')).toBe false
|
||||
|
||||
settingsPanel.set('foo.testZero', 0)
|
||||
expect(settingsPanel.isDefault('foo.testZero')).toBe true
|
||||
|
||||
it "selects the default choice for radio options", ->
|
||||
expect(settingsPanel.getDefault 'foo.radio').toBe 'Two'
|
||||
settingsPanel.set 'foo.radio', 'Two'
|
||||
expect(settingsPanel.element.querySelector '#foo\\.radio\\[Two\\]').toBeChecked()
|
||||
|
||||
describe 'scoped settings', ->
|
||||
beforeEach ->
|
||||
schema =
|
||||
scopes:
|
||||
'.source.python':
|
||||
default: 4
|
||||
|
||||
atom.config.setScopedDefaultsFromSchema('editor.tabLength', schema)
|
||||
expect(atom.config.get('editor.tabLength')).toBe(2)
|
||||
|
||||
it 'displays the scoped default', ->
|
||||
settingsPanel = new SettingsPanel({namespace: "editor", includeTitle: false, scopeName: '.source.python'})
|
||||
tabLengthEditor = settingsPanel.element.querySelector('[id="editor.tabLength"]')
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('')
|
||||
expect(tabLengthEditor.getModel().getPlaceholderText()).toBe('Default: 4')
|
||||
|
||||
it 'allows the scoped setting to be changed to its normal default if the unscoped value is different', ->
|
||||
atom.config.set('editor.tabLength', 8)
|
||||
|
||||
settingsPanel = new SettingsPanel({namespace: "editor", includeTitle: false, scopeName: '.source.js'})
|
||||
tabLengthEditor = settingsPanel.element.querySelector('[id="editor.tabLength"]')
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('')
|
||||
expect(tabLengthEditor.getModel().getPlaceholderText()).toBe('Default: 8')
|
||||
|
||||
# This is the unscoped default, but it differs from the current unscoped value
|
||||
settingsPanel.set('editor.tabLength', 2)
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('2')
|
||||
expect(atom.config.get('editor.tabLength', {scope: ['source.js']})).toBe(2)
|
||||
|
||||
it 'allows the scoped setting to be changed to the unscoped default if it is different', ->
|
||||
settingsPanel = new SettingsPanel({namespace: "editor", includeTitle: false, scopeName: '.source.python'})
|
||||
tabLengthEditor = settingsPanel.element.querySelector('[id="editor.tabLength"]')
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('')
|
||||
expect(tabLengthEditor.getModel().getPlaceholderText()).toBe('Default: 4')
|
||||
|
||||
# This is the unscoped default, but it differs from the scoped default
|
||||
settingsPanel.set('editor.tabLength', 2)
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('2')
|
||||
expect(atom.config.get('editor.tabLength', {scope: ['source.python']})).toBe(2)
|
||||
|
||||
describe 'grouped settings', ->
|
||||
beforeEach ->
|
||||
config =
|
||||
type: 'object'
|
||||
properties:
|
||||
barGroup:
|
||||
type: 'object'
|
||||
title: 'Bar group'
|
||||
description: 'description of bar group'
|
||||
properties:
|
||||
bar:
|
||||
title: 'Bar'
|
||||
description: 'The bar setting'
|
||||
type: 'boolean'
|
||||
default: false
|
||||
bazGroup:
|
||||
type: 'object'
|
||||
collapsed: true
|
||||
properties:
|
||||
baz:
|
||||
title: 'Baz'
|
||||
description: 'The baz setting'
|
||||
type: 'boolean'
|
||||
default: false
|
||||
zing:
|
||||
type: 'string'
|
||||
default: ''
|
||||
atom.config.setSchema('foo', config)
|
||||
expect(_.size(atom.config.get('foo'))).toBe 3
|
||||
settingsPanel = new SettingsPanel({namespace: 'foo', includeTitle: false})
|
||||
|
||||
it 'ensures that only grouped settings have a group title', ->
|
||||
expect(settingsPanel.element.querySelectorAll('.section-container > .section-body')).toHaveLength 1
|
||||
controlGroups = settingsPanel.element.querySelectorAll('.section-body > .control-group')
|
||||
expect(controlGroups).toHaveLength 3
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength 1
|
||||
expect(controlGroups[0].querySelector('.sub-section .sub-section-heading').textContent).toBe 'Bar group'
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section .sub-section-body')).toHaveLength 1
|
||||
subsectionBody = controlGroups[0].querySelector('.sub-section .sub-section-body')
|
||||
expect(subsectionBody.querySelectorAll('.control-group')).toHaveLength 1
|
||||
expect(controlGroups[1].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength 1
|
||||
expect(controlGroups[1].querySelector('.sub-section .sub-section-heading').textContent).toBe 'Baz Group'
|
||||
expect(controlGroups[1].querySelectorAll('.sub-section .sub-section-body')).toHaveLength 1
|
||||
subsectionBody = controlGroups[1].querySelector('.sub-section .sub-section-body')
|
||||
expect(subsectionBody.querySelectorAll('.control-group')).toHaveLength 1
|
||||
expect(controlGroups[2].querySelectorAll('.sub-section')).toHaveLength 0
|
||||
expect(controlGroups[2].querySelectorAll('.sub-section-heading')).toHaveLength 0
|
||||
|
||||
it 'ensures grouped settings are collapsable', ->
|
||||
expect(settingsPanel.element.querySelectorAll('.section-container > .section-body')).toHaveLength 1
|
||||
controlGroups = settingsPanel.element.querySelectorAll('.section-body > .control-group')
|
||||
expect(controlGroups).toHaveLength 3
|
||||
# Bar group
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength 1
|
||||
expect(controlGroups[0].querySelector('.sub-section .sub-section-heading').classList.contains('has-items')).toBe true
|
||||
# Baz Group
|
||||
expect(controlGroups[1].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength 1
|
||||
expect(controlGroups[1].querySelector('.sub-section .sub-section-heading').classList.contains('has-items')).toBe true
|
||||
# Should be already collapsed
|
||||
expect(controlGroups[1].querySelector('.sub-section .sub-section-heading').parentElement.classList.contains('collapsed')).toBe true
|
||||
|
||||
it 'ensures grouped settings can have a description', ->
|
||||
expect(settingsPanel.element.querySelectorAll('.section-container > .section-body')).toHaveLength 1
|
||||
controlGroups = settingsPanel.element.querySelectorAll('.section-body > .control-group')
|
||||
expect(controlGroups).toHaveLength 3
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section > .setting-description')).toHaveLength 1
|
||||
expect(controlGroups[0].querySelector('.sub-section > .setting-description').textContent).toBe 'description of bar group'
|
||||
|
||||
describe 'settings validation', ->
|
||||
beforeEach ->
|
||||
config =
|
||||
type: 'object'
|
||||
properties:
|
||||
minMax:
|
||||
name: 'minMax'
|
||||
title: 'Min max'
|
||||
description: 'The minMax setting'
|
||||
type: 'integer'
|
||||
default: 10
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
commaValueArray:
|
||||
name: 'commaValueArray'
|
||||
title: 'Comma value in array'
|
||||
description: 'An array with a comma value'
|
||||
type: 'array'
|
||||
default: []
|
||||
|
||||
atom.config.setSchema('foo', config)
|
||||
settingsPanel = new SettingsPanel({namespace: 'foo', includeTitle: false})
|
||||
|
||||
it 'prevents setting a value below the minimum', ->
|
||||
minMaxEditor = settingsPanel.element.querySelector('[id="foo.minMax"]')
|
||||
minMaxEditor.getModel().setText('0')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '1'
|
||||
|
||||
minMaxEditor.getModel().setText('-5')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '1'
|
||||
|
||||
it 'prevents setting a value above the maximum', ->
|
||||
minMaxEditor = settingsPanel.element.querySelector('[id="foo.minMax"]')
|
||||
minMaxEditor.getModel().setText('1000')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '100'
|
||||
|
||||
minMaxEditor.getModel().setText('10000')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '100'
|
||||
|
||||
it 'prevents setting a value that cannot be coerced to the correct type', ->
|
||||
minMaxEditor = settingsPanel.element.querySelector('[id="foo.minMax"]')
|
||||
minMaxEditor.getModel().setText('"abcde"')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '' # aka default
|
||||
|
||||
minMaxEditor.getModel().setText('15')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '15'
|
||||
|
||||
minMaxEditor.getModel().setText('"abcde"')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '15'
|
||||
|
||||
it 'allows setting a valid scoped value', ->
|
||||
settingsPanel = new SettingsPanel({namespace: 'foo', includeTitle: false, scopeName: 'source.js'})
|
||||
minMaxEditor = settingsPanel.element.querySelector('atom-text-editor')
|
||||
minMaxEditor.getModel().setText('15')
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(minMaxEditor.getModel().getText()).toBe '15'
|
||||
|
||||
describe 'commaValueArray', ->
|
||||
it 'comma in value is escaped', ->
|
||||
commaValueArrayEditor = settingsPanel.element.querySelector('[id="foo.commaValueArray"]')
|
||||
commaValueArrayEditor.getModel().setText('1, \\,, 2')
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(atom.config.get("foo.commaValueArray")).toEqual ['1', ',', '2']
|
||||
|
||||
commaValueArrayEditor.getModel().setText('1\\, 2')
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(atom.config.get('foo.commaValueArray')).toEqual ['1, 2']
|
||||
|
||||
commaValueArrayEditor.getModel().setText('1\\,')
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(atom.config.get('foo.commaValueArray')).toEqual ['1,']
|
||||
|
||||
commaValueArrayEditor.getModel().setText('\\, 2')
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay())
|
||||
expect(atom.config.get('foo.commaValueArray')).toEqual [', 2']
|
||||
|
||||
it 'renders an escaped comma', ->
|
||||
commaValueArrayEditor = settingsPanel.element.querySelector('[id="foo.commaValueArray"]')
|
||||
atom.config.set('foo.commaValueArray', ['3', ',', '4'])
|
||||
advanceClock(1000)
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe '3, \\,, 4'
|
||||
|
||||
atom.config.set('foo.commaValueArray', ['3, 4'])
|
||||
advanceClock(1000)
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe '3\\, 4'
|
||||
|
||||
atom.config.set('foo.commaValueArray', ['3,'])
|
||||
advanceClock(1000)
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe '3\\,'
|
||||
|
||||
atom.config.set('foo.commaValueArray', [', 4'])
|
||||
advanceClock(1000)
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe '\\, 4'
|
468
packages/settings-view/spec/settings-panel-spec.js
Normal file
468
packages/settings-view/spec/settings-panel-spec.js
Normal file
@ -0,0 +1,468 @@
|
||||
|
||||
const SettingsPanel = require('../lib/settings-panel');
|
||||
const _ = require('underscore-plus');
|
||||
|
||||
describe("SettingsPanel", () => {
|
||||
let settingsPanel = null;
|
||||
|
||||
describe("sorted settings", () => {
|
||||
beforeEach(() => {
|
||||
const config = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
bar: {
|
||||
title: 'Bar',
|
||||
description: 'The bar setting',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
haz: {
|
||||
title: 'Haz',
|
||||
description: 'The haz setting',
|
||||
type: 'string',
|
||||
default: 'haz'
|
||||
},
|
||||
zing: {
|
||||
title: 'Zing',
|
||||
description: 'The zing setting',
|
||||
type: 'string',
|
||||
default: 'zing',
|
||||
order: 1
|
||||
},
|
||||
zang: {
|
||||
title: 'Zang',
|
||||
description: 'The baz setting',
|
||||
type: 'string',
|
||||
default: 'zang',
|
||||
order: 100
|
||||
},
|
||||
enum: {
|
||||
title: 'An enum',
|
||||
type: 'string',
|
||||
default: 'one',
|
||||
enum: [
|
||||
{value: 'one', description: 'One'},
|
||||
'Two'
|
||||
]
|
||||
},
|
||||
radio: {
|
||||
title: 'An enum with radio buttons',
|
||||
radio: true,
|
||||
type: 'string',
|
||||
default: 'Two',
|
||||
enum: [
|
||||
{value: 'one', description: 'One'},
|
||||
'Two'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
atom.config.setSchema("foo", config);
|
||||
atom.config.setDefaults("foo", {gong: 'gong'});
|
||||
expect(_.size(atom.config.get('foo'))).toBe(7);
|
||||
settingsPanel = new SettingsPanel({namespace: "foo", includeTitle: false});
|
||||
});
|
||||
|
||||
it("sorts settings by order and then alphabetically by the key", () => {
|
||||
const settings = atom.config.get('foo');
|
||||
expect(_.size(settings)).toBe(7);
|
||||
const sortedSettings = settingsPanel.sortSettings("foo", settings);
|
||||
expect(sortedSettings[0]).toBe('zing');
|
||||
expect(sortedSettings[1]).toBe('zang');
|
||||
expect(sortedSettings[2]).toBe('bar');
|
||||
expect(sortedSettings[3]).toBe('enum');
|
||||
expect(sortedSettings[4]).toBe('gong');
|
||||
expect(sortedSettings[5]).toBe('haz');
|
||||
expect(sortedSettings[6]).toBe('radio');
|
||||
});
|
||||
|
||||
it("gracefully deals with a null settings object", () => {
|
||||
const sortedSettings = settingsPanel.sortSettings("foo", null);
|
||||
expect(sortedSettings).not.toBeNull;
|
||||
expect(_.size(sortedSettings)).toBe(0);
|
||||
});
|
||||
|
||||
it("presents enum options with their descriptions", () => {
|
||||
const select = settingsPanel.element.querySelector('#foo\\.enum');
|
||||
const pairs = (Array.from(select.children).map((opt) => [opt.value, opt.innerText]));
|
||||
expect(pairs).toEqual([['one', 'One'], ['Two', 'Two']]);
|
||||
});
|
||||
|
||||
it("presents radio options with their descriptions", () => {
|
||||
const radio = settingsPanel.element.querySelector('#foo\\.radio');
|
||||
const options = (() => {
|
||||
const result = [];
|
||||
for (let label of Array.from(radio.querySelectorAll('label'))) {
|
||||
const button = label.querySelector('input[type=radio][name="foo.radio"]');
|
||||
result.push([button.id, button.value, label.innerText]);
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
expect(options).toEqual([['foo.radio[one]', 'one', 'One'], ['foo.radio[Two]', 'Two', 'Two']]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('default settings', () => {
|
||||
beforeEach(() => {
|
||||
const config = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
haz: {
|
||||
name: 'haz',
|
||||
title: 'Haz',
|
||||
description: 'The haz setting',
|
||||
type: 'string',
|
||||
default: 'haz'
|
||||
},
|
||||
qux: {
|
||||
name: 'qux',
|
||||
title: 'Qux',
|
||||
description: 'The qux setting',
|
||||
type: 'string',
|
||||
default: 'a',
|
||||
enum: [
|
||||
{value: 'a', description: 'Alice'},
|
||||
{value: 'b', description: 'Bob'}
|
||||
]
|
||||
},
|
||||
testZero: {
|
||||
name: 'testZero',
|
||||
title: 'Test Zero',
|
||||
description: 'Setting for testing zero as a default',
|
||||
type: 'integer',
|
||||
default: 0
|
||||
},
|
||||
radio: {
|
||||
title: 'An enum with radio buttons',
|
||||
radio: true,
|
||||
type: 'string',
|
||||
default: 'Two',
|
||||
enum: [
|
||||
{value: 'one', description: 'One'},
|
||||
'Two',
|
||||
'Three'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
atom.config.setSchema("foo", config);
|
||||
atom.config.setDefaults("foo", {gong: 'gong'});
|
||||
expect(_.size(atom.config.get('foo'))).toBe(5);
|
||||
settingsPanel = new SettingsPanel({namespace: "foo", includeTitle: false});
|
||||
});
|
||||
|
||||
it('ensures default stays default', () => {
|
||||
expect(settingsPanel.getDefault('foo.haz')).toBe('haz');
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe(true);
|
||||
settingsPanel.set('foo.haz', 'haz');
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe(true);
|
||||
});
|
||||
|
||||
it('can be overwritten', () => {
|
||||
expect(settingsPanel.getDefault('foo.haz')).toBe('haz');
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe(true);
|
||||
settingsPanel.set('foo.haz', 'newhaz');
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe(false);
|
||||
expect(atom.config.get('foo.haz')).toBe('newhaz');
|
||||
});
|
||||
|
||||
it("ignores project-specific overrides", () => {
|
||||
atom.project.replace({
|
||||
originPath: 'TEST',
|
||||
config: {
|
||||
foo: {
|
||||
haz: 'newhaz'
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(settingsPanel.isDefault('foo.haz')).toBe(true);
|
||||
expect(atom.config.get('foo.haz')).toBe('newhaz');
|
||||
});
|
||||
|
||||
it('has a tooltip showing the default value', () => {
|
||||
const hazEditor = settingsPanel.element.querySelector('[id="foo.haz"]');
|
||||
const tooltips = atom.tooltips.findTooltips(hazEditor);
|
||||
expect(tooltips).toHaveLength(1);
|
||||
const {
|
||||
title
|
||||
} = tooltips[0].options;
|
||||
expect(title).toBe("Default: haz");
|
||||
});
|
||||
|
||||
it('has a tooltip showing the description of the default value', () => {
|
||||
const quxEditor = settingsPanel.element.querySelector('[id="foo.qux"]');
|
||||
const tooltips = atom.tooltips.findTooltips(quxEditor);
|
||||
expect(tooltips).toHaveLength(1);
|
||||
const {
|
||||
title
|
||||
} = tooltips[0].options;
|
||||
expect(title).toBe("Default: Alice");
|
||||
});
|
||||
|
||||
// Regression test for #783
|
||||
it('allows 0 to be a default', () => {
|
||||
const zeroEditor = settingsPanel.element.querySelector('[id="foo.testZero"]');
|
||||
expect(zeroEditor.getModel().getText()).toBe('');
|
||||
expect(zeroEditor.getModel().getPlaceholderText()).toBe('Default: 0');
|
||||
|
||||
expect(settingsPanel.getDefault('foo.testZero')).toBe(0);
|
||||
expect(settingsPanel.isDefault('foo.testZero')).toBe(true);
|
||||
|
||||
settingsPanel.set('foo.testZero', 15);
|
||||
expect(settingsPanel.isDefault('foo.testZero')).toBe(false);
|
||||
|
||||
settingsPanel.set('foo.testZero', 0);
|
||||
expect(settingsPanel.isDefault('foo.testZero')).toBe(true);
|
||||
});
|
||||
|
||||
it("selects the default choice for radio options", () => {
|
||||
expect(settingsPanel.getDefault('foo.radio')).toBe('Two');
|
||||
settingsPanel.set('foo.radio', 'Two');
|
||||
expect(settingsPanel.element.querySelector('#foo\\.radio\\[Two\\]')).toBeChecked();
|
||||
});
|
||||
|
||||
describe('scoped settings', () => {
|
||||
beforeEach(() => {
|
||||
const schema = {
|
||||
scopes: {
|
||||
'.source.python': {
|
||||
default: 4
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
atom.config.setScopedDefaultsFromSchema('editor.tabLength', schema);
|
||||
expect(atom.config.get('editor.tabLength')).toBe(2);
|
||||
});
|
||||
|
||||
it('displays the scoped default', () => {
|
||||
settingsPanel = new SettingsPanel({namespace: "editor", includeTitle: false, scopeName: '.source.python'});
|
||||
const tabLengthEditor = settingsPanel.element.querySelector('[id="editor.tabLength"]');
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('');
|
||||
expect(tabLengthEditor.getModel().getPlaceholderText()).toBe('Default: 4');
|
||||
});
|
||||
|
||||
it('allows the scoped setting to be changed to its normal default if the unscoped value is different', () => {
|
||||
atom.config.set('editor.tabLength', 8);
|
||||
|
||||
settingsPanel = new SettingsPanel({namespace: "editor", includeTitle: false, scopeName: '.source.js'});
|
||||
const tabLengthEditor = settingsPanel.element.querySelector('[id="editor.tabLength"]');
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('');
|
||||
expect(tabLengthEditor.getModel().getPlaceholderText()).toBe('Default: 8');
|
||||
|
||||
// This is the unscoped default, but it differs from the current unscoped value
|
||||
settingsPanel.set('editor.tabLength', 2);
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('2');
|
||||
expect(atom.config.get('editor.tabLength', {scope: ['source.js']})).toBe(2);
|
||||
});
|
||||
|
||||
it('allows the scoped setting to be changed to the unscoped default if it is different', () => {
|
||||
settingsPanel = new SettingsPanel({namespace: "editor", includeTitle: false, scopeName: '.source.python'});
|
||||
const tabLengthEditor = settingsPanel.element.querySelector('[id="editor.tabLength"]');
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('');
|
||||
expect(tabLengthEditor.getModel().getPlaceholderText()).toBe('Default: 4');
|
||||
|
||||
// This is the unscoped default, but it differs from the scoped default
|
||||
settingsPanel.set('editor.tabLength', 2);
|
||||
expect(tabLengthEditor.getModel().getText()).toBe('2');
|
||||
expect(atom.config.get('editor.tabLength', {scope: ['source.python']})).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('grouped settings', () => {
|
||||
beforeEach(() => {
|
||||
const config = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
barGroup: {
|
||||
type: 'object',
|
||||
title: 'Bar group',
|
||||
description: 'description of bar group',
|
||||
properties: {
|
||||
bar: {
|
||||
title: 'Bar',
|
||||
description: 'The bar setting',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}
|
||||
}
|
||||
},
|
||||
bazGroup: {
|
||||
type: 'object',
|
||||
collapsed: true,
|
||||
properties: {
|
||||
baz: {
|
||||
title: 'Baz',
|
||||
description: 'The baz setting',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}
|
||||
}
|
||||
},
|
||||
zing: {
|
||||
type: 'string',
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
};
|
||||
atom.config.setSchema('foo', config);
|
||||
expect(_.size(atom.config.get('foo'))).toBe(3);
|
||||
settingsPanel = new SettingsPanel({namespace: 'foo', includeTitle: false});
|
||||
});
|
||||
|
||||
it('ensures that only grouped settings have a group title', () => {
|
||||
expect(settingsPanel.element.querySelectorAll('.section-container > .section-body')).toHaveLength(1);
|
||||
const controlGroups = settingsPanel.element.querySelectorAll('.section-body > .control-group');
|
||||
expect(controlGroups).toHaveLength(3);
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength(1);
|
||||
expect(controlGroups[0].querySelector('.sub-section .sub-section-heading').textContent).toBe('Bar group');
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section .sub-section-body')).toHaveLength(1);
|
||||
let subsectionBody = controlGroups[0].querySelector('.sub-section .sub-section-body');
|
||||
expect(subsectionBody.querySelectorAll('.control-group')).toHaveLength(1);
|
||||
expect(controlGroups[1].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength(1);
|
||||
expect(controlGroups[1].querySelector('.sub-section .sub-section-heading').textContent).toBe('Baz Group');
|
||||
expect(controlGroups[1].querySelectorAll('.sub-section .sub-section-body')).toHaveLength(1);
|
||||
subsectionBody = controlGroups[1].querySelector('.sub-section .sub-section-body');
|
||||
expect(subsectionBody.querySelectorAll('.control-group')).toHaveLength(1);
|
||||
expect(controlGroups[2].querySelectorAll('.sub-section')).toHaveLength(0);
|
||||
expect(controlGroups[2].querySelectorAll('.sub-section-heading')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('ensures grouped settings are collapsable', () => {
|
||||
expect(settingsPanel.element.querySelectorAll('.section-container > .section-body')).toHaveLength(1);
|
||||
const controlGroups = settingsPanel.element.querySelectorAll('.section-body > .control-group');
|
||||
expect(controlGroups).toHaveLength(3);
|
||||
// Bar group
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength(1);
|
||||
expect(controlGroups[0].querySelector('.sub-section .sub-section-heading').classList.contains('has-items')).toBe(true);
|
||||
// Baz Group
|
||||
expect(controlGroups[1].querySelectorAll('.sub-section .sub-section-heading')).toHaveLength(1);
|
||||
expect(controlGroups[1].querySelector('.sub-section .sub-section-heading').classList.contains('has-items')).toBe(true);
|
||||
// Should be already collapsed
|
||||
expect(controlGroups[1].querySelector('.sub-section .sub-section-heading').parentElement.classList.contains('collapsed')).toBe(true);
|
||||
});
|
||||
|
||||
it('ensures grouped settings can have a description', () => {
|
||||
expect(settingsPanel.element.querySelectorAll('.section-container > .section-body')).toHaveLength(1);
|
||||
const controlGroups = settingsPanel.element.querySelectorAll('.section-body > .control-group');
|
||||
expect(controlGroups).toHaveLength(3);
|
||||
expect(controlGroups[0].querySelectorAll('.sub-section > .setting-description')).toHaveLength(1);
|
||||
expect(controlGroups[0].querySelector('.sub-section > .setting-description').textContent).toBe('description of bar group');
|
||||
});
|
||||
});
|
||||
|
||||
describe('settings validation', () => {
|
||||
beforeEach(() => {
|
||||
const config = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
minMax: {
|
||||
name: 'minMax',
|
||||
title: 'Min max',
|
||||
description: 'The minMax setting',
|
||||
type: 'integer',
|
||||
default: 10,
|
||||
minimum: 1,
|
||||
maximum: 100
|
||||
},
|
||||
commaValueArray: {
|
||||
name: 'commaValueArray',
|
||||
title: 'Comma value in array',
|
||||
description: 'An array with a comma value',
|
||||
type: 'array',
|
||||
default: []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
atom.config.setSchema('foo', config);
|
||||
settingsPanel = new SettingsPanel({namespace: 'foo', includeTitle: false});
|
||||
});
|
||||
|
||||
it('prevents setting a value below the minimum', () => {
|
||||
const minMaxEditor = settingsPanel.element.querySelector('[id="foo.minMax"]');
|
||||
minMaxEditor.getModel().setText('0');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe('1');
|
||||
|
||||
minMaxEditor.getModel().setText('-5');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe('1');
|
||||
});
|
||||
|
||||
it('prevents setting a value above the maximum', () => {
|
||||
const minMaxEditor = settingsPanel.element.querySelector('[id="foo.minMax"]');
|
||||
minMaxEditor.getModel().setText('1000');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe('100');
|
||||
|
||||
minMaxEditor.getModel().setText('10000');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe('100');
|
||||
});
|
||||
|
||||
it('prevents setting a value that cannot be coerced to the correct type', () => {
|
||||
const minMaxEditor = settingsPanel.element.querySelector('[id="foo.minMax"]');
|
||||
minMaxEditor.getModel().setText('"abcde"');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe(''); // aka default
|
||||
|
||||
minMaxEditor.getModel().setText('15');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe('15');
|
||||
|
||||
minMaxEditor.getModel().setText('"abcde"');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe('15');
|
||||
});
|
||||
|
||||
it('allows setting a valid scoped value', () => {
|
||||
settingsPanel = new SettingsPanel({namespace: 'foo', includeTitle: false, scopeName: 'source.js'});
|
||||
const minMaxEditor = settingsPanel.element.querySelector('atom-text-editor');
|
||||
minMaxEditor.getModel().setText('15');
|
||||
advanceClock(minMaxEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(minMaxEditor.getModel().getText()).toBe('15');
|
||||
});
|
||||
|
||||
describe('commaValueArray', () => {
|
||||
it('comma in value is escaped', () => {
|
||||
const commaValueArrayEditor = settingsPanel.element.querySelector('[id="foo.commaValueArray"]');
|
||||
commaValueArrayEditor.getModel().setText('1, \\,, 2');
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(atom.config.get("foo.commaValueArray")).toEqual(['1', ',', '2']);
|
||||
|
||||
commaValueArrayEditor.getModel().setText('1\\, 2');
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(atom.config.get('foo.commaValueArray')).toEqual(['1, 2']);
|
||||
|
||||
commaValueArrayEditor.getModel().setText('1\\,');
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(atom.config.get('foo.commaValueArray')).toEqual(['1,']);
|
||||
|
||||
commaValueArrayEditor.getModel().setText('\\, 2');
|
||||
advanceClock(commaValueArrayEditor.getModel().getBuffer().getStoppedChangingDelay());
|
||||
expect(atom.config.get('foo.commaValueArray')).toEqual([', 2']);
|
||||
});
|
||||
|
||||
it('renders an escaped comma', () => {
|
||||
const commaValueArrayEditor = settingsPanel.element.querySelector('[id="foo.commaValueArray"]');
|
||||
atom.config.set('foo.commaValueArray', ['3', ',', '4']);
|
||||
advanceClock(1000);
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe('3, \\,, 4');
|
||||
|
||||
atom.config.set('foo.commaValueArray', ['3, 4']);
|
||||
advanceClock(1000);
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe('3\\, 4');
|
||||
|
||||
atom.config.set('foo.commaValueArray', ['3,']);
|
||||
advanceClock(1000);
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe('3\\,');
|
||||
|
||||
atom.config.set('foo.commaValueArray', [', 4']);
|
||||
advanceClock(1000);
|
||||
expect(commaValueArrayEditor.getModel().getText()).toBe('\\, 4');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,519 +0,0 @@
|
||||
path = require 'path'
|
||||
main = require '../lib/main'
|
||||
PackageManager = require '../lib/package-manager'
|
||||
SettingsView = require '../lib/settings-view'
|
||||
SnippetsProvider =
|
||||
getSnippets: -> {}
|
||||
|
||||
describe "SettingsView", ->
|
||||
settingsView = null
|
||||
packageManager = new PackageManager()
|
||||
|
||||
beforeEach ->
|
||||
settingsView = main.createSettingsView({packageManager: packageManager, snippetsProvider: SnippetsProvider})
|
||||
spyOn(settingsView, "initializePanels").andCallThrough()
|
||||
window.advanceClock(10000)
|
||||
waitsFor ->
|
||||
settingsView.initializePanels.callCount > 0
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers which panel was visible", ->
|
||||
settingsView.showPanel('Themes')
|
||||
newSettingsView = main.createSettingsView(settingsView.serialize())
|
||||
settingsView.destroy()
|
||||
jasmine.attachToDOM(newSettingsView.element)
|
||||
newSettingsView.initializePanels()
|
||||
expect(newSettingsView.activePanel).toEqual {name: 'Themes', options: {}}
|
||||
|
||||
it "shows the previously active panel if it is added after deserialization", ->
|
||||
settingsView.addCorePanel('Panel 1', 'panel-1', ->
|
||||
div = document.createElement('div')
|
||||
div.id = 'panel-1'
|
||||
{
|
||||
element: div,
|
||||
show: -> div.style.display = '',
|
||||
focus: -> div.focus(),
|
||||
destroy: -> div.remove()
|
||||
}
|
||||
)
|
||||
settingsView.showPanel('Panel 1')
|
||||
newSettingsView = main.createSettingsView(settingsView.serialize())
|
||||
newSettingsView.addPanel('Panel 1', ->
|
||||
div = document.createElement('div')
|
||||
div.id = 'panel-1'
|
||||
{
|
||||
element: div,
|
||||
show: -> div.style.display = '',
|
||||
focus: -> div.focus(),
|
||||
destroy: -> div.remove()
|
||||
}
|
||||
)
|
||||
newSettingsView.initializePanels()
|
||||
jasmine.attachToDOM(newSettingsView.element)
|
||||
expect(newSettingsView.activePanel).toEqual {name: 'Panel 1', options: {}}
|
||||
|
||||
it "shows the Settings panel if the last saved active panel name no longer exists", ->
|
||||
settingsView.addCorePanel('Panel 1', 'panel1', ->
|
||||
div = document.createElement('div')
|
||||
div.id = 'panel-1'
|
||||
{
|
||||
element: div,
|
||||
show: -> div.style.display = '',
|
||||
focus: -> div.focus(),
|
||||
destroy: -> div.remove()
|
||||
}
|
||||
)
|
||||
settingsView.showPanel('Panel 1')
|
||||
newSettingsView = main.createSettingsView(settingsView.serialize())
|
||||
settingsView.destroy()
|
||||
jasmine.attachToDOM(newSettingsView.element)
|
||||
newSettingsView.initializePanels()
|
||||
expect(newSettingsView.activePanel).toEqual {name: 'Core', options: {}}
|
||||
|
||||
it "serializes the active panel name even when the panels were never initialized", ->
|
||||
settingsView.showPanel('Themes')
|
||||
settingsView2 = main.createSettingsView(settingsView.serialize())
|
||||
settingsView3 = main.createSettingsView(settingsView2.serialize())
|
||||
jasmine.attachToDOM(settingsView3.element)
|
||||
settingsView3.initializePanels()
|
||||
expect(settingsView3.activePanel).toEqual {name: 'Themes', options: {}}
|
||||
|
||||
describe ".addCorePanel(name, iconName, view)", ->
|
||||
it "adds a menu entry to the left and a panel that can be activated by clicking it", ->
|
||||
settingsView.addCorePanel('Panel 1', 'panel1', ->
|
||||
div = document.createElement('div')
|
||||
div.id = 'panel-1'
|
||||
{
|
||||
element: div,
|
||||
show: -> div.style.display = '',
|
||||
focus: -> div.focus(),
|
||||
destroy: -> div.remove()
|
||||
}
|
||||
)
|
||||
settingsView.addCorePanel('Panel 2', 'panel2', ->
|
||||
div = document.createElement('div')
|
||||
div.id = 'panel-2'
|
||||
{
|
||||
element: div,
|
||||
show: -> div.style.display = '',
|
||||
focus: -> div.focus(),
|
||||
destroy: -> div.remove()
|
||||
}
|
||||
)
|
||||
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 1"]')).toExist()
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 2"]')).toExist()
|
||||
#expect(settingsView.refs.panelMenu.children[1]).toHaveClass 'active' # TODO FIX
|
||||
|
||||
jasmine.attachToDOM(settingsView.element)
|
||||
settingsView.refs.panelMenu.querySelector('li[name="Panel 1"] a').click()
|
||||
expect(settingsView.refs.panelMenu.querySelectorAll('.active').length).toBe 1
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 1"]')).toHaveClass('active')
|
||||
expect(settingsView.refs.panels.querySelector('#panel-1')).toBeVisible()
|
||||
expect(settingsView.refs.panels.querySelector('#panel-2')).not.toExist()
|
||||
settingsView.refs.panelMenu.querySelector('li[name="Panel 2"] a').click()
|
||||
expect(settingsView.refs.panelMenu.querySelectorAll('.active').length).toBe 1
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 2"]')).toHaveClass('active')
|
||||
expect(settingsView.refs.panels.querySelector('#panel-1')).toBeHidden()
|
||||
expect(settingsView.refs.panels.querySelector('#panel-2')).toBeVisible()
|
||||
|
||||
describe "when the package is activated", ->
|
||||
openWithCommand = (command) ->
|
||||
waitsFor (done) ->
|
||||
openSubscription = atom.workspace.onDidOpen ->
|
||||
openSubscription.dispose()
|
||||
done()
|
||||
atom.commands.dispatch(atom.views.getView(atom.workspace), command)
|
||||
|
||||
beforeEach ->
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace))
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('settings-view')
|
||||
|
||||
describe "when the settings view is opened with a settings-view:* command", ->
|
||||
beforeEach ->
|
||||
settingsView = null
|
||||
|
||||
describe "settings-view:open", ->
|
||||
it "opens the settings view", ->
|
||||
openWithCommand('settings-view:open')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Core', options: {}
|
||||
|
||||
it "always open existing item in workspace", ->
|
||||
center = atom.workspace.getCenter()
|
||||
[pane1, pane2] = []
|
||||
|
||||
waitsForPromise -> atom.workspace.open(null, split: 'right')
|
||||
runs ->
|
||||
expect(center.getPanes()).toHaveLength(2)
|
||||
[pane1, pane2] = center.getPanes()
|
||||
expect(atom.workspace.getActivePane()).toBe(pane2)
|
||||
|
||||
openWithCommand('settings-view:open')
|
||||
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel).toEqual name: 'Core', options: {}
|
||||
expect(atom.workspace.getActivePane()).toBe(pane2)
|
||||
|
||||
runs ->
|
||||
pane1.activate()
|
||||
|
||||
openWithCommand('settings-view:open')
|
||||
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel).toEqual name: 'Core', options: {}
|
||||
expect(atom.workspace.getActivePane()).toBe(pane2)
|
||||
|
||||
describe "settings-view:core", ->
|
||||
it "opens the core settings view", ->
|
||||
openWithCommand('settings-view:editor')
|
||||
runs ->
|
||||
openWithCommand('settings-view:core')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Core', options: uri: 'atom://config/core'
|
||||
|
||||
describe "settings-view:editor", ->
|
||||
it "opens the editor settings view", ->
|
||||
openWithCommand('settings-view:editor')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Editor', options: uri: 'atom://config/editor'
|
||||
|
||||
describe "settings-view:show-keybindings", ->
|
||||
it "opens the settings view to the keybindings page", ->
|
||||
openWithCommand('settings-view:show-keybindings')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Keybindings', options: uri: 'atom://config/keybindings'
|
||||
|
||||
describe "settings-view:change-themes", ->
|
||||
it "opens the settings view to the themes page", ->
|
||||
openWithCommand('settings-view:change-themes')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Themes', options: uri: 'atom://config/themes'
|
||||
|
||||
describe "settings-view:uninstall-themes", ->
|
||||
it "opens the settings view to the themes page", ->
|
||||
openWithCommand('settings-view:uninstall-themes')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Themes', options: uri: 'atom://config/themes'
|
||||
|
||||
describe "settings-view:uninstall-packages", ->
|
||||
it "opens the settings view to the install page", ->
|
||||
openWithCommand('settings-view:uninstall-packages')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Packages', options: uri: 'atom://config/packages'
|
||||
|
||||
describe "settings-view:install-packages-and-themes", ->
|
||||
it "opens the settings view to the install page", ->
|
||||
openWithCommand('settings-view:install-packages-and-themes')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Install', options: uri: 'atom://config/install'
|
||||
|
||||
describe "settings-view:check-for-package-updates", ->
|
||||
it "opens the settings view to the install page", ->
|
||||
openWithCommand('settings-view:check-for-package-updates')
|
||||
runs ->
|
||||
expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual name: 'Updates', options: uri: 'atom://config/updates'
|
||||
|
||||
describe "when atom.workspace.open() is used with a config URI", ->
|
||||
focusIsWithinActivePanel = ->
|
||||
activePanel = settingsView.panelsByName[settingsView.activePanel.name]
|
||||
activePanel.element is document.activeElement or activePanel.element.contains(document.activeElement)
|
||||
|
||||
expectActivePanelToBeKeyboardScrollable = ->
|
||||
activePanel = settingsView.panelsByName[settingsView.activePanel.name]
|
||||
spyOn(activePanel, 'pageDown')
|
||||
atom.commands.dispatch(activePanel.element, 'core:page-down')
|
||||
expect(activePanel.pageDown).toHaveBeenCalled()
|
||||
spyOn(activePanel, 'pageUp')
|
||||
atom.commands.dispatch(activePanel.element, 'core:page-up')
|
||||
expect(activePanel.pageUp).toHaveBeenCalled()
|
||||
|
||||
beforeEach ->
|
||||
settingsView = null
|
||||
|
||||
it "opens the settings to the correct panel with atom://config/<panel-name> and that panel is keyboard-scrollable", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config').then (s) -> settingsView = s
|
||||
|
||||
waitsFor (done) -> process.nextTick(done)
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Core', options: {}
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/editor').then (s) -> settingsView = s
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Editor', options: uri: 'atom://config/editor'
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/keybindings').then (s) -> settingsView = s
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Keybindings', options: uri: 'atom://config/keybindings'
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/packages').then (s) -> settingsView = s
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Packages', options: uri: 'atom://config/packages'
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/themes').then (s) -> settingsView = s
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Themes', options: uri: 'atom://config/themes'
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/updates').then (s) -> settingsView = s
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Updates', options: uri: 'atom://config/updates'
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/install').then (s) -> settingsView = s
|
||||
|
||||
hasSystemPanel = false
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Install', options: uri: 'atom://config/install'
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
hasSystemPanel = settingsView.panelsByName['System']?
|
||||
|
||||
if hasSystemPanel
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/system').then (s) -> settingsView = s
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'System', options: uri: 'atom://config/system'
|
||||
expect(focusIsWithinActivePanel()).toBe true
|
||||
expectActivePanelToBeKeyboardScrollable()
|
||||
|
||||
it "opens the package settings view with atom://config/packages/<package-name>", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-readme'))
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/packages/package-with-readme').then (s) -> settingsView = s
|
||||
|
||||
waitsFor (done) -> process.nextTick(done)
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'package-with-readme', options: {
|
||||
uri: 'atom://config/packages/package-with-readme',
|
||||
pack:
|
||||
name: 'package-with-readme'
|
||||
metadata:
|
||||
name: 'package-with-readme'
|
||||
back: 'Packages'
|
||||
}
|
||||
|
||||
it "doesn't use cached package detail when package re-activated and opnes the package view with atom://config/packages/<package-name>", ->
|
||||
[detailInitial, detailAfterReactivate] = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activate()
|
||||
new Promise (resolve) -> atom.packages.onDidActivateInitialPackages(resolve)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-readme'))
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/packages/package-with-readme').then (s) -> settingsView = s
|
||||
|
||||
waitsFor (done) -> process.nextTick(done)
|
||||
|
||||
runs ->
|
||||
detailInitial = settingsView.getOrCreatePanel('package-with-readme')
|
||||
expect(settingsView.getOrCreatePanel('package-with-readme')).toBe detailInitial
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.deactivatePackage('package-with-readme')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-readme'))
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/packages/package-with-readme')
|
||||
|
||||
runs ->
|
||||
detailAfterReactivate = settingsView.getOrCreatePanel('package-with-readme')
|
||||
expect(settingsView.getOrCreatePanel('package-with-readme')).toBe detailAfterReactivate
|
||||
expect(detailInitial).toBeTruthy()
|
||||
expect(detailAfterReactivate).toBeTruthy()
|
||||
expect(detailInitial).not.toBe(detailAfterReactivate)
|
||||
|
||||
it "passes the URI to a pane's beforeShow() method on settings view initialization", ->
|
||||
InstallPanel = require '../lib/install-panel'
|
||||
spyOn(InstallPanel::, 'beforeShow')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/install/package:something').then (s) -> settingsView = s
|
||||
|
||||
waitsFor ->
|
||||
settingsView.activePanel?
|
||||
, 'The activePanel should be set', 5000
|
||||
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Install', options: uri: 'atom://config/install/package:something'
|
||||
expect(InstallPanel::beforeShow).toHaveBeenCalledWith {uri: 'atom://config/install/package:something'}
|
||||
|
||||
it "passes the URI to a pane's beforeShow() method after initialization", ->
|
||||
InstallPanel = require '../lib/install-panel'
|
||||
spyOn(InstallPanel::, 'beforeShow')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config').then (s) -> settingsView = s
|
||||
|
||||
waitsFor (done) -> process.nextTick(done)
|
||||
|
||||
runs ->
|
||||
expect(settingsView.activePanel).toEqual {name: 'Core', options: {}}
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('atom://config/install/package:something').then (s) -> settingsView = s
|
||||
|
||||
waits 1
|
||||
runs ->
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual name: 'Install', options: uri: 'atom://config/install/package:something'
|
||||
expect(InstallPanel::beforeShow).toHaveBeenCalledWith {uri: 'atom://config/install/package:something'}
|
||||
|
||||
describe "when the package is then deactivated", ->
|
||||
beforeEach ->
|
||||
settingsView = null
|
||||
|
||||
it "calls the dispose method on all panels", ->
|
||||
openWithCommand('settings-view:open')
|
||||
|
||||
waitsFor (done) -> process.nextTick(done)
|
||||
|
||||
runs ->
|
||||
settingsView = atom.workspace.getActivePaneItem()
|
||||
panels = [
|
||||
settingsView.getOrCreatePanel('Core')
|
||||
settingsView.getOrCreatePanel('Editor')
|
||||
settingsView.getOrCreatePanel('Keybindings')
|
||||
settingsView.getOrCreatePanel('Packages')
|
||||
settingsView.getOrCreatePanel('Themes')
|
||||
settingsView.getOrCreatePanel('Updates')
|
||||
settingsView.getOrCreatePanel('Install')
|
||||
]
|
||||
systemPanel = settingsView.getOrCreatePanel('System')
|
||||
if systemPanel?
|
||||
panels.push systemPanel
|
||||
for panel in panels
|
||||
if panel.dispose
|
||||
spyOn(panel, 'dispose')
|
||||
else
|
||||
spyOn(panel, 'destroy')
|
||||
|
||||
waitsForPromise ->
|
||||
Promise.resolve(atom.packages.deactivatePackage('settings-view')) # Ensure works on promise and non-promise versions
|
||||
|
||||
runs ->
|
||||
for panel in panels
|
||||
if panel.dispose
|
||||
expect(panel.dispose).toHaveBeenCalled()
|
||||
else
|
||||
expect(panel.destroy).toHaveBeenCalled()
|
||||
|
||||
return
|
||||
|
||||
describe "when an installed package is clicked from the Install panel", ->
|
||||
it "displays the package details", ->
|
||||
waitsFor ->
|
||||
atom.packages.activatePackage('settings-view')
|
||||
|
||||
runs ->
|
||||
settingsView.packageManager.getClient()
|
||||
spyOn(settingsView.packageManager.client, 'featuredPackages').andCallFake (callback) ->
|
||||
callback(null, [{name: 'settings-view'}])
|
||||
settingsView.showPanel('Install')
|
||||
|
||||
waitsFor ->
|
||||
settingsView.element.querySelectorAll('.package-card:not(.hidden)').length > 0
|
||||
|
||||
runs ->
|
||||
settingsView.element.querySelectorAll('.package-card:not(.hidden)')[0].click()
|
||||
|
||||
packageDetail = settingsView.element.querySelector('.package-detail .active')
|
||||
expect(packageDetail.textContent).toBe 'Settings View'
|
||||
|
||||
describe "when the active theme has settings", ->
|
||||
panel = null
|
||||
|
||||
beforeEach ->
|
||||
atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures'))
|
||||
atom.packages.loadPackage('ui-theme-with-config')
|
||||
atom.packages.loadPackage('syntax-theme-with-config')
|
||||
atom.config.set('core.themes', ['ui-theme-with-config', 'syntax-theme-with-config'])
|
||||
|
||||
reloadedHandler = jasmine.createSpy('reloadedHandler')
|
||||
atom.themes.onDidChangeActiveThemes(reloadedHandler)
|
||||
atom.themes.activatePackages()
|
||||
|
||||
waitsFor "themes to be reloaded", ->
|
||||
reloadedHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
settingsView.showPanel('Themes')
|
||||
panel = settingsView.element.querySelector('.themes-panel')
|
||||
|
||||
afterEach ->
|
||||
atom.themes.unwatchUserStylesheet()
|
||||
|
||||
describe "when the UI theme's settings button is clicked", ->
|
||||
it "navigates to that theme's detail view", ->
|
||||
jasmine.attachToDOM(settingsView.element)
|
||||
expect(panel.querySelector('.active-theme-settings')).toBeVisible()
|
||||
|
||||
panel.querySelector('.active-theme-settings').click()
|
||||
packageDetail = settingsView.element.querySelector('.package-detail li.active')
|
||||
expect(packageDetail.textContent).toBe 'Ui Theme With Config'
|
||||
|
||||
describe "when the syntax theme's settings button is clicked", ->
|
||||
it "navigates to that theme's detail view", ->
|
||||
jasmine.attachToDOM(settingsView.element)
|
||||
expect(panel.querySelector('.active-syntax-settings')).toBeVisible()
|
||||
|
||||
panel.querySelector('.active-syntax-settings').click()
|
||||
packageDetail = settingsView.element.querySelector('.package-detail li.active')
|
||||
expect(packageDetail.textContent).toBe 'Syntax Theme With Config'
|
530
packages/settings-view/spec/settings-view-spec.js
Normal file
530
packages/settings-view/spec/settings-view-spec.js
Normal file
@ -0,0 +1,530 @@
|
||||
|
||||
const path = require('path');
|
||||
const main = require('../lib/main');
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const SettingsView = require('../lib/settings-view');
|
||||
const SnippetsProvider =
|
||||
{getSnippets() { return {}; }};
|
||||
|
||||
describe("SettingsView", function() {
|
||||
let settingsView = null;
|
||||
const packageManager = new PackageManager();
|
||||
|
||||
beforeEach(function() {
|
||||
settingsView = main.createSettingsView({packageManager, snippetsProvider: SnippetsProvider});
|
||||
spyOn(settingsView, "initializePanels").andCallThrough();
|
||||
window.advanceClock(10000);
|
||||
waitsFor(() => settingsView.initializePanels.callCount > 0);
|
||||
});
|
||||
|
||||
describe("serialization", function() {
|
||||
it("remembers which panel was visible", function() {
|
||||
settingsView.showPanel('Themes');
|
||||
const newSettingsView = main.createSettingsView(settingsView.serialize());
|
||||
settingsView.destroy();
|
||||
jasmine.attachToDOM(newSettingsView.element);
|
||||
newSettingsView.initializePanels();
|
||||
expect(newSettingsView.activePanel).toEqual({name: 'Themes', options: {}});
|
||||
});
|
||||
|
||||
it("shows the previously active panel if it is added after deserialization", function() {
|
||||
settingsView.addCorePanel('Panel 1', 'panel-1', function() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'panel-1';
|
||||
return {
|
||||
element: div,
|
||||
show() { return div.style.display = ''; },
|
||||
focus() { return div.focus(); },
|
||||
destroy() { return div.remove(); }
|
||||
};
|
||||
});
|
||||
settingsView.showPanel('Panel 1');
|
||||
const newSettingsView = main.createSettingsView(settingsView.serialize());
|
||||
newSettingsView.addPanel('Panel 1', function() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'panel-1';
|
||||
return {
|
||||
element: div,
|
||||
show() { return div.style.display = ''; },
|
||||
focus() { return div.focus(); },
|
||||
destroy() { return div.remove(); }
|
||||
};
|
||||
});
|
||||
newSettingsView.initializePanels();
|
||||
jasmine.attachToDOM(newSettingsView.element);
|
||||
expect(newSettingsView.activePanel).toEqual({name: 'Panel 1', options: {}});
|
||||
});
|
||||
|
||||
it("shows the Settings panel if the last saved active panel name no longer exists", function() {
|
||||
settingsView.addCorePanel('Panel 1', 'panel1', function() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'panel-1';
|
||||
return {
|
||||
element: div,
|
||||
show() { return div.style.display = ''; },
|
||||
focus() { return div.focus(); },
|
||||
destroy() { return div.remove(); }
|
||||
};
|
||||
});
|
||||
settingsView.showPanel('Panel 1');
|
||||
const newSettingsView = main.createSettingsView(settingsView.serialize());
|
||||
settingsView.destroy();
|
||||
jasmine.attachToDOM(newSettingsView.element);
|
||||
newSettingsView.initializePanels();
|
||||
expect(newSettingsView.activePanel).toEqual({name: 'Core', options: {}});
|
||||
});
|
||||
|
||||
it("serializes the active panel name even when the panels were never initialized", function() {
|
||||
settingsView.showPanel('Themes');
|
||||
const settingsView2 = main.createSettingsView(settingsView.serialize());
|
||||
const settingsView3 = main.createSettingsView(settingsView2.serialize());
|
||||
jasmine.attachToDOM(settingsView3.element);
|
||||
settingsView3.initializePanels();
|
||||
expect(settingsView3.activePanel).toEqual({name: 'Themes', options: {}});
|
||||
});
|
||||
});
|
||||
|
||||
describe(".addCorePanel(name, iconName, view)", () => it("adds a menu entry to the left and a panel that can be activated by clicking it", function() {
|
||||
settingsView.addCorePanel('Panel 1', 'panel1', function() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'panel-1';
|
||||
return {
|
||||
element: div,
|
||||
show() { return div.style.display = ''; },
|
||||
focus() { return div.focus(); },
|
||||
destroy() { return div.remove(); }
|
||||
};
|
||||
});
|
||||
settingsView.addCorePanel('Panel 2', 'panel2', function() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'panel-2';
|
||||
return {
|
||||
element: div,
|
||||
show() { return div.style.display = ''; },
|
||||
focus() { return div.focus(); },
|
||||
destroy() { return div.remove(); }
|
||||
};
|
||||
});
|
||||
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 1"]')).toExist();
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 2"]')).toExist();
|
||||
//expect(settingsView.refs.panelMenu.children[1]).toHaveClass 'active' # TODO FIX
|
||||
|
||||
jasmine.attachToDOM(settingsView.element);
|
||||
settingsView.refs.panelMenu.querySelector('li[name="Panel 1"] a').click();
|
||||
expect(settingsView.refs.panelMenu.querySelectorAll('.active').length).toBe(1);
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 1"]')).toHaveClass('active');
|
||||
expect(settingsView.refs.panels.querySelector('#panel-1')).toBeVisible();
|
||||
expect(settingsView.refs.panels.querySelector('#panel-2')).not.toExist();
|
||||
settingsView.refs.panelMenu.querySelector('li[name="Panel 2"] a').click();
|
||||
expect(settingsView.refs.panelMenu.querySelectorAll('.active').length).toBe(1);
|
||||
expect(settingsView.refs.panelMenu.querySelector('li[name="Panel 2"]')).toHaveClass('active');
|
||||
expect(settingsView.refs.panels.querySelector('#panel-1')).toBeHidden();
|
||||
expect(settingsView.refs.panels.querySelector('#panel-2')).toBeVisible();
|
||||
}));
|
||||
|
||||
describe("when the package is activated", function() {
|
||||
const openWithCommand = command => waitsFor(function(done) {
|
||||
var openSubscription = atom.workspace.onDidOpen(function() {
|
||||
openSubscription.dispose();
|
||||
return done();
|
||||
});
|
||||
return atom.commands.dispatch(atom.views.getView(atom.workspace), command);
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace));
|
||||
waitsForPromise(() => atom.packages.activatePackage('settings-view'));
|
||||
});
|
||||
|
||||
describe("when the settings view is opened with a settings-view:* command", function() {
|
||||
beforeEach(() => settingsView = null);
|
||||
|
||||
describe("settings-view:open", function() {
|
||||
it("opens the settings view", function() {
|
||||
openWithCommand('settings-view:open');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Core', options: {}}));
|
||||
});
|
||||
|
||||
it("always open existing item in workspace", function() {
|
||||
const center = atom.workspace.getCenter();
|
||||
let [pane1, pane2] = [];
|
||||
|
||||
waitsForPromise(() => atom.workspace.open(null, {split: 'right'}));
|
||||
runs(function() {
|
||||
expect(center.getPanes()).toHaveLength(2);
|
||||
[pane1, pane2] = center.getPanes();
|
||||
expect(atom.workspace.getActivePane()).toBe(pane2);
|
||||
});
|
||||
|
||||
openWithCommand('settings-view:open');
|
||||
|
||||
runs(function() {
|
||||
expect(atom.workspace.getActivePaneItem().activePanel).toEqual({name: 'Core', options: {}});
|
||||
expect(atom.workspace.getActivePane()).toBe(pane2);
|
||||
});
|
||||
|
||||
runs(() => pane1.activate());
|
||||
|
||||
openWithCommand('settings-view:open');
|
||||
|
||||
runs(function() {
|
||||
expect(atom.workspace.getActivePaneItem().activePanel).toEqual({name: 'Core', options: {}});
|
||||
expect(atom.workspace.getActivePane()).toBe(pane2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("settings-view:core", () => it("opens the core settings view", function() {
|
||||
openWithCommand('settings-view:editor');
|
||||
runs(() => openWithCommand('settings-view:core'));
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Core', options: {uri: 'atom://config/core'}}));
|
||||
}));
|
||||
|
||||
describe("settings-view:editor", () => it("opens the editor settings view", function() {
|
||||
openWithCommand('settings-view:editor');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Editor', options: {uri: 'atom://config/editor'}}));
|
||||
}));
|
||||
|
||||
describe("settings-view:show-keybindings", () => it("opens the settings view to the keybindings page", function() {
|
||||
openWithCommand('settings-view:show-keybindings');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Keybindings', options: {uri: 'atom://config/keybindings'}}));
|
||||
}));
|
||||
|
||||
describe("settings-view:change-themes", () => it("opens the settings view to the themes page", function() {
|
||||
openWithCommand('settings-view:change-themes');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Themes', options: {uri: 'atom://config/themes'}}));
|
||||
}));
|
||||
|
||||
describe("settings-view:uninstall-themes", () => it("opens the settings view to the themes page", function() {
|
||||
openWithCommand('settings-view:uninstall-themes');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Themes', options: {uri: 'atom://config/themes'}}));
|
||||
}));
|
||||
|
||||
describe("settings-view:uninstall-packages", () => it("opens the settings view to the install page", function() {
|
||||
openWithCommand('settings-view:uninstall-packages');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Packages', options: {uri: 'atom://config/packages'}}));
|
||||
}));
|
||||
|
||||
describe("settings-view:install-packages-and-themes", () => it("opens the settings view to the install page", function() {
|
||||
openWithCommand('settings-view:install-packages-and-themes');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Install', options: {uri: 'atom://config/install'}}));
|
||||
}));
|
||||
|
||||
describe("settings-view:check-for-package-updates", () => it("opens the settings view to the install page", function() {
|
||||
openWithCommand('settings-view:check-for-package-updates');
|
||||
runs(() => expect(atom.workspace.getActivePaneItem().activePanel)
|
||||
.toEqual({name: 'Updates', options: {uri: 'atom://config/updates'}}));
|
||||
}));
|
||||
});
|
||||
|
||||
describe("when atom.workspace.open() is used with a config URI", function() {
|
||||
const focusIsWithinActivePanel = function() {
|
||||
const activePanel = settingsView.panelsByName[settingsView.activePanel.name];
|
||||
return (activePanel.element === document.activeElement) || activePanel.element.contains(document.activeElement);
|
||||
};
|
||||
|
||||
const expectActivePanelToBeKeyboardScrollable = function() {
|
||||
const activePanel = settingsView.panelsByName[settingsView.activePanel.name];
|
||||
spyOn(activePanel, 'pageDown');
|
||||
atom.commands.dispatch(activePanel.element, 'core:page-down');
|
||||
expect(activePanel.pageDown).toHaveBeenCalled();
|
||||
spyOn(activePanel, 'pageUp');
|
||||
atom.commands.dispatch(activePanel.element, 'core:page-up');
|
||||
expect(activePanel.pageUp).toHaveBeenCalled();
|
||||
};
|
||||
|
||||
beforeEach(() => settingsView = null);
|
||||
|
||||
it("opens the settings to the correct panel with atom://config/<panel-name> and that panel is keyboard-scrollable", function() {
|
||||
waitsForPromise(() => atom.workspace.open('atom://config').then(s => settingsView = s));
|
||||
|
||||
waitsFor(done => process.nextTick(done));
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Core', options: {}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/editor').then(s => settingsView = s));
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Editor', options: {uri: 'atom://config/editor'}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/keybindings').then(s => settingsView = s));
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Keybindings', options: {uri: 'atom://config/keybindings'}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/packages').then(s => settingsView = s));
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Packages', options: {uri: 'atom://config/packages'}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/themes').then(s => settingsView = s));
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Themes', options: {uri: 'atom://config/themes'}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/updates').then(s => settingsView = s));
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Updates', options: {uri: 'atom://config/updates'}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/install').then(s => settingsView = s));
|
||||
|
||||
let hasSystemPanel = false;
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Install', options: {uri: 'atom://config/install'}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
return hasSystemPanel = (settingsView.panelsByName['System'] != null);
|
||||
});
|
||||
|
||||
if (hasSystemPanel) {
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/system').then(s => settingsView = s));
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'System', options: {uri: 'atom://config/system'}});
|
||||
expect(focusIsWithinActivePanel()).toBe(true);
|
||||
expectActivePanelToBeKeyboardScrollable();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("opens the package settings view with atom://config/packages/<package-name>", function() {
|
||||
waitsForPromise(() => atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-readme')));
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/packages/package-with-readme').then(s => settingsView = s));
|
||||
|
||||
waitsFor(done => process.nextTick(done));
|
||||
runs(() => expect(settingsView.activePanel)
|
||||
.toEqual({name: 'package-with-readme', options: {
|
||||
uri: 'atom://config/packages/package-with-readme',
|
||||
pack: {
|
||||
name: 'package-with-readme',
|
||||
metadata: {
|
||||
name: 'package-with-readme'
|
||||
}
|
||||
},
|
||||
back: 'Packages'
|
||||
}}));
|
||||
});
|
||||
|
||||
it("doesn't use cached package detail when package re-activated and opnes the package view with atom://config/packages/<package-name>", function() {
|
||||
let [detailInitial, detailAfterReactivate] = [];
|
||||
|
||||
waitsForPromise(function() {
|
||||
atom.packages.activate();
|
||||
return new Promise(resolve => atom.packages.onDidActivateInitialPackages(resolve));
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-readme')));
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/packages/package-with-readme').then(s => settingsView = s));
|
||||
|
||||
waitsFor(done => process.nextTick(done));
|
||||
|
||||
runs(function() {
|
||||
detailInitial = settingsView.getOrCreatePanel('package-with-readme');
|
||||
expect(settingsView.getOrCreatePanel('package-with-readme')).toBe(detailInitial);
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.packages.deactivatePackage('package-with-readme'));
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-readme')));
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/packages/package-with-readme'));
|
||||
|
||||
runs(function() {
|
||||
detailAfterReactivate = settingsView.getOrCreatePanel('package-with-readme');
|
||||
expect(settingsView.getOrCreatePanel('package-with-readme')).toBe(detailAfterReactivate);
|
||||
expect(detailInitial).toBeTruthy();
|
||||
expect(detailAfterReactivate).toBeTruthy();
|
||||
expect(detailInitial).not.toBe(detailAfterReactivate);
|
||||
});
|
||||
});
|
||||
|
||||
it("passes the URI to a pane's beforeShow() method on settings view initialization", function() {
|
||||
const InstallPanel = require('../lib/install-panel');
|
||||
spyOn(InstallPanel.prototype, 'beforeShow');
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/install/package:something').then(s => settingsView = s));
|
||||
|
||||
waitsFor(() => settingsView.activePanel != null
|
||||
, 'The activePanel should be set', 5000);
|
||||
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Install', options: {uri: 'atom://config/install/package:something'}});
|
||||
expect(InstallPanel.prototype.beforeShow).toHaveBeenCalledWith({uri: 'atom://config/install/package:something'});});
|
||||
});
|
||||
|
||||
it("passes the URI to a pane's beforeShow() method after initialization", function() {
|
||||
const InstallPanel = require('../lib/install-panel');
|
||||
spyOn(InstallPanel.prototype, 'beforeShow');
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config').then(s => settingsView = s));
|
||||
|
||||
waitsFor(done => process.nextTick(done));
|
||||
|
||||
runs(() => expect(settingsView.activePanel).toEqual({name: 'Core', options: {}}));
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('atom://config/install/package:something').then(s => settingsView = s));
|
||||
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(settingsView.activePanel)
|
||||
.toEqual({name: 'Install', options: {uri: 'atom://config/install/package:something'}});
|
||||
expect(InstallPanel.prototype.beforeShow).toHaveBeenCalledWith({uri: 'atom://config/install/package:something'});});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the package is then deactivated", function() {
|
||||
beforeEach(() => settingsView = null);
|
||||
|
||||
it("calls the dispose method on all panels", function() {
|
||||
openWithCommand('settings-view:open');
|
||||
|
||||
waitsFor(done => process.nextTick(done));
|
||||
|
||||
runs(function() {
|
||||
let panel;
|
||||
settingsView = atom.workspace.getActivePaneItem();
|
||||
const panels = [
|
||||
settingsView.getOrCreatePanel('Core'),
|
||||
settingsView.getOrCreatePanel('Editor'),
|
||||
settingsView.getOrCreatePanel('Keybindings'),
|
||||
settingsView.getOrCreatePanel('Packages'),
|
||||
settingsView.getOrCreatePanel('Themes'),
|
||||
settingsView.getOrCreatePanel('Updates'),
|
||||
settingsView.getOrCreatePanel('Install')
|
||||
];
|
||||
const systemPanel = settingsView.getOrCreatePanel('System');
|
||||
if (systemPanel != null) {
|
||||
panels.push(systemPanel);
|
||||
}
|
||||
for (panel of Array.from(panels)) {
|
||||
if (panel.dispose) {
|
||||
spyOn(panel, 'dispose');
|
||||
} else {
|
||||
spyOn(panel, 'destroy');
|
||||
}
|
||||
}
|
||||
|
||||
waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackage('settings-view'))); // Ensure works on promise and non-promise versions
|
||||
|
||||
runs(function() {
|
||||
for (panel of Array.from(panels)) {
|
||||
if (panel.dispose) {
|
||||
expect(panel.dispose).toHaveBeenCalled();
|
||||
} else {
|
||||
expect(panel.destroy).toHaveBeenCalled();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when an installed package is clicked from the Install panel", () => it("displays the package details", function() {
|
||||
waitsFor(() => atom.packages.activatePackage('settings-view'));
|
||||
|
||||
runs(function() {
|
||||
settingsView.packageManager.getClient();
|
||||
spyOn(settingsView.packageManager.client, 'featuredPackages').andCallFake(callback => callback(null, [{name: 'settings-view'}]));
|
||||
return settingsView.showPanel('Install');
|
||||
});
|
||||
|
||||
waitsFor(() => settingsView.element.querySelectorAll('.package-card:not(.hidden)').length > 0);
|
||||
|
||||
runs(function() {
|
||||
settingsView.element.querySelectorAll('.package-card:not(.hidden)')[0].click();
|
||||
|
||||
const packageDetail = settingsView.element.querySelector('.package-detail .active');
|
||||
expect(packageDetail.textContent).toBe('Settings View');
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when the active theme has settings", function() {
|
||||
let panel = null;
|
||||
|
||||
beforeEach(function() {
|
||||
atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures'));
|
||||
atom.packages.loadPackage('ui-theme-with-config');
|
||||
atom.packages.loadPackage('syntax-theme-with-config');
|
||||
atom.config.set('core.themes', ['ui-theme-with-config', 'syntax-theme-with-config']);
|
||||
|
||||
const reloadedHandler = jasmine.createSpy('reloadedHandler');
|
||||
atom.themes.onDidChangeActiveThemes(reloadedHandler);
|
||||
atom.themes.activatePackages();
|
||||
|
||||
waitsFor("themes to be reloaded", () => reloadedHandler.callCount === 1);
|
||||
|
||||
runs(function() {
|
||||
settingsView.showPanel('Themes');
|
||||
return panel = settingsView.element.querySelector('.themes-panel');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => atom.themes.unwatchUserStylesheet());
|
||||
|
||||
describe("when the UI theme's settings button is clicked", () => it("navigates to that theme's detail view", function() {
|
||||
jasmine.attachToDOM(settingsView.element);
|
||||
expect(panel.querySelector('.active-theme-settings')).toBeVisible();
|
||||
|
||||
panel.querySelector('.active-theme-settings').click();
|
||||
const packageDetail = settingsView.element.querySelector('.package-detail li.active');
|
||||
expect(packageDetail.textContent).toBe('Ui Theme With Config');
|
||||
}));
|
||||
|
||||
describe("when the syntax theme's settings button is clicked", () => it("navigates to that theme's detail view", function() {
|
||||
jasmine.attachToDOM(settingsView.element);
|
||||
expect(panel.querySelector('.active-syntax-settings')).toBeVisible();
|
||||
|
||||
panel.querySelector('.active-syntax-settings').click();
|
||||
const packageDetail = settingsView.element.querySelector('.package-detail li.active');
|
||||
expect(packageDetail.textContent).toBe('Syntax Theme With Config');
|
||||
}));
|
||||
});
|
||||
});
|
@ -1,208 +0,0 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
|
||||
CSON = require 'season'
|
||||
|
||||
PackageManager = require '../lib/package-manager'
|
||||
ThemesPanel = require '../lib/themes-panel'
|
||||
|
||||
describe "ThemesPanel", ->
|
||||
[panel, packageManager, reloadedHandler] = []
|
||||
settingsView = null
|
||||
|
||||
beforeEach ->
|
||||
atom.packages.loadPackage('atom-light-ui')
|
||||
atom.packages.loadPackage('atom-dark-ui')
|
||||
atom.packages.loadPackage('atom-light-syntax')
|
||||
atom.packages.loadPackage('atom-dark-syntax')
|
||||
atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures'))
|
||||
atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
|
||||
reloadedHandler = jasmine.createSpy('reloadedHandler')
|
||||
atom.themes.onDidChangeActiveThemes(reloadedHandler)
|
||||
atom.themes.activatePackages()
|
||||
|
||||
waitsFor "themes to be reloaded", ->
|
||||
reloadedHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
packageManager = new PackageManager
|
||||
themeMetadata = CSON.readFileSync(path.join(__dirname, 'fixtures', 'a-theme', 'package.json'))
|
||||
spyOn(packageManager, 'getFeatured').andCallFake (callback) ->
|
||||
Promise.resolve([themeMetadata])
|
||||
panel = new ThemesPanel(settingsView, packageManager)
|
||||
|
||||
# Make updates synchronous
|
||||
spyOn(panel, 'scheduleUpdateThemeConfig').andCallFake -> @updateThemeConfig()
|
||||
|
||||
afterEach ->
|
||||
atom.packages.unloadPackage('a-theme') if atom.packages.isPackageLoaded('a-theme')
|
||||
waitsForPromise ->
|
||||
Promise.resolve(atom.themes.deactivateThemes()) # Ensure works on promise and non-promise versions
|
||||
|
||||
it "selects the active syntax and UI themes", ->
|
||||
expect(panel.refs.uiMenu.value).toBe 'atom-dark-ui'
|
||||
expect(panel.refs.syntaxMenu.value).toBe 'atom-dark-syntax'
|
||||
|
||||
describe "when a UI theme is selected", ->
|
||||
it "updates the 'core.themes' config key with the selected UI theme", ->
|
||||
for child in panel.refs.uiMenu.children
|
||||
child.selected = child.value is 'atom-light-ui'
|
||||
child.dispatchEvent(new Event('change', {bubbles: true}))
|
||||
waitsFor ->
|
||||
reloadedHandler.callCount is 2
|
||||
runs ->
|
||||
expect(atom.config.get('core.themes')).toEqual ['atom-light-ui', 'atom-dark-syntax']
|
||||
|
||||
describe "when a syntax theme is selected", ->
|
||||
it "updates the 'core.themes' config key with the selected syntax theme", ->
|
||||
for child in panel.refs.syntaxMenu.children
|
||||
child.selected = child.value is 'atom-light-syntax'
|
||||
child.dispatchEvent(new Event('change', {bubbles: true}))
|
||||
waitsFor ->
|
||||
reloadedHandler.callCount is 2
|
||||
runs ->
|
||||
expect(atom.config.get('core.themes')).toEqual ['atom-dark-ui', 'atom-light-syntax']
|
||||
|
||||
describe "when the 'core.config' key changes", ->
|
||||
it "refreshes the theme menus", ->
|
||||
reloadedHandler.reset()
|
||||
atom.config.set('core.themes', ['atom-light-ui', 'atom-light-syntax'])
|
||||
|
||||
waitsFor ->
|
||||
reloadedHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect(panel.refs.uiMenu.value).toBe 'atom-light-ui'
|
||||
expect(panel.refs.syntaxMenu.value).toBe 'atom-light-syntax'
|
||||
|
||||
xdescribe "when the themes panel is navigated to", ->
|
||||
xit "focuses the search filter", ->
|
||||
settingsView.showPanel('Themes')
|
||||
expect(panel.refs.filterEditor.element).toHaveFocus()
|
||||
|
||||
describe "theme lists", ->
|
||||
[installed] = []
|
||||
beforeEach ->
|
||||
installed = JSON.parse fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json'))
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake ->
|
||||
spyOn(packageManager, 'getInstalled').andReturn Promise.resolve(installed)
|
||||
panel = new ThemesPanel(settingsView, packageManager)
|
||||
|
||||
waitsFor ->
|
||||
packageManager.getInstalled.callCount is 1 and panel.refs.communityCount.textContent.indexOf('…') < 0
|
||||
|
||||
it 'shows the themes', ->
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe '1'
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
expect(panel.refs.coreCount.textContent.trim()).toBe '1'
|
||||
expect(panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
expect(panel.refs.devCount.textContent.trim()).toBe '1'
|
||||
expect(panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
it 'filters themes by name', ->
|
||||
panel.refs.filterEditor.setText('user-')
|
||||
window.advanceClock(panel.refs.filterEditor.getBuffer().stoppedChangingDelay)
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe '1/1'
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
expect(panel.refs.coreCount.textContent.trim()).toBe '0/1'
|
||||
expect(panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 0
|
||||
|
||||
expect(panel.refs.devCount.textContent.trim()).toBe '0/1'
|
||||
expect(panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 0
|
||||
|
||||
it 'adds newly installed themes to the list', ->
|
||||
[installCallback] = []
|
||||
spyOn(packageManager, 'runCommand').andCallFake (args, callback) ->
|
||||
installCallback = callback
|
||||
onWillThrowError: ->
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake (name) ->
|
||||
installed.user.push {name, theme: 'ui'}
|
||||
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe '1'
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 1
|
||||
|
||||
packageManager.install({name: 'another-user-theme', theme: 'ui'})
|
||||
installCallback(0, '', '')
|
||||
|
||||
advanceClock ThemesPanel.loadPackagesDelay()
|
||||
waits 1
|
||||
runs ->
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe '2'
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe 2
|
||||
|
||||
it 'collapses/expands a sub-section if its header is clicked', ->
|
||||
expect(panel.element.querySelectorAll('.sub-section-heading.has-items').length).toBe 3
|
||||
panel.element.querySelector('.sub-section.installed-packages .sub-section-heading.has-items').click()
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).toHaveClass 'collapsed'
|
||||
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass 'collapsed'
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
panel.element.querySelector('.sub-section.installed-packages .sub-section-heading.has-items').click()
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
it 'can collapse and expand any of the sub-sections', ->
|
||||
expect(panel.element.querySelectorAll('.sub-section-heading.has-items').length).toBe 3
|
||||
|
||||
for heading in panel.element.querySelectorAll('.sub-section-heading.has-items')
|
||||
heading.click()
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).toHaveClass 'collapsed'
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).toHaveClass 'collapsed'
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).toHaveClass 'collapsed'
|
||||
|
||||
for heading in panel.element.querySelectorAll('.sub-section-heading.has-items')
|
||||
heading.click()
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass 'collapsed'
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass 'collapsed'
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
it 'can collapse sub-sections when filtering', ->
|
||||
panel.refs.filterEditor.setText('user-')
|
||||
window.advanceClock(panel.refs.filterEditor.getBuffer().stoppedChangingDelay)
|
||||
|
||||
hasItems = panel.element.querySelectorAll('.sub-section-heading.has-items')
|
||||
expect(hasItems.length).toBe 1
|
||||
expect(hasItems[0].textContent).toMatch /^Community Themes/
|
||||
|
||||
describe 'when there are no themes', ->
|
||||
beforeEach ->
|
||||
installed =
|
||||
dev: []
|
||||
user: []
|
||||
core: []
|
||||
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake ->
|
||||
spyOn(packageManager, 'getInstalled').andReturn Promise.resolve(installed)
|
||||
panel = new ThemesPanel(settingsView, packageManager)
|
||||
|
||||
waitsFor ->
|
||||
packageManager.getInstalled.callCount is 1 and panel.refs.communityCount.textContent.indexOf('…') < 0
|
||||
|
||||
afterEach ->
|
||||
waitsForPromise ->
|
||||
Promise.resolve(atom.themes.deactivateThemes()) # Ensure works on promise and non-promise versions
|
||||
|
||||
it 'has a count of zero in all headings', ->
|
||||
for heading in panel.element.querySelector('.section-heading-count')
|
||||
expect(heading.textContent).toMatch /^0+$/
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan').length).toBe 4
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan.has-items').length).toBe 0
|
||||
|
||||
it 'can collapse and expand any of the sub-sections', ->
|
||||
for heading in panel.element.querySelectorAll('.sub-section-heading')
|
||||
heading.click()
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass 'collapsed'
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass 'collapsed'
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass 'collapsed'
|
||||
|
||||
it 'does not allow collapsing on any section when filtering', ->
|
||||
panel.refs.filterEditor.setText('user-')
|
||||
window.advanceClock(panel.refs.filterEditor.getBuffer().stoppedChangingDelay)
|
||||
|
||||
for heading in panel.element.querySelector('.section-heading-count')
|
||||
expect(heading.textContent).toMatch /^(0\/0)+$/
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan').length).toBe 4
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan.has-items').length).toBe 0
|
226
packages/settings-view/spec/themes-panel-spec.js
Normal file
226
packages/settings-view/spec/themes-panel-spec.js
Normal file
@ -0,0 +1,226 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const CSON = require('season');
|
||||
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const ThemesPanel = require('../lib/themes-panel');
|
||||
|
||||
describe("ThemesPanel", function() {
|
||||
let [panel, packageManager, reloadedHandler] = [];
|
||||
const settingsView = null;
|
||||
|
||||
beforeEach(function() {
|
||||
atom.packages.loadPackage('atom-light-ui');
|
||||
atom.packages.loadPackage('atom-dark-ui');
|
||||
atom.packages.loadPackage('atom-light-syntax');
|
||||
atom.packages.loadPackage('atom-dark-syntax');
|
||||
atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures'));
|
||||
atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax']);
|
||||
reloadedHandler = jasmine.createSpy('reloadedHandler');
|
||||
atom.themes.onDidChangeActiveThemes(reloadedHandler);
|
||||
atom.themes.activatePackages();
|
||||
|
||||
waitsFor("themes to be reloaded", () => reloadedHandler.callCount === 1);
|
||||
|
||||
runs(function() {
|
||||
packageManager = new PackageManager;
|
||||
const themeMetadata = CSON.readFileSync(path.join(__dirname, 'fixtures', 'a-theme', 'package.json'));
|
||||
spyOn(packageManager, 'getFeatured').andCallFake(callback => Promise.resolve([themeMetadata]));
|
||||
panel = new ThemesPanel(settingsView, packageManager);
|
||||
|
||||
// Make updates synchronous
|
||||
spyOn(panel, 'scheduleUpdateThemeConfig').andCallFake(function() { return this.updateThemeConfig(); });
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (atom.packages.isPackageLoaded('a-theme')) { atom.packages.unloadPackage('a-theme'); }
|
||||
waitsForPromise(() => Promise.resolve(atom.themes.deactivateThemes()));
|
||||
}); // Ensure works on promise and non-promise versions
|
||||
|
||||
it("selects the active syntax and UI themes", function() {
|
||||
expect(panel.refs.uiMenu.value).toBe('atom-dark-ui');
|
||||
expect(panel.refs.syntaxMenu.value).toBe('atom-dark-syntax');
|
||||
});
|
||||
|
||||
describe("when a UI theme is selected", () => it("updates the 'core.themes' config key with the selected UI theme", function() {
|
||||
for (let child of Array.from(panel.refs.uiMenu.children)) {
|
||||
child.selected = child.value === 'atom-light-ui';
|
||||
child.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
}
|
||||
waitsFor(() => reloadedHandler.callCount === 2);
|
||||
runs(() => expect(atom.config.get('core.themes')).toEqual(['atom-light-ui', 'atom-dark-syntax']));
|
||||
}));
|
||||
|
||||
describe("when a syntax theme is selected", () => it("updates the 'core.themes' config key with the selected syntax theme", function() {
|
||||
for (let child of Array.from(panel.refs.syntaxMenu.children)) {
|
||||
child.selected = child.value === 'atom-light-syntax';
|
||||
child.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
}
|
||||
waitsFor(() => reloadedHandler.callCount === 2);
|
||||
runs(() => expect(atom.config.get('core.themes')).toEqual(['atom-dark-ui', 'atom-light-syntax']));
|
||||
}));
|
||||
|
||||
describe("when the 'core.config' key changes", () => it("refreshes the theme menus", function() {
|
||||
reloadedHandler.reset();
|
||||
atom.config.set('core.themes', ['atom-light-ui', 'atom-light-syntax']);
|
||||
|
||||
waitsFor(() => reloadedHandler.callCount === 1);
|
||||
|
||||
runs(function() {
|
||||
expect(panel.refs.uiMenu.value).toBe('atom-light-ui');
|
||||
expect(panel.refs.syntaxMenu.value).toBe('atom-light-syntax');
|
||||
});
|
||||
}));
|
||||
|
||||
xdescribe("when the themes panel is navigated to", () => xit("focuses the search filter", function() {
|
||||
settingsView.showPanel('Themes');
|
||||
expect(panel.refs.filterEditor.element).toHaveFocus();
|
||||
}));
|
||||
|
||||
describe("theme lists", function() {
|
||||
let [installed] = [];
|
||||
beforeEach(function() {
|
||||
installed = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', 'installed.json')));
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake(function() {});
|
||||
spyOn(packageManager, 'getInstalled').andReturn(Promise.resolve(installed));
|
||||
panel = new ThemesPanel(settingsView, packageManager);
|
||||
|
||||
waitsFor(() => (packageManager.getInstalled.callCount === 1) && (panel.refs.communityCount.textContent.indexOf('…') < 0));
|
||||
});
|
||||
|
||||
it('shows the themes', function() {
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe('1');
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
expect(panel.refs.coreCount.textContent.trim()).toBe('1');
|
||||
expect(panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
expect(panel.refs.devCount.textContent.trim()).toBe('1');
|
||||
expect(panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
});
|
||||
|
||||
it('filters themes by name', function() {
|
||||
panel.refs.filterEditor.setText('user-');
|
||||
window.advanceClock(panel.refs.filterEditor.getBuffer().stoppedChangingDelay);
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe('1/1');
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
expect(panel.refs.coreCount.textContent.trim()).toBe('0/1');
|
||||
expect(panel.refs.corePackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(0);
|
||||
|
||||
expect(panel.refs.devCount.textContent.trim()).toBe('0/1');
|
||||
expect(panel.refs.devPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(0);
|
||||
});
|
||||
|
||||
it('adds newly installed themes to the list', function() {
|
||||
let [installCallback] = [];
|
||||
spyOn(packageManager, 'runCommand').andCallFake(function(args, callback) {
|
||||
installCallback = callback;
|
||||
return {onWillThrowError() {}};
|
||||
});
|
||||
spyOn(atom.packages, 'loadPackage').andCallFake(name => installed.user.push({name, theme: 'ui'}));
|
||||
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe('1');
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(1);
|
||||
|
||||
packageManager.install({name: 'another-user-theme', theme: 'ui'});
|
||||
installCallback(0, '', '');
|
||||
|
||||
advanceClock(ThemesPanel.loadPackagesDelay());
|
||||
waits(1);
|
||||
runs(function() {
|
||||
expect(panel.refs.communityCount.textContent.trim()).toBe('2');
|
||||
expect(panel.refs.communityPackages.querySelectorAll('.package-card:not(.hidden)').length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('collapses/expands a sub-section if its header is clicked', function() {
|
||||
expect(panel.element.querySelectorAll('.sub-section-heading.has-items').length).toBe(3);
|
||||
panel.element.querySelector('.sub-section.installed-packages .sub-section-heading.has-items').click();
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).toHaveClass('collapsed');
|
||||
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass('collapsed');
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass('collapsed');
|
||||
|
||||
panel.element.querySelector('.sub-section.installed-packages .sub-section-heading.has-items').click();
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass('collapsed');
|
||||
});
|
||||
|
||||
it('can collapse and expand any of the sub-sections', function() {
|
||||
let heading;
|
||||
expect(panel.element.querySelectorAll('.sub-section-heading.has-items').length).toBe(3);
|
||||
|
||||
for (heading of Array.from(panel.element.querySelectorAll('.sub-section-heading.has-items'))) {
|
||||
heading.click();
|
||||
}
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).toHaveClass('collapsed');
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).toHaveClass('collapsed');
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).toHaveClass('collapsed');
|
||||
|
||||
for (heading of Array.from(panel.element.querySelectorAll('.sub-section-heading.has-items'))) {
|
||||
heading.click();
|
||||
}
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass('collapsed');
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass('collapsed');
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass('collapsed');
|
||||
});
|
||||
|
||||
it('can collapse sub-sections when filtering', function() {
|
||||
panel.refs.filterEditor.setText('user-');
|
||||
window.advanceClock(panel.refs.filterEditor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
const hasItems = panel.element.querySelectorAll('.sub-section-heading.has-items');
|
||||
expect(hasItems.length).toBe(1);
|
||||
expect(hasItems[0].textContent).toMatch(/^Community Themes/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are no themes', function() {
|
||||
beforeEach(function() {
|
||||
const installed = {
|
||||
dev: [],
|
||||
user: [],
|
||||
core: []
|
||||
};
|
||||
|
||||
spyOn(packageManager, 'loadCompatiblePackageVersion').andCallFake(function() {});
|
||||
spyOn(packageManager, 'getInstalled').andReturn(Promise.resolve(installed));
|
||||
panel = new ThemesPanel(settingsView, packageManager);
|
||||
|
||||
waitsFor(() => (packageManager.getInstalled.callCount === 1) && (panel.refs.communityCount.textContent.indexOf('…') < 0));
|
||||
});
|
||||
|
||||
afterEach(() => waitsForPromise(() => Promise.resolve(atom.themes.deactivateThemes()))); // Ensure works on promise and non-promise versions
|
||||
|
||||
it('has a count of zero in all headings', function() {
|
||||
for (let heading of Array.from(panel.element.querySelector('.section-heading-count'))) {
|
||||
expect(heading.textContent).toMatch(/^0+$/);
|
||||
}
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan').length).toBe(4);
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan.has-items').length).toBe(0);
|
||||
});
|
||||
|
||||
it('can collapse and expand any of the sub-sections', function() {
|
||||
for (let heading of Array.from(panel.element.querySelectorAll('.sub-section-heading'))) {
|
||||
heading.click();
|
||||
}
|
||||
expect(panel.element.querySelector('.sub-section.installed-packages')).not.toHaveClass('collapsed');
|
||||
expect(panel.element.querySelector('.sub-section.core-packages')).not.toHaveClass('collapsed');
|
||||
expect(panel.element.querySelector('.sub-section.dev-packages')).not.toHaveClass('collapsed');
|
||||
});
|
||||
|
||||
it('does not allow collapsing on any section when filtering', function() {
|
||||
panel.refs.filterEditor.setText('user-');
|
||||
window.advanceClock(panel.refs.filterEditor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
for (let heading of Array.from(panel.element.querySelector('.section-heading-count'))) {
|
||||
expect(heading.textContent).toMatch(/^(0\/0)+$/);
|
||||
}
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan').length).toBe(4);
|
||||
expect(panel.element.querySelectorAll('.sub-section .icon-paintcan.has-items').length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,225 +0,0 @@
|
||||
UpdatesPanel = require '../lib/updates-panel'
|
||||
PackageManager = require '../lib/package-manager'
|
||||
SettingsView = require '../lib/settings-view'
|
||||
|
||||
describe 'UpdatesPanel', ->
|
||||
panel = null
|
||||
settingsView = null
|
||||
packageManager = null
|
||||
[resolveOutdated, rejectOutdated] = []
|
||||
|
||||
beforeEach ->
|
||||
settingsView = new SettingsView
|
||||
packageManager = new PackageManager
|
||||
# This spy is only needed for the Check for Updates specs,
|
||||
# but we have to instantiate it here because we need to pass the spy to the UpdatesPanel
|
||||
spyOn(packageManager, 'getOutdated').andReturn(new Promise((resolve, reject) -> [resolveOutdated, rejectOutdated] = [resolve, reject]))
|
||||
panel = new UpdatesPanel(settingsView, packageManager)
|
||||
jasmine.attachToDOM(panel.element)
|
||||
|
||||
it "shows updates when updates are available", ->
|
||||
pack =
|
||||
name: 'test-package'
|
||||
description: 'some description'
|
||||
latestVersion: '99.0.0'
|
||||
version: '1.0.0'
|
||||
|
||||
# skip packman stubbing
|
||||
panel.beforeShow(updates: [pack])
|
||||
expect(panel.refs.updatesContainer.children.length).toBe(1)
|
||||
|
||||
it "shows a message when updates are not available", ->
|
||||
panel.beforeShow(updates: [])
|
||||
expect(panel.refs.updatesContainer.children.length).toBe(0)
|
||||
expect(panel.refs.noUpdatesMessage.style.display).not.toBe('none')
|
||||
|
||||
describe "version pinned packages message", ->
|
||||
it 'shows a message when there are pinned version packages', ->
|
||||
spyOn(packageManager, 'getVersionPinnedPackages').andReturn(['foo', 'bar', 'baz'])
|
||||
panel.beforeShow(updates: [])
|
||||
expect(panel.refs.versionPinnedPackagesMessage.style.display).not.toBe('none')
|
||||
|
||||
it 'does not show a message when there are no version pinned packages', ->
|
||||
spyOn(packageManager, 'getVersionPinnedPackages').andReturn([])
|
||||
panel.beforeShow(updates: [])
|
||||
expect(panel.refs.versionPinnedPackagesMessage.style.display).toBe('none')
|
||||
|
||||
describe "the Update All button", ->
|
||||
packA =
|
||||
name: 'test-package-a'
|
||||
description: 'some description'
|
||||
latestVersion: '99.0.0'
|
||||
version: '1.0.0'
|
||||
packB =
|
||||
name: 'test-package-b'
|
||||
description: 'some description'
|
||||
latestVersion: '99.0.0'
|
||||
version: '1.0.0'
|
||||
packC =
|
||||
name: 'test-package-c'
|
||||
description: 'some description'
|
||||
latestVersion: '99.0.0'
|
||||
version: '1.0.0'
|
||||
|
||||
[cardA, cardB, cardC] = []
|
||||
[resolveA, resolveB, resolveC, rejectA, rejectB, rejectC] = []
|
||||
|
||||
beforeEach ->
|
||||
# skip packman stubbing
|
||||
panel.beforeShow(updates: [packA, packB, packC])
|
||||
|
||||
[cardA, cardB, cardC] = panel.packageCards
|
||||
|
||||
# fake a git url package
|
||||
cardC.pack.apmInstallSource = {type: 'git', sha: 'cf23df2207d99a74fbe169e3eba035e633b65d94'}
|
||||
cardC.pack.latestSha = 'a296114f3d0deec519a41f4c62e7fc56075b7f01'
|
||||
|
||||
spyOn(cardA, 'update').andReturn(new Promise((resolve, reject) -> [resolveA, rejectA] = [resolve, reject]))
|
||||
spyOn(cardB, 'update').andReturn(new Promise((resolve, reject) -> [resolveB, rejectB] = [resolve, reject]))
|
||||
spyOn(cardC, 'update').andReturn(new Promise((resolve, reject) -> [resolveC, rejectC] = [resolve, reject]))
|
||||
|
||||
atom.config.set("settings-view.packageUpdateConcurrency", -1)
|
||||
|
||||
it 'attempts to update all packages and prompts to restart if at least one package updates successfully', ->
|
||||
expect(atom.notifications.getNotifications().length).toBe 0
|
||||
expect(panel.refs.updateAllButton).toBeVisible()
|
||||
|
||||
panel.updateAll()
|
||||
|
||||
resolveA()
|
||||
rejectB('Error updating package')
|
||||
|
||||
waits 0
|
||||
runs ->
|
||||
expect(atom.notifications.getNotifications().length).toBe 0
|
||||
|
||||
resolveC()
|
||||
|
||||
waitsFor ->
|
||||
atom.notifications.getNotifications().length is 1
|
||||
|
||||
runs ->
|
||||
notifications = atom.notifications.getNotifications()
|
||||
expect(notifications.length).toBe 1
|
||||
notif = notifications[0]
|
||||
|
||||
expect(notif.options.detail).toBe 'test-package-a@1.0.0 -> 99.0.0\ntest-package-b@1.0.0 -> 99.0.0\ntest-package-c@cf23df22 -> a296114f'
|
||||
|
||||
expect(notif.options.buttons.length).toBe(2)
|
||||
|
||||
spyOn(atom, 'restartApplication')
|
||||
notif.options.buttons[0].onDidClick()
|
||||
expect(atom.restartApplication).toHaveBeenCalled()
|
||||
|
||||
spyOn(notif, 'dismiss')
|
||||
notif.options.buttons[1].onDidClick()
|
||||
expect(notif.dismiss).toHaveBeenCalled()
|
||||
|
||||
it 'works with queue enabled', ->
|
||||
expect(panel.refs.updateAllButton).not.toBeDisabled()
|
||||
atom.config.set("settings-view.packageUpdateConcurrency", 2)
|
||||
|
||||
panel.updateAll()
|
||||
|
||||
resolveA()
|
||||
resolveB()
|
||||
resolveC()
|
||||
|
||||
waitsFor ->
|
||||
panel.refs.updateAllButton.style.display is 'none'
|
||||
|
||||
it 'becomes hidden if all updates succeed', ->
|
||||
expect(panel.refs.updateAllButton).not.toBeDisabled()
|
||||
|
||||
panel.updateAll()
|
||||
|
||||
expect(panel.refs.updateAllButton).toBeDisabled()
|
||||
|
||||
resolveA()
|
||||
resolveB()
|
||||
resolveC()
|
||||
|
||||
waitsFor ->
|
||||
panel.refs.updateAllButton.style.display is 'none'
|
||||
|
||||
it 'remains enabled and visible if not all updates succeed', ->
|
||||
expect(panel.refs.updateAllButton).not.toBeDisabled()
|
||||
|
||||
panel.updateAll()
|
||||
|
||||
expect(panel.refs.updateAllButton).toBeDisabled()
|
||||
|
||||
resolveA()
|
||||
rejectB('Error updating package')
|
||||
resolveC()
|
||||
|
||||
waitsFor ->
|
||||
panel.refs.updateAllButton.disabled is false
|
||||
|
||||
runs ->
|
||||
expect(panel.refs.updateAllButton).toBeVisible()
|
||||
|
||||
it 'does not attempt to update packages that are already updating', ->
|
||||
cardA.update()
|
||||
packageManager.emitPackageEvent 'updating', packA
|
||||
panel.updateAll()
|
||||
|
||||
expect(cardA.update.calls.length).toBe 1
|
||||
|
||||
describe 'the Check for Updates button', ->
|
||||
pack =
|
||||
name: 'test-package'
|
||||
description: 'some description'
|
||||
latestVersion: '99.0.0'
|
||||
version: '1.0.0'
|
||||
|
||||
beforeEach ->
|
||||
# skip packman stubbing - without this, getOutdated() is called another time
|
||||
# this is not an issue in actual usage as getOutdated() isn't blocked on a spy
|
||||
panel.beforeShow(updates: [pack])
|
||||
|
||||
it 'disables itself when clicked until the list of outdated packages is returned', ->
|
||||
# Updates panel checks for updates on initialization so resolve the promise
|
||||
resolveOutdated()
|
||||
|
||||
waits 0
|
||||
runs ->
|
||||
expect(panel.refs.checkButton.disabled).toBe false
|
||||
|
||||
panel.checkForUpdates()
|
||||
expect(panel.refs.checkButton.disabled).toBe true
|
||||
|
||||
resolveOutdated()
|
||||
|
||||
waits 0
|
||||
runs ->
|
||||
expect(panel.refs.checkButton.disabled).toBe false
|
||||
|
||||
it 'clears the outdated cache when checking for updates', ->
|
||||
# This spec just tests that we're passing the clearCache bool through, not the actual implementation
|
||||
# For that, look at the PackageManager specs
|
||||
resolveOutdated()
|
||||
waits 0
|
||||
runs ->
|
||||
panel.refs.checkButton.click()
|
||||
expect(packageManager.getOutdated).toHaveBeenCalledWith true
|
||||
|
||||
it 'is disabled when packages are updating', ->
|
||||
# Updates panel checks for updates on initialization so resolve the promise
|
||||
resolveOutdated()
|
||||
|
||||
waits 0
|
||||
runs ->
|
||||
expect(panel.refs.checkButton.disabled).toBe false
|
||||
|
||||
packageManager.emitPackageEvent 'updating', {name: 'packA'}
|
||||
expect(panel.refs.checkButton.disabled).toBe true
|
||||
|
||||
packageManager.emitPackageEvent 'updating', {name: 'packB'}
|
||||
expect(panel.refs.checkButton.disabled).toBe true
|
||||
|
||||
packageManager.emitPackageEvent 'updated', {name: 'packB'}
|
||||
expect(panel.refs.checkButton.disabled).toBe true
|
||||
|
||||
packageManager.emitPackageEvent 'update-failed', {name: 'packA'}
|
||||
expect(panel.refs.checkButton.disabled).toBe false
|
247
packages/settings-view/spec/updates-panel-spec.js
Normal file
247
packages/settings-view/spec/updates-panel-spec.js
Normal file
@ -0,0 +1,247 @@
|
||||
|
||||
const UpdatesPanel = require('../lib/updates-panel');
|
||||
const PackageManager = require('../lib/package-manager');
|
||||
const SettingsView = require('../lib/settings-view');
|
||||
|
||||
describe('UpdatesPanel', function() {
|
||||
let panel = null;
|
||||
let settingsView = null;
|
||||
let packageManager = null;
|
||||
let [resolveOutdated, rejectOutdated] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
settingsView = new SettingsView;
|
||||
packageManager = new PackageManager;
|
||||
// This spy is only needed for the Check for Updates specs,
|
||||
// but we have to instantiate it here because we need to pass the spy to the UpdatesPanel
|
||||
spyOn(packageManager, 'getOutdated').andReturn(new Promise((resolve, reject) => [resolveOutdated, rejectOutdated] = [resolve, reject]));
|
||||
panel = new UpdatesPanel(settingsView, packageManager);
|
||||
return jasmine.attachToDOM(panel.element);
|
||||
});
|
||||
|
||||
it("shows updates when updates are available", function() {
|
||||
const pack = {
|
||||
name: 'test-package',
|
||||
description: 'some description',
|
||||
latestVersion: '99.0.0',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
// skip packman stubbing
|
||||
panel.beforeShow({updates: [pack]});
|
||||
expect(panel.refs.updatesContainer.children.length).toBe(1);
|
||||
});
|
||||
|
||||
it("shows a message when updates are not available", function() {
|
||||
panel.beforeShow({updates: []});
|
||||
expect(panel.refs.updatesContainer.children.length).toBe(0);
|
||||
expect(panel.refs.noUpdatesMessage.style.display).not.toBe('none');
|
||||
});
|
||||
|
||||
describe("version pinned packages message", function() {
|
||||
it('shows a message when there are pinned version packages', function() {
|
||||
spyOn(packageManager, 'getVersionPinnedPackages').andReturn(['foo', 'bar', 'baz']);
|
||||
panel.beforeShow({updates: []});
|
||||
expect(panel.refs.versionPinnedPackagesMessage.style.display).not.toBe('none');
|
||||
});
|
||||
|
||||
it('does not show a message when there are no version pinned packages', function() {
|
||||
spyOn(packageManager, 'getVersionPinnedPackages').andReturn([]);
|
||||
panel.beforeShow({updates: []});
|
||||
expect(panel.refs.versionPinnedPackagesMessage.style.display).toBe('none');
|
||||
});
|
||||
});
|
||||
|
||||
describe("the Update All button", function() {
|
||||
const packA = {
|
||||
name: 'test-package-a',
|
||||
description: 'some description',
|
||||
latestVersion: '99.0.0',
|
||||
version: '1.0.0'
|
||||
};
|
||||
const packB = {
|
||||
name: 'test-package-b',
|
||||
description: 'some description',
|
||||
latestVersion: '99.0.0',
|
||||
version: '1.0.0'
|
||||
};
|
||||
const packC = {
|
||||
name: 'test-package-c',
|
||||
description: 'some description',
|
||||
latestVersion: '99.0.0',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
let [cardA, cardB, cardC] = [];
|
||||
let [resolveA, resolveB, resolveC, rejectA, rejectB, rejectC] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
// skip packman stubbing
|
||||
panel.beforeShow({updates: [packA, packB, packC]});
|
||||
|
||||
[cardA, cardB, cardC] = panel.packageCards;
|
||||
|
||||
// fake a git url package
|
||||
cardC.pack.apmInstallSource = {type: 'git', sha: 'cf23df2207d99a74fbe169e3eba035e633b65d94'};
|
||||
cardC.pack.latestSha = 'a296114f3d0deec519a41f4c62e7fc56075b7f01';
|
||||
|
||||
spyOn(cardA, 'update').andReturn(new Promise((resolve, reject) => [resolveA, rejectA] = [resolve, reject]));
|
||||
spyOn(cardB, 'update').andReturn(new Promise((resolve, reject) => [resolveB, rejectB] = [resolve, reject]));
|
||||
spyOn(cardC, 'update').andReturn(new Promise((resolve, reject) => [resolveC, rejectC] = [resolve, reject]));
|
||||
|
||||
atom.config.set("settings-view.packageUpdateConcurrency", -1);
|
||||
});
|
||||
|
||||
it('attempts to update all packages and prompts to restart if at least one package updates successfully', function() {
|
||||
expect(atom.notifications.getNotifications().length).toBe(0);
|
||||
expect(panel.refs.updateAllButton).toBeVisible();
|
||||
|
||||
panel.updateAll();
|
||||
|
||||
resolveA();
|
||||
rejectB('Error updating package');
|
||||
|
||||
waits(0);
|
||||
runs(function() {
|
||||
expect(atom.notifications.getNotifications().length).toBe(0);
|
||||
|
||||
return resolveC();
|
||||
});
|
||||
|
||||
waitsFor(() => atom.notifications.getNotifications().length === 1);
|
||||
|
||||
runs(function() {
|
||||
const notifications = atom.notifications.getNotifications();
|
||||
expect(notifications.length).toBe(1);
|
||||
const notif = notifications[0];
|
||||
|
||||
expect(notif.options.detail).toBe('test-package-a@1.0.0 -> 99.0.0\ntest-package-b@1.0.0 -> 99.0.0\ntest-package-c@cf23df22 -> a296114f');
|
||||
|
||||
expect(notif.options.buttons.length).toBe(2);
|
||||
|
||||
spyOn(atom, 'restartApplication');
|
||||
notif.options.buttons[0].onDidClick();
|
||||
expect(atom.restartApplication).toHaveBeenCalled();
|
||||
|
||||
spyOn(notif, 'dismiss');
|
||||
notif.options.buttons[1].onDidClick();
|
||||
expect(notif.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('works with queue enabled', function() {
|
||||
expect(panel.refs.updateAllButton).not.toBeDisabled();
|
||||
atom.config.set("settings-view.packageUpdateConcurrency", 2);
|
||||
|
||||
panel.updateAll();
|
||||
|
||||
resolveA();
|
||||
resolveB();
|
||||
resolveC();
|
||||
|
||||
waitsFor(() => panel.refs.updateAllButton.style.display === 'none');
|
||||
});
|
||||
|
||||
it('becomes hidden if all updates succeed', function() {
|
||||
expect(panel.refs.updateAllButton).not.toBeDisabled();
|
||||
|
||||
panel.updateAll();
|
||||
|
||||
expect(panel.refs.updateAllButton).toBeDisabled();
|
||||
|
||||
resolveA();
|
||||
resolveB();
|
||||
resolveC();
|
||||
|
||||
waitsFor(() => panel.refs.updateAllButton.style.display === 'none');
|
||||
});
|
||||
|
||||
it('remains enabled and visible if not all updates succeed', function() {
|
||||
expect(panel.refs.updateAllButton).not.toBeDisabled();
|
||||
|
||||
panel.updateAll();
|
||||
|
||||
expect(panel.refs.updateAllButton).toBeDisabled();
|
||||
|
||||
resolveA();
|
||||
rejectB('Error updating package');
|
||||
resolveC();
|
||||
|
||||
waitsFor(() => panel.refs.updateAllButton.disabled === false);
|
||||
|
||||
runs(() => expect(panel.refs.updateAllButton).toBeVisible());
|
||||
});
|
||||
|
||||
it('does not attempt to update packages that are already updating', function() {
|
||||
cardA.update();
|
||||
packageManager.emitPackageEvent('updating', packA);
|
||||
panel.updateAll();
|
||||
|
||||
expect(cardA.update.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the Check for Updates button', function() {
|
||||
const pack = {
|
||||
name: 'test-package',
|
||||
description: 'some description',
|
||||
latestVersion: '99.0.0',
|
||||
version: '1.0.0'
|
||||
};
|
||||
|
||||
beforeEach(() => // skip packman stubbing - without this, getOutdated() is called another time
|
||||
// this is not an issue in actual usage as getOutdated() isn't blocked on a spy
|
||||
panel.beforeShow({updates: [pack]}));
|
||||
|
||||
it('disables itself when clicked until the list of outdated packages is returned', function() {
|
||||
// Updates panel checks for updates on initialization so resolve the promise
|
||||
resolveOutdated();
|
||||
|
||||
waits(0);
|
||||
runs(function() {
|
||||
expect(panel.refs.checkButton.disabled).toBe(false);
|
||||
|
||||
panel.checkForUpdates();
|
||||
expect(panel.refs.checkButton.disabled).toBe(true);
|
||||
|
||||
return resolveOutdated();
|
||||
});
|
||||
|
||||
waits(0);
|
||||
runs(() => expect(panel.refs.checkButton.disabled).toBe(false));
|
||||
});
|
||||
|
||||
it('clears the outdated cache when checking for updates', function() {
|
||||
// This spec just tests that we're passing the clearCache bool through, not the actual implementation
|
||||
// For that, look at the PackageManager specs
|
||||
resolveOutdated();
|
||||
waits(0);
|
||||
runs(function() {
|
||||
panel.refs.checkButton.click();
|
||||
expect(packageManager.getOutdated).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('is disabled when packages are updating', function() {
|
||||
// Updates panel checks for updates on initialization so resolve the promise
|
||||
resolveOutdated();
|
||||
|
||||
waits(0);
|
||||
runs(function() {
|
||||
expect(panel.refs.checkButton.disabled).toBe(false);
|
||||
|
||||
packageManager.emitPackageEvent('updating', {name: 'packA'});
|
||||
expect(panel.refs.checkButton.disabled).toBe(true);
|
||||
|
||||
packageManager.emitPackageEvent('updating', {name: 'packB'});
|
||||
expect(panel.refs.checkButton.disabled).toBe(true);
|
||||
|
||||
packageManager.emitPackageEvent('updated', {name: 'packB'});
|
||||
expect(panel.refs.checkButton.disabled).toBe(true);
|
||||
|
||||
packageManager.emitPackageEvent('update-failed', {name: 'packA'});
|
||||
expect(panel.refs.checkButton.disabled).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
14
packages/settings-view/spec/utils-spec.js
Normal file
14
packages/settings-view/spec/utils-spec.js
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
const {ownerFromRepository} = require('../lib/utils');
|
||||
|
||||
describe("Utils", () => describe("ownerFromRepository", () => {
|
||||
it("handles a long github url", () => {
|
||||
const owner = ownerFromRepository("http://github.com/omgwow/some-package");
|
||||
expect(owner).toBe("omgwow");
|
||||
});
|
||||
|
||||
it("handles a short github url", () => {
|
||||
const owner = ownerFromRepository("omgwow/some-package");
|
||||
expect(owner).toBe("omgwow");
|
||||
});
|
||||
}));
|
@ -1,659 +0,0 @@
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
process = require 'process'
|
||||
|
||||
describe "Built-in Status Bar Tiles", ->
|
||||
[statusBar, workspaceElement, dummyView] = []
|
||||
|
||||
beforeEach ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
dummyView = document.createElement("div")
|
||||
statusBar = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('status-bar')
|
||||
|
||||
runs ->
|
||||
statusBar = workspaceElement.querySelector("status-bar")
|
||||
|
||||
describe "the file info, cursor and selection tiles", ->
|
||||
[editor, buffer, fileInfo, cursorPosition, selectionCount] = []
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
runs ->
|
||||
[launchMode, fileInfo, cursorPosition, selectionCount] =
|
||||
statusBar.getLeftTiles().map (tile) -> tile.getItem()
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
buffer = editor.getBuffer()
|
||||
|
||||
describe "when associated with an unsaved buffer", ->
|
||||
it "displays 'untitled' instead of the buffer's path, but still displays the buffer position", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(fileInfo.currentPath.textContent).toBe 'untitled'
|
||||
expect(cursorPosition.textContent).toBe '1:1'
|
||||
expect(selectionCount).toBeHidden()
|
||||
|
||||
describe "when the associated editor's path changes", ->
|
||||
it "updates the path in the status bar", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt')
|
||||
|
||||
runs ->
|
||||
expect(fileInfo.currentPath.textContent).toBe 'sample.txt'
|
||||
|
||||
describe "when associated with remote file path", ->
|
||||
beforeEach ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
dummyView.getPath = -> 'remote://server:123/folder/remote_file.txt'
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
|
||||
it "updates the path in the status bar", ->
|
||||
# The remote path isn't relativized in the test because no remote directory provider is registered.
|
||||
expect(fileInfo.currentPath.textContent).toBe 'remote://server:123/folder/remote_file.txt'
|
||||
expect(fileInfo.currentPath).toBeVisible()
|
||||
|
||||
it "when the path is clicked", ->
|
||||
fileInfo.currentPath.click()
|
||||
expect(atom.clipboard.read()).toBe '/folder/remote_file.txt'
|
||||
|
||||
it "calls relativize with the remote URL on shift-click", ->
|
||||
spy = spyOn(atom.project, 'relativize').andReturn 'remote_file.txt'
|
||||
event = new MouseEvent('click', shiftKey: true)
|
||||
fileInfo.dispatchEvent(event)
|
||||
expect(atom.clipboard.read()).toBe 'remote_file.txt'
|
||||
expect(spy).toHaveBeenCalledWith 'remote://server:123/folder/remote_file.txt'
|
||||
|
||||
describe "when file info tile is clicked", ->
|
||||
it "copies the absolute path into the clipboard if available", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt')
|
||||
|
||||
runs ->
|
||||
fileInfo.click()
|
||||
expect(atom.clipboard.read()).toBe fileInfo.getActiveItem().getPath()
|
||||
|
||||
describe "when the file info tile is shift-clicked", ->
|
||||
it "copies the relative path into the clipboard if available", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt')
|
||||
|
||||
runs ->
|
||||
event = new MouseEvent('click', shiftKey: true)
|
||||
fileInfo.dispatchEvent(event)
|
||||
expect(atom.clipboard.read()).toBe 'sample.txt'
|
||||
|
||||
describe "when path of an unsaved buffer is clicked", ->
|
||||
it "copies the 'untitled' into clipboard", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
fileInfo.currentPath.click()
|
||||
expect(atom.clipboard.read()).toBe 'untitled'
|
||||
|
||||
describe "when buffer's path is not clicked", ->
|
||||
it "doesn't display a path tooltip", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
expect(document.querySelector('.tooltip')).not.toExist()
|
||||
|
||||
describe "when buffer's path is clicked", ->
|
||||
it "displays path tooltip and the tooltip disappears after ~2 seconds", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
fileInfo.currentPath.click()
|
||||
expect(document.querySelector('.tooltip')).toBeVisible()
|
||||
# extra leeway so test won't fail because tooltip disappeared few milliseconds too late
|
||||
advanceClock(2100)
|
||||
expect(document.querySelector('.tooltip')).not.toExist()
|
||||
|
||||
describe "when saved buffer's path is clicked", ->
|
||||
it "displays a tooltip containing text 'Copied:' and an absolute native path", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt')
|
||||
|
||||
runs ->
|
||||
fileInfo.currentPath.click()
|
||||
expect(document.querySelector('.tooltip')).toHaveText "Copied: #{fileInfo.getActiveItem().getPath()}"
|
||||
|
||||
it "displays a tooltip containing text 'Copied:' for an absolute Unix path", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
dummyView.getPath = -> '/user/path/for/my/file.txt'
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
|
||||
runs ->
|
||||
fileInfo.currentPath.click()
|
||||
expect(document.querySelector('.tooltip')).toHaveText "Copied: #{dummyView.getPath()}"
|
||||
|
||||
it "displays a tooltip containing text 'Copied:' for an absolute Windows path", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
dummyView.getPath = -> 'c:\\user\\path\\for\\my\\file.txt'
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
|
||||
runs ->
|
||||
fileInfo.currentPath.click()
|
||||
expect(document.querySelector('.tooltip')).toHaveText "Copied: #{dummyView.getPath()}"
|
||||
|
||||
describe "when unsaved buffer's path is clicked", ->
|
||||
it "displays a tooltip containing text 'Copied: untitled", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
fileInfo.currentPath.click()
|
||||
expect(document.querySelector('.tooltip')).toHaveText "Copied: untitled"
|
||||
|
||||
describe "when the associated editor's buffer's content changes", ->
|
||||
it "enables the buffer modified indicator", ->
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
editor.insertText("\n")
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true)
|
||||
editor.backspace()
|
||||
|
||||
describe "when the buffer content has changed from the content on disk", ->
|
||||
it "disables the buffer modified indicator on save", ->
|
||||
filePath = path.join(os.tmpdir(), "atom-whitespace.txt")
|
||||
fs.writeFileSync(filePath, "")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
|
||||
runs ->
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
editor.insertText("\n")
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true)
|
||||
|
||||
waitsForPromise ->
|
||||
# TODO - remove this Promise.resolve once atom/atom#14435 lands.
|
||||
Promise.resolve(editor.getBuffer().save())
|
||||
|
||||
runs ->
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
|
||||
it "disables the buffer modified indicator if the content matches again", ->
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
editor.insertText("\n")
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true)
|
||||
editor.backspace()
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
|
||||
it "disables the buffer modified indicator when the change is undone", ->
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
editor.insertText("\n")
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true)
|
||||
editor.undo()
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
it "updates the buffer modified indicator for the new buffer", ->
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt')
|
||||
|
||||
runs ->
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
editor.insertText("\n")
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true)
|
||||
|
||||
it "doesn't update the buffer modified indicator for the old buffer", ->
|
||||
oldBuffer = editor.getBuffer()
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt')
|
||||
|
||||
runs ->
|
||||
oldBuffer.setText("new text")
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false)
|
||||
|
||||
describe "when the associated editor's cursor position changes", ->
|
||||
it "updates the cursor position in the status bar", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(cursorPosition.textContent).toBe '2:3'
|
||||
|
||||
it "does not throw an exception if the cursor is moved as the result of the active pane item changing to a non-editor (regression)", ->
|
||||
waitsForPromise ->
|
||||
Promise.resolve(atom.packages.deactivatePackage('status-bar')) # Wrapped so works with Promise & non-Promise deactivate
|
||||
runs ->
|
||||
atom.workspace.onDidChangeActivePaneItem(-> editor.setCursorScreenPosition([1, 2]))
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('status-bar')
|
||||
runs ->
|
||||
statusBar = workspaceElement.querySelector("status-bar")
|
||||
cursorPosition = statusBar.getLeftTiles()[2].getItem()
|
||||
|
||||
atom.workspace.getActivePane().activateItem(document.createElement('div'))
|
||||
expect(editor.getCursorScreenPosition()).toEqual([1, 2])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(cursorPosition).toBeHidden()
|
||||
|
||||
describe "when the associated editor's selection changes", ->
|
||||
it "updates the selection count in the status bar", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 0]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe ''
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 2]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe '(1, 2)'
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 30]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe "(2, 60)"
|
||||
|
||||
it "does not throw an exception if the cursor is moved as the result of the active pane item changing to a non-editor (regression)", ->
|
||||
waitsForPromise ->
|
||||
Promise.resolve(atom.packages.deactivatePackage('status-bar')) # Wrapped so works with Promise & non-Promise deactivate
|
||||
runs ->
|
||||
atom.workspace.onDidChangeActivePaneItem(-> editor.setSelectedBufferRange([[1, 2], [1, 3]]))
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('status-bar')
|
||||
runs ->
|
||||
statusBar = workspaceElement.querySelector("status-bar")
|
||||
selectionCount = statusBar.getLeftTiles()[3].getItem()
|
||||
|
||||
atom.workspace.getActivePane().activateItem(document.createElement('div'))
|
||||
expect(editor.getSelectedBufferRange()).toEqual([[1, 2], [1, 3]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount).toBeHidden()
|
||||
|
||||
describe "when the active pane item does not implement getCursorBufferPosition()", ->
|
||||
it "hides the cursor position view", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(cursorPosition).toBeHidden()
|
||||
|
||||
describe "when the active pane item implements getTitle() but not getPath()", ->
|
||||
it "displays the title", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
dummyView.getTitle = -> 'View Title'
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
expect(fileInfo.currentPath.textContent).toBe 'View Title'
|
||||
expect(fileInfo.currentPath).toBeVisible()
|
||||
|
||||
describe "when the active pane item neither getTitle() nor getPath()", ->
|
||||
it "hides the path view", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
expect(fileInfo.currentPath).toBeHidden()
|
||||
|
||||
describe "when the active pane item's title changes", ->
|
||||
it "updates the path view with the new title", ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
callbacks = []
|
||||
dummyView.onDidChangeTitle = (fn) ->
|
||||
callbacks.push(fn)
|
||||
{
|
||||
dispose: ->
|
||||
}
|
||||
dummyView.getTitle = -> 'View Title'
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
expect(fileInfo.currentPath.textContent).toBe 'View Title'
|
||||
dummyView.getTitle = -> 'New Title'
|
||||
callback() for callback in callbacks
|
||||
expect(fileInfo.currentPath.textContent).toBe 'New Title'
|
||||
|
||||
describe 'the cursor position tile', ->
|
||||
beforeEach ->
|
||||
atom.config.set('status-bar.cursorPositionFormat', 'foo %L bar %C')
|
||||
|
||||
it 'respects a format string', ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(cursorPosition.textContent).toBe 'foo 2 bar 3'
|
||||
|
||||
it 'updates when the configuration changes', ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(cursorPosition.textContent).toBe 'foo 2 bar 3'
|
||||
|
||||
atom.config.set('status-bar.cursorPositionFormat', 'baz %C quux %L')
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(cursorPosition.textContent).toBe 'baz 3 quux 2'
|
||||
|
||||
describe "when clicked", ->
|
||||
it "triggers the go-to-line toggle event", ->
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
atom.commands.add('atom-text-editor', 'go-to-line:toggle', eventHandler)
|
||||
cursorPosition.click()
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe 'the selection count tile', ->
|
||||
beforeEach ->
|
||||
atom.config.set('status-bar.selectionCountFormat', '%L foo %C bar selected')
|
||||
|
||||
it 'respects a format string', ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 30]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe "2 foo 60 bar selected"
|
||||
|
||||
it 'updates when the configuration changes', ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 30]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe "2 foo 60 bar selected"
|
||||
|
||||
atom.config.set('status-bar.selectionCountFormat', 'Selection: baz %C quux %L')
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe "Selection: baz 60 quux 2"
|
||||
|
||||
it 'does not include the next line if the last selected character is a LF', ->
|
||||
lineEndingRegExp = /\r\n|\n|\r/g
|
||||
buffer = editor.getBuffer()
|
||||
buffer.setText(buffer.getText().replace(lineEndingRegExp, '\n'))
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 0]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe "1 foo 30 bar selected"
|
||||
|
||||
it 'does not include the next line if the last selected character is CRLF', ->
|
||||
lineEndingRegExp = /\r\n|\n|\r/g
|
||||
buffer = editor.getBuffer()
|
||||
buffer.setText(buffer.getText().replace(lineEndingRegExp, '\r\n'))
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 0]])
|
||||
atom.views.performDocumentUpdate()
|
||||
expect(selectionCount.textContent).toBe "1 foo 31 bar selected"
|
||||
|
||||
describe "the git tile", ->
|
||||
gitView = null
|
||||
|
||||
hover = (element, fn) ->
|
||||
# FIXME: Only use hoverDefaults once Atom 1.13 is on stable
|
||||
hoverDelay = atom.tooltips.defaults.delay?.show ? atom.tooltips.hoverDefaults.delay.show
|
||||
element.dispatchEvent(new CustomEvent('mouseenter', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseover', bubbles: true))
|
||||
advanceClock(hoverDelay)
|
||||
fn()
|
||||
element.dispatchEvent(new CustomEvent('mouseleave', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseout', bubbles: true))
|
||||
advanceClock(hoverDelay)
|
||||
|
||||
setupWorkingDir = (name) ->
|
||||
dir = atom.project.getDirectories()[0]
|
||||
target = "#{os.tmpdir()}/#{name}"
|
||||
targetGit = target + '/.git'
|
||||
fs.copySync(dir.resolve('git/working-dir'), path.resolve(target))
|
||||
fs.removeSync(path.resolve(targetGit))
|
||||
fs.copySync(dir.resolve("git/#{name}.git"), path.resolve(targetGit))
|
||||
target
|
||||
|
||||
beforeEach ->
|
||||
[gitView] = statusBar.getRightTiles().map (tile) -> tile.getItem()
|
||||
|
||||
describe "the git ahead/behind count labels", ->
|
||||
beforeEach ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
|
||||
it "shows the number of commits that can be pushed/pulled", ->
|
||||
workingDir = setupWorkingDir('ahead-behind-repo')
|
||||
atom.project.setPaths([workingDir])
|
||||
filePath = atom.project.getDirectories()[0].resolve('a.txt')
|
||||
repo = atom.project.getRepositories()[0]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then -> repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
behindElement = document.body.querySelector(".commits-behind-label")
|
||||
aheadElement = document.body.querySelector(".commits-ahead-label")
|
||||
expect(aheadElement).toBeVisible()
|
||||
expect(behindElement).toBeVisible()
|
||||
expect(aheadElement.textContent).toContain '1'
|
||||
|
||||
it "stays hidden when no commits can be pushed/pulled", ->
|
||||
workingDir = setupWorkingDir('no-ahead-behind-repo')
|
||||
atom.project.setPaths([workingDir])
|
||||
filePath = atom.project.getDirectories()[0].resolve('a.txt')
|
||||
repo = atom.project.getRepositories()[0]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then -> repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
behindElement = document.body.querySelector(".commits-behind-label")
|
||||
aheadElement = document.body.querySelector(".commits-ahead-label")
|
||||
expect(aheadElement).not.toBeVisible()
|
||||
expect(behindElement).not.toBeVisible()
|
||||
|
||||
describe "the git branch label", ->
|
||||
projectPath = null
|
||||
beforeEach ->
|
||||
projectPath = atom.project.getDirectories()[0].resolve('git/working-dir')
|
||||
fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'))
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
|
||||
afterEach ->
|
||||
fs.moveSync(path.join(projectPath, '.git'), path.join(projectPath, 'git.git'))
|
||||
|
||||
it "displays the current branch for files in repositories", ->
|
||||
atom.project.setPaths([projectPath])
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('a.txt')
|
||||
|
||||
runs ->
|
||||
currentBranch = atom.project.getRepositories()[0].getShortHead()
|
||||
expect(gitView.branchArea).toBeVisible()
|
||||
expect(gitView.branchLabel.textContent).toBe currentBranch
|
||||
|
||||
atom.workspace.getActivePane().destroyItems()
|
||||
expect(gitView.branchArea).toBeVisible()
|
||||
expect(gitView.branchLabel.textContent).toBe currentBranch
|
||||
|
||||
atom.workspace.getActivePane().activateItem(dummyView)
|
||||
|
||||
runs -> expect(gitView.branchArea).not.toBeVisible()
|
||||
|
||||
it "displays the current branch tooltip", ->
|
||||
atom.project.setPaths([projectPath])
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('a.txt')
|
||||
|
||||
runs ->
|
||||
currentBranch = atom.project.getRepositories()[0].getShortHead()
|
||||
hover gitView.branchArea, ->
|
||||
expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("On branch #{currentBranch}")
|
||||
|
||||
it "doesn't display the current branch for a file not in a repository", ->
|
||||
atom.project.setPaths([os.tmpdir()])
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(path.join(os.tmpdir(), 'temp.txt'))
|
||||
|
||||
runs ->
|
||||
expect(gitView.branchArea).toBeHidden()
|
||||
|
||||
it "doesn't display the current branch for a file outside the current project", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(path.join(os.tmpdir(), 'atom-specs', 'not-in-project.txt'))
|
||||
|
||||
runs ->
|
||||
expect(gitView.branchArea).toBeHidden()
|
||||
|
||||
describe "the git status label", ->
|
||||
[repo, filePath, originalPathText, newPath, ignorePath, ignoredPath, projectPath] = []
|
||||
|
||||
beforeEach ->
|
||||
projectPath = atom.project.getDirectories()[0].resolve('git/working-dir')
|
||||
fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'))
|
||||
atom.project.setPaths([projectPath])
|
||||
filePath = atom.project.getDirectories()[0].resolve('a.txt')
|
||||
newPath = atom.project.getDirectories()[0].resolve('new.txt')
|
||||
fs.writeFileSync(newPath, "I'm new here")
|
||||
ignorePath = path.join(projectPath, '.gitignore')
|
||||
fs.writeFileSync(ignorePath, 'ignored.txt')
|
||||
ignoredPath = path.join(projectPath, 'ignored.txt')
|
||||
fs.writeFileSync(ignoredPath, '')
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
|
||||
repo = atom.project.getRepositories()[0]
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
waitsForPromise -> repo.refreshStatus()
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
fs.removeSync(newPath)
|
||||
fs.removeSync(ignorePath)
|
||||
fs.removeSync(ignoredPath)
|
||||
fs.moveSync(path.join(projectPath, '.git'), path.join(projectPath, 'git.git'))
|
||||
|
||||
it "displays the modified icon for a changed file", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then ->
|
||||
fs.writeFileSync(filePath, "i've changed for the worse")
|
||||
repo.refreshStatus()
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toHaveClass('icon-diff-modified')
|
||||
|
||||
it "displays the 1 line added and not committed tooltip", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then ->
|
||||
fs.writeFileSync(filePath, "i've changed for the worse")
|
||||
repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
hover gitView.gitStatusIcon, ->
|
||||
expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("1 line added to this file not yet committed")
|
||||
|
||||
it "displays the x lines added and not committed tooltip", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then ->
|
||||
fs.writeFileSync(filePath, "i've changed#{os.EOL}for the worse")
|
||||
repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
hover gitView.gitStatusIcon, ->
|
||||
expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("2 lines added to this file not yet committed")
|
||||
|
||||
it "doesn't display the modified icon for an unchanged file", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then -> repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toHaveText('')
|
||||
|
||||
it "displays the new icon for a new file", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(newPath)
|
||||
.then -> repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toHaveClass('icon-diff-added')
|
||||
hover gitView.gitStatusIcon, ->
|
||||
expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("1 line in this new file not yet committed")
|
||||
|
||||
it "displays the 1 line added and not committed to new file tooltip", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(newPath)
|
||||
.then -> repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
hover gitView.gitStatusIcon, ->
|
||||
expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("1 line in this new file not yet committed")
|
||||
|
||||
it "displays the x lines added and not committed to new file tooltip", ->
|
||||
fs.writeFileSync(newPath, "I'm new#{os.EOL}here")
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(newPath)
|
||||
.then -> repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
hover gitView.gitStatusIcon, ->
|
||||
expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("2 lines in this new file not yet committed")
|
||||
|
||||
it "displays the ignored icon for an ignored file", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(ignoredPath)
|
||||
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toHaveClass('icon-diff-ignored')
|
||||
hover gitView.gitStatusIcon, ->
|
||||
expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("File is ignored by git")
|
||||
|
||||
it "updates when a status-changed event occurs", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then ->
|
||||
fs.writeFileSync(filePath, "i've changed for the worse")
|
||||
repo.refreshStatus()
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toHaveClass('icon-diff-modified')
|
||||
|
||||
waitsForPromise ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
repo.refreshStatus()
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).not.toHaveClass('icon-diff-modified')
|
||||
|
||||
it "displays the diff stat for modified files", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath)
|
||||
.then ->
|
||||
fs.writeFileSync(filePath, "i've changed for the worse")
|
||||
repo.refreshStatus()
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toHaveText('+1')
|
||||
|
||||
it "displays the diff stat for new files", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(newPath)
|
||||
.then -> repo.refreshStatus()
|
||||
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toHaveText('+1')
|
||||
|
||||
it "does not display for files not in the current project", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('/tmp/atom-specs/not-in-project.txt')
|
||||
|
||||
runs ->
|
||||
expect(gitView.gitStatusIcon).toBeHidden()
|
684
packages/status-bar/spec/built-in-tiles-spec.js
Normal file
684
packages/status-bar/spec/built-in-tiles-spec.js
Normal file
@ -0,0 +1,684 @@
|
||||
|
||||
const fs = require('fs-plus');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const process = require('process');
|
||||
|
||||
describe("Built-in Status Bar Tiles", function() {
|
||||
let [statusBar, workspaceElement, dummyView] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
dummyView = document.createElement("div");
|
||||
statusBar = null;
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage('status-bar'));
|
||||
|
||||
runs(() => statusBar = workspaceElement.querySelector("status-bar"));
|
||||
});
|
||||
|
||||
describe("the file info, cursor and selection tiles", function() {
|
||||
let [editor, buffer, fileInfo, cursorPosition, selectionCount] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
waitsForPromise(() => atom.workspace.open('sample.js'));
|
||||
|
||||
runs(function() {
|
||||
let launchMode;
|
||||
[launchMode, fileInfo, cursorPosition, selectionCount] =
|
||||
statusBar.getLeftTiles().map(tile => tile.getItem());
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
return buffer = editor.getBuffer();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when associated with an unsaved buffer", () => it("displays 'untitled' instead of the buffer's path, but still displays the buffer position", function() {
|
||||
waitsForPromise(() => atom.workspace.open());
|
||||
|
||||
runs(function() {
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(fileInfo.currentPath.textContent).toBe('untitled');
|
||||
expect(cursorPosition.textContent).toBe('1:1');
|
||||
expect(selectionCount).toBeHidden();
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when the associated editor's path changes", () => it("updates the path in the status bar", function() {
|
||||
waitsForPromise(() => atom.workspace.open('sample.txt'));
|
||||
|
||||
runs(() => expect(fileInfo.currentPath.textContent).toBe('sample.txt'));
|
||||
}));
|
||||
|
||||
describe("when associated with remote file path", function() {
|
||||
beforeEach(function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
dummyView.getPath = () => 'remote://server:123/folder/remote_file.txt';
|
||||
return atom.workspace.getActivePane().activateItem(dummyView);
|
||||
});
|
||||
|
||||
it("updates the path in the status bar", function() {
|
||||
// The remote path isn't relativized in the test because no remote directory provider is registered.
|
||||
expect(fileInfo.currentPath.textContent).toBe('remote://server:123/folder/remote_file.txt');
|
||||
expect(fileInfo.currentPath).toBeVisible();
|
||||
});
|
||||
|
||||
it("when the path is clicked", function() {
|
||||
fileInfo.currentPath.click();
|
||||
expect(atom.clipboard.read()).toBe('/folder/remote_file.txt');
|
||||
});
|
||||
|
||||
it("calls relativize with the remote URL on shift-click", function() {
|
||||
const spy = spyOn(atom.project, 'relativize').andReturn('remote_file.txt');
|
||||
const event = new MouseEvent('click', {shiftKey: true});
|
||||
fileInfo.dispatchEvent(event);
|
||||
expect(atom.clipboard.read()).toBe('remote_file.txt');
|
||||
expect(spy).toHaveBeenCalledWith('remote://server:123/folder/remote_file.txt');
|
||||
});
|
||||
});
|
||||
|
||||
describe("when file info tile is clicked", () => it("copies the absolute path into the clipboard if available", function() {
|
||||
waitsForPromise(() => atom.workspace.open('sample.txt'));
|
||||
|
||||
runs(function() {
|
||||
fileInfo.click();
|
||||
expect(atom.clipboard.read()).toBe(fileInfo.getActiveItem().getPath());
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when the file info tile is shift-clicked", () => it("copies the relative path into the clipboard if available", function() {
|
||||
waitsForPromise(() => atom.workspace.open('sample.txt'));
|
||||
|
||||
runs(function() {
|
||||
const event = new MouseEvent('click', {shiftKey: true});
|
||||
fileInfo.dispatchEvent(event);
|
||||
expect(atom.clipboard.read()).toBe('sample.txt');
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when path of an unsaved buffer is clicked", () => it("copies the 'untitled' into clipboard", function() {
|
||||
waitsForPromise(() => atom.workspace.open());
|
||||
|
||||
runs(function() {
|
||||
fileInfo.currentPath.click();
|
||||
expect(atom.clipboard.read()).toBe('untitled');
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when buffer's path is not clicked", () => it("doesn't display a path tooltip", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
waitsForPromise(() => atom.workspace.open());
|
||||
|
||||
runs(() => expect(document.querySelector('.tooltip')).not.toExist());
|
||||
}));
|
||||
|
||||
describe("when buffer's path is clicked", () => it("displays path tooltip and the tooltip disappears after ~2 seconds", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
waitsForPromise(() => atom.workspace.open());
|
||||
|
||||
runs(function() {
|
||||
fileInfo.currentPath.click();
|
||||
expect(document.querySelector('.tooltip')).toBeVisible();
|
||||
// extra leeway so test won't fail because tooltip disappeared few milliseconds too late
|
||||
advanceClock(2100);
|
||||
expect(document.querySelector('.tooltip')).not.toExist();
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when saved buffer's path is clicked", function() {
|
||||
it("displays a tooltip containing text 'Copied:' and an absolute native path", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
waitsForPromise(() => atom.workspace.open('sample.txt'));
|
||||
|
||||
runs(function() {
|
||||
fileInfo.currentPath.click();
|
||||
expect(document.querySelector('.tooltip')).toHaveText(`Copied: ${fileInfo.getActiveItem().getPath()}`);
|
||||
});
|
||||
});
|
||||
|
||||
it("displays a tooltip containing text 'Copied:' for an absolute Unix path", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
dummyView.getPath = () => '/user/path/for/my/file.txt';
|
||||
atom.workspace.getActivePane().activateItem(dummyView);
|
||||
|
||||
runs(function() {
|
||||
fileInfo.currentPath.click();
|
||||
expect(document.querySelector('.tooltip')).toHaveText(`Copied: ${dummyView.getPath()}`);
|
||||
});
|
||||
});
|
||||
|
||||
it("displays a tooltip containing text 'Copied:' for an absolute Windows path", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
dummyView.getPath = () => 'c:\\user\\path\\for\\my\\file.txt';
|
||||
atom.workspace.getActivePane().activateItem(dummyView);
|
||||
|
||||
runs(function() {
|
||||
fileInfo.currentPath.click();
|
||||
expect(document.querySelector('.tooltip')).toHaveText(`Copied: ${dummyView.getPath()}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when unsaved buffer's path is clicked", () => it("displays a tooltip containing text 'Copied: untitled", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
waitsForPromise(() => atom.workspace.open());
|
||||
|
||||
runs(function() {
|
||||
fileInfo.currentPath.click();
|
||||
expect(document.querySelector('.tooltip')).toHaveText("Copied: untitled");
|
||||
});
|
||||
}));
|
||||
|
||||
describe("when the associated editor's buffer's content changes", () => it("enables the buffer modified indicator", function() {
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
editor.insertText("\n");
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true);
|
||||
return editor.backspace();
|
||||
}));
|
||||
|
||||
describe("when the buffer content has changed from the content on disk", function() {
|
||||
it("disables the buffer modified indicator on save", function() {
|
||||
const filePath = path.join(os.tmpdir(), "atom-whitespace.txt");
|
||||
fs.writeFileSync(filePath, "");
|
||||
|
||||
waitsForPromise(() => atom.workspace.open(filePath));
|
||||
|
||||
runs(function() {
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
editor.insertText("\n");
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true);
|
||||
});
|
||||
|
||||
waitsForPromise(() => // TODO - remove this Promise.resolve once atom/atom#14435 lands.
|
||||
Promise.resolve(editor.getBuffer().save()));
|
||||
|
||||
runs(() => expect(fileInfo.classList.contains('buffer-modified')).toBe(false));
|
||||
});
|
||||
|
||||
it("disables the buffer modified indicator if the content matches again", function() {
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
editor.insertText("\n");
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true);
|
||||
editor.backspace();
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
});
|
||||
|
||||
it("disables the buffer modified indicator when the change is undone", function() {
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
editor.insertText("\n");
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true);
|
||||
editor.undo();
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the buffer changes", function() {
|
||||
it("updates the buffer modified indicator for the new buffer", function() {
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('sample.txt'));
|
||||
|
||||
runs(function() {
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.insertText("\n");
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't update the buffer modified indicator for the old buffer", function() {
|
||||
const oldBuffer = editor.getBuffer();
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('sample.txt'));
|
||||
|
||||
runs(function() {
|
||||
oldBuffer.setText("new text");
|
||||
advanceClock(buffer.stoppedChangingDelay);
|
||||
expect(fileInfo.classList.contains('buffer-modified')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the associated editor's cursor position changes", function() {
|
||||
it("updates the cursor position in the status bar", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor.setCursorScreenPosition([1, 2]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(cursorPosition.textContent).toBe('2:3');
|
||||
});
|
||||
|
||||
it("does not throw an exception if the cursor is moved as the result of the active pane item changing to a non-editor (regression)", function() {
|
||||
waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackage('status-bar'))); // Wrapped so works with Promise & non-Promise deactivate
|
||||
runs(() => atom.workspace.onDidChangeActivePaneItem(() => editor.setCursorScreenPosition([1, 2])));
|
||||
waitsForPromise(() => atom.packages.activatePackage('status-bar'));
|
||||
runs(function() {
|
||||
statusBar = workspaceElement.querySelector("status-bar");
|
||||
cursorPosition = statusBar.getLeftTiles()[2].getItem();
|
||||
|
||||
atom.workspace.getActivePane().activateItem(document.createElement('div'));
|
||||
expect(editor.getCursorScreenPosition()).toEqual([1, 2]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(cursorPosition).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the associated editor's selection changes", function() {
|
||||
it("updates the selection count in the status bar", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 0]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe('');
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 2]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe('(1, 2)');
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 30]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe("(2, 60)");
|
||||
});
|
||||
|
||||
it("does not throw an exception if the cursor is moved as the result of the active pane item changing to a non-editor (regression)", function() {
|
||||
waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackage('status-bar'))); // Wrapped so works with Promise & non-Promise deactivate
|
||||
runs(() => atom.workspace.onDidChangeActivePaneItem(() => editor.setSelectedBufferRange([[1, 2], [1, 3]])));
|
||||
waitsForPromise(() => atom.packages.activatePackage('status-bar'));
|
||||
runs(function() {
|
||||
statusBar = workspaceElement.querySelector("status-bar");
|
||||
selectionCount = statusBar.getLeftTiles()[3].getItem();
|
||||
|
||||
atom.workspace.getActivePane().activateItem(document.createElement('div'));
|
||||
expect(editor.getSelectedBufferRange()).toEqual([[1, 2], [1, 3]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the active pane item does not implement getCursorBufferPosition()", () => it("hides the cursor position view", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
atom.workspace.getActivePane().activateItem(dummyView);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(cursorPosition).toBeHidden();
|
||||
}));
|
||||
|
||||
describe("when the active pane item implements getTitle() but not getPath()", () => it("displays the title", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
dummyView.getTitle = () => 'View Title';
|
||||
atom.workspace.getActivePane().activateItem(dummyView);
|
||||
expect(fileInfo.currentPath.textContent).toBe('View Title');
|
||||
expect(fileInfo.currentPath).toBeVisible();
|
||||
}));
|
||||
|
||||
describe("when the active pane item neither getTitle() nor getPath()", () => it("hides the path view", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
atom.workspace.getActivePane().activateItem(dummyView);
|
||||
expect(fileInfo.currentPath).toBeHidden();
|
||||
}));
|
||||
|
||||
describe("when the active pane item's title changes", () => it("updates the path view with the new title", function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
const callbacks = [];
|
||||
dummyView.onDidChangeTitle = function(fn) {
|
||||
callbacks.push(fn);
|
||||
return {
|
||||
dispose() {}
|
||||
};
|
||||
};
|
||||
dummyView.getTitle = () => 'View Title';
|
||||
atom.workspace.getActivePane().activateItem(dummyView);
|
||||
expect(fileInfo.currentPath.textContent).toBe('View Title');
|
||||
dummyView.getTitle = () => 'New Title';
|
||||
for (let callback of Array.from(callbacks)) { callback(); }
|
||||
expect(fileInfo.currentPath.textContent).toBe('New Title');
|
||||
}));
|
||||
|
||||
describe('the cursor position tile', function() {
|
||||
beforeEach(() => atom.config.set('status-bar.cursorPositionFormat', 'foo %L bar %C'));
|
||||
|
||||
it('respects a format string', function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor.setCursorScreenPosition([1, 2]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(cursorPosition.textContent).toBe('foo 2 bar 3');
|
||||
});
|
||||
|
||||
it('updates when the configuration changes', function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor.setCursorScreenPosition([1, 2]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(cursorPosition.textContent).toBe('foo 2 bar 3');
|
||||
|
||||
atom.config.set('status-bar.cursorPositionFormat', 'baz %C quux %L');
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(cursorPosition.textContent).toBe('baz 3 quux 2');
|
||||
});
|
||||
|
||||
describe("when clicked", () => it("triggers the go-to-line toggle event", function() {
|
||||
const eventHandler = jasmine.createSpy('eventHandler');
|
||||
atom.commands.add('atom-text-editor', 'go-to-line:toggle', eventHandler);
|
||||
cursorPosition.click();
|
||||
expect(eventHandler).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('the selection count tile', function() {
|
||||
beforeEach(() => atom.config.set('status-bar.selectionCountFormat', '%L foo %C bar selected'));
|
||||
|
||||
it('respects a format string', function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 30]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe("2 foo 60 bar selected");
|
||||
});
|
||||
|
||||
it('updates when the configuration changes', function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 30]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe("2 foo 60 bar selected");
|
||||
|
||||
atom.config.set('status-bar.selectionCountFormat', 'Selection: baz %C quux %L');
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe("Selection: baz 60 quux 2");
|
||||
});
|
||||
|
||||
it('does not include the next line if the last selected character is a LF', function() {
|
||||
const lineEndingRegExp = /\r\n|\n|\r/g;
|
||||
buffer = editor.getBuffer();
|
||||
buffer.setText(buffer.getText().replace(lineEndingRegExp, '\n'));
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 0]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe("1 foo 30 bar selected");
|
||||
});
|
||||
|
||||
it('does not include the next line if the last selected character is CRLF', function() {
|
||||
const lineEndingRegExp = /\r\n|\n|\r/g;
|
||||
buffer = editor.getBuffer();
|
||||
buffer.setText(buffer.getText().replace(lineEndingRegExp, '\r\n'));
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor.setSelectedBufferRange([[0, 0], [1, 0]]);
|
||||
atom.views.performDocumentUpdate();
|
||||
expect(selectionCount.textContent).toBe("1 foo 31 bar selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("the git tile", function() {
|
||||
let gitView = null;
|
||||
|
||||
const hover = function(element, fn) {
|
||||
// FIXME: Only use hoverDefaults once Atom 1.13 is on stable
|
||||
const hoverDelay = atom.tooltips.defaults.delay?.show != null ? atom.tooltips.defaults.delay?.show : atom.tooltips.hoverDefaults.delay.show;
|
||||
element.dispatchEvent(new CustomEvent('mouseenter', {bubbles: false}));
|
||||
element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true}));
|
||||
advanceClock(hoverDelay);
|
||||
fn();
|
||||
element.dispatchEvent(new CustomEvent('mouseleave', {bubbles: false}));
|
||||
element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true}));
|
||||
return advanceClock(hoverDelay);
|
||||
};
|
||||
|
||||
const setupWorkingDir = function(name) {
|
||||
const dir = atom.project.getDirectories()[0];
|
||||
const target = `${os.tmpdir()}/${name}`;
|
||||
const targetGit = target + '/.git';
|
||||
fs.copySync(dir.resolve('git/working-dir'), path.resolve(target));
|
||||
fs.removeSync(path.resolve(targetGit));
|
||||
fs.copySync(dir.resolve(`git/${name}.git`), path.resolve(targetGit));
|
||||
return target;
|
||||
};
|
||||
|
||||
beforeEach(() => [gitView] = statusBar.getRightTiles().map(tile => tile.getItem()));
|
||||
|
||||
describe("the git ahead/behind count labels", function() {
|
||||
beforeEach(() => jasmine.attachToDOM(workspaceElement));
|
||||
|
||||
it("shows the number of commits that can be pushed/pulled", function() {
|
||||
const workingDir = setupWorkingDir('ahead-behind-repo');
|
||||
atom.project.setPaths([workingDir]);
|
||||
const filePath = atom.project.getDirectories()[0].resolve('a.txt');
|
||||
const repo = atom.project.getRepositories()[0];
|
||||
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(() => repo.refreshStatus()));
|
||||
|
||||
runs(function() {
|
||||
const behindElement = document.body.querySelector(".commits-behind-label");
|
||||
const aheadElement = document.body.querySelector(".commits-ahead-label");
|
||||
expect(aheadElement).toBeVisible();
|
||||
expect(behindElement).toBeVisible();
|
||||
expect(aheadElement.textContent).toContain('1');
|
||||
});
|
||||
});
|
||||
|
||||
it("stays hidden when no commits can be pushed/pulled", function() {
|
||||
const workingDir = setupWorkingDir('no-ahead-behind-repo');
|
||||
atom.project.setPaths([workingDir]);
|
||||
const filePath = atom.project.getDirectories()[0].resolve('a.txt');
|
||||
const repo = atom.project.getRepositories()[0];
|
||||
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(() => repo.refreshStatus()));
|
||||
|
||||
runs(function() {
|
||||
const behindElement = document.body.querySelector(".commits-behind-label");
|
||||
const aheadElement = document.body.querySelector(".commits-ahead-label");
|
||||
expect(aheadElement).not.toBeVisible();
|
||||
expect(behindElement).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("the git branch label", function() {
|
||||
let projectPath = null;
|
||||
beforeEach(function() {
|
||||
projectPath = atom.project.getDirectories()[0].resolve('git/working-dir');
|
||||
fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'));
|
||||
return jasmine.attachToDOM(workspaceElement);
|
||||
});
|
||||
|
||||
afterEach(() => fs.moveSync(path.join(projectPath, '.git'), path.join(projectPath, 'git.git')));
|
||||
|
||||
it("displays the current branch for files in repositories", function() {
|
||||
atom.project.setPaths([projectPath]);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('a.txt'));
|
||||
|
||||
runs(function() {
|
||||
const currentBranch = atom.project.getRepositories()[0].getShortHead();
|
||||
expect(gitView.branchArea).toBeVisible();
|
||||
expect(gitView.branchLabel.textContent).toBe(currentBranch);
|
||||
|
||||
atom.workspace.getActivePane().destroyItems();
|
||||
expect(gitView.branchArea).toBeVisible();
|
||||
expect(gitView.branchLabel.textContent).toBe(currentBranch);
|
||||
|
||||
return atom.workspace.getActivePane().activateItem(dummyView);
|
||||
});
|
||||
|
||||
runs(() => expect(gitView.branchArea).not.toBeVisible());
|
||||
});
|
||||
|
||||
it("displays the current branch tooltip", function() {
|
||||
atom.project.setPaths([projectPath]);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('a.txt'));
|
||||
|
||||
runs(function() {
|
||||
const currentBranch = atom.project.getRepositories()[0].getShortHead();
|
||||
return hover(gitView.branchArea, () => expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe(`On branch ${currentBranch}`));
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't display the current branch for a file not in a repository", function() {
|
||||
atom.project.setPaths([os.tmpdir()]);
|
||||
|
||||
waitsForPromise(() => atom.workspace.open(path.join(os.tmpdir(), 'temp.txt')));
|
||||
|
||||
runs(() => expect(gitView.branchArea).toBeHidden());
|
||||
});
|
||||
|
||||
it("doesn't display the current branch for a file outside the current project", function() {
|
||||
waitsForPromise(() => atom.workspace.open(path.join(os.tmpdir(), 'atom-specs', 'not-in-project.txt')));
|
||||
|
||||
runs(() => expect(gitView.branchArea).toBeHidden());
|
||||
});
|
||||
});
|
||||
|
||||
describe("the git status label", function() {
|
||||
let [repo, filePath, originalPathText, newPath, ignorePath, ignoredPath, projectPath] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
projectPath = atom.project.getDirectories()[0].resolve('git/working-dir');
|
||||
fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'));
|
||||
atom.project.setPaths([projectPath]);
|
||||
filePath = atom.project.getDirectories()[0].resolve('a.txt');
|
||||
newPath = atom.project.getDirectories()[0].resolve('new.txt');
|
||||
fs.writeFileSync(newPath, "I'm new here");
|
||||
ignorePath = path.join(projectPath, '.gitignore');
|
||||
fs.writeFileSync(ignorePath, 'ignored.txt');
|
||||
ignoredPath = path.join(projectPath, 'ignored.txt');
|
||||
fs.writeFileSync(ignoredPath, '');
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
|
||||
repo = atom.project.getRepositories()[0];
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8');
|
||||
return waitsForPromise(() => repo.refreshStatus());
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
fs.writeFileSync(filePath, originalPathText);
|
||||
fs.removeSync(newPath);
|
||||
fs.removeSync(ignorePath);
|
||||
fs.removeSync(ignoredPath);
|
||||
return fs.moveSync(path.join(projectPath, '.git'), path.join(projectPath, 'git.git'));
|
||||
});
|
||||
|
||||
it("displays the modified icon for a changed file", function() {
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(function() {
|
||||
fs.writeFileSync(filePath, "i've changed for the worse");
|
||||
return repo.refreshStatus();
|
||||
}));
|
||||
runs(() => expect(gitView.gitStatusIcon).toHaveClass('icon-diff-modified'));
|
||||
});
|
||||
|
||||
it("displays the 1 line added and not committed tooltip", function() {
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(function() {
|
||||
fs.writeFileSync(filePath, "i've changed for the worse");
|
||||
return repo.refreshStatus();
|
||||
}));
|
||||
|
||||
runs(() => hover(gitView.gitStatusIcon, () => expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("1 line added to this file not yet committed")));
|
||||
});
|
||||
|
||||
it("displays the x lines added and not committed tooltip", function() {
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(function() {
|
||||
fs.writeFileSync(filePath, `i've changed${os.EOL}for the worse`);
|
||||
return repo.refreshStatus();
|
||||
}));
|
||||
|
||||
runs(() => hover(gitView.gitStatusIcon, () => expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("2 lines added to this file not yet committed")));
|
||||
});
|
||||
|
||||
it("doesn't display the modified icon for an unchanged file", function() {
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(() => repo.refreshStatus()));
|
||||
|
||||
runs(() => expect(gitView.gitStatusIcon).toHaveText(''));
|
||||
});
|
||||
|
||||
it("displays the new icon for a new file", function() {
|
||||
waitsForPromise(() => atom.workspace.open(newPath)
|
||||
.then(() => repo.refreshStatus()));
|
||||
|
||||
runs(function() {
|
||||
expect(gitView.gitStatusIcon).toHaveClass('icon-diff-added');
|
||||
return hover(gitView.gitStatusIcon, () => expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("1 line in this new file not yet committed"));
|
||||
});
|
||||
});
|
||||
|
||||
it("displays the 1 line added and not committed to new file tooltip", function() {
|
||||
waitsForPromise(() => atom.workspace.open(newPath)
|
||||
.then(() => repo.refreshStatus()));
|
||||
|
||||
runs(() => hover(gitView.gitStatusIcon, () => expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("1 line in this new file not yet committed")));
|
||||
});
|
||||
|
||||
it("displays the x lines added and not committed to new file tooltip", function() {
|
||||
fs.writeFileSync(newPath, `I'm new${os.EOL}here`);
|
||||
waitsForPromise(() => atom.workspace.open(newPath)
|
||||
.then(() => repo.refreshStatus()));
|
||||
|
||||
runs(() => hover(gitView.gitStatusIcon, () => expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("2 lines in this new file not yet committed")));
|
||||
});
|
||||
|
||||
it("displays the ignored icon for an ignored file", function() {
|
||||
waitsForPromise(() => atom.workspace.open(ignoredPath));
|
||||
|
||||
runs(function() {
|
||||
expect(gitView.gitStatusIcon).toHaveClass('icon-diff-ignored');
|
||||
return hover(gitView.gitStatusIcon, () => expect(document.body.querySelector(".tooltip").innerText)
|
||||
.toBe("File is ignored by git"));
|
||||
});
|
||||
});
|
||||
|
||||
it("updates when a status-changed event occurs", function() {
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(function() {
|
||||
fs.writeFileSync(filePath, "i've changed for the worse");
|
||||
return repo.refreshStatus();
|
||||
}));
|
||||
runs(function() {
|
||||
expect(gitView.gitStatusIcon).toHaveClass('icon-diff-modified');
|
||||
|
||||
waitsForPromise(function() {
|
||||
fs.writeFileSync(filePath, originalPathText);
|
||||
return repo.refreshStatus();
|
||||
});
|
||||
runs(() => expect(gitView.gitStatusIcon).not.toHaveClass('icon-diff-modified'));
|
||||
});
|
||||
});
|
||||
|
||||
it("displays the diff stat for modified files", function() {
|
||||
waitsForPromise(() => atom.workspace.open(filePath)
|
||||
.then(function() {
|
||||
fs.writeFileSync(filePath, "i've changed for the worse");
|
||||
return repo.refreshStatus();
|
||||
}));
|
||||
runs(() => expect(gitView.gitStatusIcon).toHaveText('+1'));
|
||||
});
|
||||
|
||||
it("displays the diff stat for new files", function() {
|
||||
waitsForPromise(() => atom.workspace.open(newPath)
|
||||
.then(() => repo.refreshStatus()));
|
||||
|
||||
runs(() => expect(gitView.gitStatusIcon).toHaveText('+1'));
|
||||
});
|
||||
|
||||
it("does not display for files not in the current project", function() {
|
||||
waitsForPromise(() => atom.workspace.open('/tmp/atom-specs/not-in-project.txt'));
|
||||
|
||||
runs(() => expect(gitView.gitStatusIcon).toBeHidden());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,111 +0,0 @@
|
||||
describe "Status Bar package", ->
|
||||
[editor, statusBar, statusBarService, workspaceElement, mainModule] = []
|
||||
|
||||
beforeEach ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('status-bar').then (pack) ->
|
||||
statusBar = workspaceElement.querySelector("status-bar")
|
||||
statusBarService = pack.mainModule.provideStatusBar()
|
||||
{mainModule} = pack
|
||||
|
||||
describe "@activate()", ->
|
||||
it "appends only one status bar", ->
|
||||
expect(workspaceElement.querySelectorAll('status-bar').length).toBe 1
|
||||
atom.workspace.getActivePane().splitRight(copyActiveItem: true)
|
||||
expect(workspaceElement.querySelectorAll('status-bar').length).toBe 1
|
||||
|
||||
describe "@deactivate()", ->
|
||||
it "removes the status bar view", ->
|
||||
waitsForPromise ->
|
||||
Promise.resolve(atom.packages.deactivatePackage('status-bar')) # Wrapped so works with Promise & non-Promise deactivate
|
||||
runs ->
|
||||
expect(workspaceElement.querySelector('status-bar')).toBeNull()
|
||||
|
||||
describe "isVisible option", ->
|
||||
beforeEach ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
|
||||
describe "when it is true", ->
|
||||
beforeEach ->
|
||||
atom.config.set 'status-bar.isVisible', true
|
||||
|
||||
it "shows status bar", ->
|
||||
expect(workspaceElement.querySelector('status-bar').parentNode).toBeVisible()
|
||||
|
||||
describe "when it is false", ->
|
||||
beforeEach ->
|
||||
atom.config.set 'status-bar.isVisible', false
|
||||
|
||||
it "hides status bar", ->
|
||||
expect(workspaceElement.querySelector('status-bar').parentNode).not.toBeVisible()
|
||||
|
||||
describe "when status-bar:toggle is triggered", ->
|
||||
beforeEach ->
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
atom.config.set 'status-bar.isVisible', true
|
||||
|
||||
it "hides or shows the status bar", ->
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle')
|
||||
expect(workspaceElement.querySelector('status-bar').parentNode).not.toBeVisible()
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle')
|
||||
expect(workspaceElement.querySelector('status-bar').parentNode).toBeVisible()
|
||||
|
||||
it "toggles the value of isVisible in config file", ->
|
||||
expect(atom.config.get 'status-bar.isVisible').toBe true
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle')
|
||||
expect(atom.config.get 'status-bar.isVisible').toBe false
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle')
|
||||
expect(atom.config.get 'status-bar.isVisible').toBe true
|
||||
|
||||
describe "full-width setting", ->
|
||||
[containers] = []
|
||||
|
||||
beforeEach ->
|
||||
containers = atom.workspace.panelContainers
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
it "expects the setting to be enabled by default", ->
|
||||
expect(atom.config.get('status-bar.fullWidth')).toBeTruthy()
|
||||
expect(containers.footer.panels).toContain(mainModule.statusBarPanel)
|
||||
|
||||
describe "when setting is changed", ->
|
||||
it "fits status bar to editor's width", ->
|
||||
atom.config.set('status-bar.fullWidth', false)
|
||||
expect(containers.bottom.panels).toContain(mainModule.statusBarPanel)
|
||||
expect(containers.footer.panels).not.toContain(mainModule.statusBarPanel)
|
||||
|
||||
it "restores the status-bar location when re-enabling setting", ->
|
||||
atom.config.set('status-bar.fullWidth', true)
|
||||
expect(containers.footer.panels).toContain(mainModule.statusBarPanel)
|
||||
expect(containers.bottom.panels).not.toContain(mainModule.statusBarPanel)
|
||||
|
||||
describe "the 'status-bar' service", ->
|
||||
it "allows tiles to be added, removed, and retrieved", ->
|
||||
dummyView = document.createElement("div")
|
||||
tile = statusBarService.addLeftTile(item: dummyView)
|
||||
expect(statusBar).toContain(dummyView)
|
||||
expect(statusBarService.getLeftTiles()).toContain(tile)
|
||||
tile.destroy()
|
||||
expect(statusBar).not.toContain(dummyView)
|
||||
expect(statusBarService.getLeftTiles()).not.toContain(tile)
|
||||
|
||||
dummyView = document.createElement("div")
|
||||
tile = statusBarService.addRightTile(item: dummyView)
|
||||
expect(statusBar).toContain(dummyView)
|
||||
expect(statusBarService.getRightTiles()).toContain(tile)
|
||||
tile.destroy()
|
||||
expect(statusBar).not.toContain(dummyView)
|
||||
expect(statusBarService.getRightTiles()).not.toContain(tile)
|
||||
|
||||
it "allows the git info tile to be disabled", ->
|
||||
getGitInfoTile = ->
|
||||
statusBar.getRightTiles().find((tile) -> tile.item.matches('.git-view'))
|
||||
|
||||
expect(getGitInfoTile()).not.toBeUndefined()
|
||||
statusBarService.disableGitInfoTile()
|
||||
expect(getGitInfoTile()).toBeUndefined()
|
121
packages/status-bar/spec/status-bar-spec.js
Normal file
121
packages/status-bar/spec/status-bar-spec.js
Normal file
@ -0,0 +1,121 @@
|
||||
|
||||
describe("Status Bar package", function() {
|
||||
let [editor, statusBar, statusBarService, workspaceElement, mainModule] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage('status-bar').then(function(pack) {
|
||||
statusBar = workspaceElement.querySelector("status-bar");
|
||||
statusBarService = pack.mainModule.provideStatusBar();
|
||||
return ({mainModule} = pack);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("@activate()", () => it("appends only one status bar", function() {
|
||||
expect(workspaceElement.querySelectorAll('status-bar').length).toBe(1);
|
||||
atom.workspace.getActivePane().splitRight({copyActiveItem: true});
|
||||
expect(workspaceElement.querySelectorAll('status-bar').length).toBe(1);
|
||||
}));
|
||||
|
||||
describe("@deactivate()", () => it("removes the status bar view", function() {
|
||||
waitsForPromise(() => Promise.resolve(atom.packages.deactivatePackage('status-bar'))); // Wrapped so works with Promise & non-Promise deactivate
|
||||
runs(() => expect(workspaceElement.querySelector('status-bar')).toBeNull());
|
||||
}));
|
||||
|
||||
describe("isVisible option", function() {
|
||||
beforeEach(() => jasmine.attachToDOM(workspaceElement));
|
||||
|
||||
describe("when it is true", function() {
|
||||
beforeEach(() => atom.config.set('status-bar.isVisible', true));
|
||||
|
||||
it("shows status bar", () => expect(workspaceElement.querySelector('status-bar').parentNode).toBeVisible());
|
||||
});
|
||||
|
||||
describe("when it is false", function() {
|
||||
beforeEach(() => atom.config.set('status-bar.isVisible', false));
|
||||
|
||||
it("hides status bar", () => expect(workspaceElement.querySelector('status-bar').parentNode).not.toBeVisible());
|
||||
});
|
||||
});
|
||||
|
||||
describe("when status-bar:toggle is triggered", function() {
|
||||
beforeEach(function() {
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
return atom.config.set('status-bar.isVisible', true);
|
||||
});
|
||||
|
||||
it("hides or shows the status bar", function() {
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle');
|
||||
expect(workspaceElement.querySelector('status-bar').parentNode).not.toBeVisible();
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle');
|
||||
expect(workspaceElement.querySelector('status-bar').parentNode).toBeVisible();
|
||||
});
|
||||
|
||||
it("toggles the value of isVisible in config file", function() {
|
||||
expect(atom.config.get('status-bar.isVisible')).toBe(true);
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle');
|
||||
expect(atom.config.get('status-bar.isVisible')).toBe(false);
|
||||
atom.commands.dispatch(workspaceElement, 'status-bar:toggle');
|
||||
expect(atom.config.get('status-bar.isVisible')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("full-width setting", function() {
|
||||
let [containers] = [];
|
||||
|
||||
beforeEach(function() {
|
||||
containers = atom.workspace.panelContainers;
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
|
||||
return waitsForPromise(() => atom.workspace.open('sample.js'));
|
||||
});
|
||||
|
||||
it("expects the setting to be enabled by default", function() {
|
||||
expect(atom.config.get('status-bar.fullWidth')).toBeTruthy();
|
||||
expect(containers.footer.panels).toContain(mainModule.statusBarPanel);
|
||||
});
|
||||
|
||||
describe("when setting is changed", function() {
|
||||
it("fits status bar to editor's width", function() {
|
||||
atom.config.set('status-bar.fullWidth', false);
|
||||
expect(containers.bottom.panels).toContain(mainModule.statusBarPanel);
|
||||
expect(containers.footer.panels).not.toContain(mainModule.statusBarPanel);
|
||||
});
|
||||
|
||||
it("restores the status-bar location when re-enabling setting", function() {
|
||||
atom.config.set('status-bar.fullWidth', true);
|
||||
expect(containers.footer.panels).toContain(mainModule.statusBarPanel);
|
||||
expect(containers.bottom.panels).not.toContain(mainModule.statusBarPanel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("the 'status-bar' service", function() {
|
||||
it("allows tiles to be added, removed, and retrieved", function() {
|
||||
let dummyView = document.createElement("div");
|
||||
let tile = statusBarService.addLeftTile({item: dummyView});
|
||||
expect(statusBar).toContain(dummyView);
|
||||
expect(statusBarService.getLeftTiles()).toContain(tile);
|
||||
tile.destroy();
|
||||
expect(statusBar).not.toContain(dummyView);
|
||||
expect(statusBarService.getLeftTiles()).not.toContain(tile);
|
||||
|
||||
dummyView = document.createElement("div");
|
||||
tile = statusBarService.addRightTile({item: dummyView});
|
||||
expect(statusBar).toContain(dummyView);
|
||||
expect(statusBarService.getRightTiles()).toContain(tile);
|
||||
tile.destroy();
|
||||
expect(statusBar).not.toContain(dummyView);
|
||||
expect(statusBarService.getRightTiles()).not.toContain(tile);
|
||||
});
|
||||
|
||||
it("allows the git info tile to be disabled", function() {
|
||||
const getGitInfoTile = () => statusBar.getRightTiles().find(tile => tile.item.matches('.git-view'));
|
||||
|
||||
expect(getGitInfoTile()).not.toBeUndefined();
|
||||
statusBarService.disableGitInfoTile();
|
||||
expect(getGitInfoTile()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,103 +0,0 @@
|
||||
StatusBarView = require '../lib/status-bar-view'
|
||||
|
||||
describe "StatusBarView", ->
|
||||
statusBarView = null
|
||||
|
||||
class TestItem
|
||||
constructor: (@id) ->
|
||||
|
||||
beforeEach ->
|
||||
statusBarView = new StatusBarView()
|
||||
|
||||
atom.views.addViewProvider TestItem, (model) ->
|
||||
element = document.createElement("item-view")
|
||||
element.model = model
|
||||
element
|
||||
|
||||
describe "::addLeftTile({item, priority})", ->
|
||||
it "appends the view for the given item to its left side", ->
|
||||
testItem1 = new TestItem(1)
|
||||
testItem2 = new TestItem(2)
|
||||
testItem3 = new TestItem(3)
|
||||
|
||||
tile1 = statusBarView.addLeftTile(item: testItem1, priority: 10)
|
||||
tile2 = statusBarView.addLeftTile(item: testItem2, priority: 30)
|
||||
tile3 = statusBarView.addLeftTile(item: testItem3, priority: 20)
|
||||
|
||||
{leftPanel} = statusBarView
|
||||
|
||||
expect(leftPanel.children[0].nodeName).toBe("ITEM-VIEW")
|
||||
expect(leftPanel.children[1].nodeName).toBe("ITEM-VIEW")
|
||||
expect(leftPanel.children[2].nodeName).toBe("ITEM-VIEW")
|
||||
|
||||
expect(leftPanel.children[0].model).toBe(testItem1)
|
||||
expect(leftPanel.children[1].model).toBe(testItem3)
|
||||
expect(leftPanel.children[2].model).toBe(testItem2)
|
||||
|
||||
expect(statusBarView.getLeftTiles()).toEqual([tile1, tile3, tile2])
|
||||
expect(tile1.getPriority()).toBe(10)
|
||||
expect(tile1.getItem()).toBe(testItem1)
|
||||
|
||||
it "allows the view to be removed", ->
|
||||
testItem = new TestItem(1)
|
||||
tile = statusBarView.addLeftTile(item: testItem, priority: 10)
|
||||
tile.destroy()
|
||||
expect(statusBarView.leftPanel.children.length).toBe(0)
|
||||
|
||||
statusBarView.addLeftTile(item: testItem, priority: 9)
|
||||
|
||||
describe "when no priority is given", ->
|
||||
it "appends the item", ->
|
||||
testItem1 = new TestItem(1)
|
||||
testItem2 = new TestItem(2)
|
||||
|
||||
statusBarView.addLeftTile(item: testItem1, priority: 1000)
|
||||
statusBarView.addLeftTile(item: testItem2)
|
||||
|
||||
{leftPanel} = statusBarView
|
||||
expect(leftPanel.children[0].model).toBe(testItem1)
|
||||
expect(leftPanel.children[1].model).toBe(testItem2)
|
||||
|
||||
describe "::addRightTile({item, priority})", ->
|
||||
it "appends the view for the given item to its right side", ->
|
||||
testItem1 = new TestItem(1)
|
||||
testItem2 = new TestItem(2)
|
||||
testItem3 = new TestItem(3)
|
||||
|
||||
tile1 = statusBarView.addRightTile(item: testItem1, priority: 10)
|
||||
tile2 = statusBarView.addRightTile(item: testItem2, priority: 30)
|
||||
tile3 = statusBarView.addRightTile(item: testItem3, priority: 20)
|
||||
|
||||
{rightPanel} = statusBarView
|
||||
|
||||
expect(rightPanel.children[0].nodeName).toBe("ITEM-VIEW")
|
||||
expect(rightPanel.children[1].nodeName).toBe("ITEM-VIEW")
|
||||
expect(rightPanel.children[2].nodeName).toBe("ITEM-VIEW")
|
||||
|
||||
expect(rightPanel.children[0].model).toBe(testItem2)
|
||||
expect(rightPanel.children[1].model).toBe(testItem3)
|
||||
expect(rightPanel.children[2].model).toBe(testItem1)
|
||||
|
||||
expect(statusBarView.getRightTiles()).toEqual([tile2, tile3, tile1])
|
||||
expect(tile1.getPriority()).toBe(10)
|
||||
expect(tile1.getItem()).toBe(testItem1)
|
||||
|
||||
it "allows the view to be removed", ->
|
||||
testItem = new TestItem(1)
|
||||
disposable = statusBarView.addRightTile(item: testItem, priority: 10)
|
||||
disposable.destroy()
|
||||
expect(statusBarView.rightPanel.children.length).toBe(0)
|
||||
|
||||
statusBarView.addRightTile(item: testItem, priority: 11)
|
||||
|
||||
describe "when no priority is given", ->
|
||||
it "prepends the item", ->
|
||||
testItem1 = new TestItem(1, priority: 1000)
|
||||
testItem2 = new TestItem(2)
|
||||
|
||||
statusBarView.addRightTile(item: testItem1, priority: 1000)
|
||||
statusBarView.addRightTile(item: testItem2)
|
||||
|
||||
{rightPanel} = statusBarView
|
||||
expect(rightPanel.children[0].model).toBe(testItem2)
|
||||
expect(rightPanel.children[1].model).toBe(testItem1)
|
116
packages/status-bar/spec/status-bar-view-spec.js
Normal file
116
packages/status-bar/spec/status-bar-view-spec.js
Normal file
@ -0,0 +1,116 @@
|
||||
|
||||
const StatusBarView = require('../lib/status-bar-view');
|
||||
|
||||
describe("StatusBarView", function() {
|
||||
let statusBarView = null;
|
||||
|
||||
class TestItem {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
statusBarView = new StatusBarView();
|
||||
|
||||
atom.views.addViewProvider(TestItem, function(model) {
|
||||
const element = document.createElement("item-view");
|
||||
element.model = model;
|
||||
return element;
|
||||
});
|
||||
});
|
||||
|
||||
describe("::addLeftTile({item, priority})", function() {
|
||||
it("appends the view for the given item to its left side", function() {
|
||||
const testItem1 = new TestItem(1);
|
||||
const testItem2 = new TestItem(2);
|
||||
const testItem3 = new TestItem(3);
|
||||
|
||||
const tile1 = statusBarView.addLeftTile({item: testItem1, priority: 10});
|
||||
const tile2 = statusBarView.addLeftTile({item: testItem2, priority: 30});
|
||||
const tile3 = statusBarView.addLeftTile({item: testItem3, priority: 20});
|
||||
|
||||
const {leftPanel} = statusBarView;
|
||||
|
||||
expect(leftPanel.children[0].nodeName).toBe("ITEM-VIEW");
|
||||
expect(leftPanel.children[1].nodeName).toBe("ITEM-VIEW");
|
||||
expect(leftPanel.children[2].nodeName).toBe("ITEM-VIEW");
|
||||
|
||||
expect(leftPanel.children[0].model).toBe(testItem1);
|
||||
expect(leftPanel.children[1].model).toBe(testItem3);
|
||||
expect(leftPanel.children[2].model).toBe(testItem2);
|
||||
|
||||
expect(statusBarView.getLeftTiles()).toEqual([tile1, tile3, tile2]);
|
||||
expect(tile1.getPriority()).toBe(10);
|
||||
expect(tile1.getItem()).toBe(testItem1);
|
||||
});
|
||||
|
||||
it("allows the view to be removed", function() {
|
||||
const testItem = new TestItem(1);
|
||||
const tile = statusBarView.addLeftTile({item: testItem, priority: 10});
|
||||
tile.destroy();
|
||||
expect(statusBarView.leftPanel.children.length).toBe(0);
|
||||
|
||||
return statusBarView.addLeftTile({item: testItem, priority: 9});
|
||||
});
|
||||
|
||||
describe("when no priority is given", () => it("appends the item", function() {
|
||||
const testItem1 = new TestItem(1);
|
||||
const testItem2 = new TestItem(2);
|
||||
|
||||
statusBarView.addLeftTile({item: testItem1, priority: 1000});
|
||||
statusBarView.addLeftTile({item: testItem2});
|
||||
|
||||
const {leftPanel} = statusBarView;
|
||||
expect(leftPanel.children[0].model).toBe(testItem1);
|
||||
expect(leftPanel.children[1].model).toBe(testItem2);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("::addRightTile({item, priority})", function() {
|
||||
it("appends the view for the given item to its right side", function() {
|
||||
const testItem1 = new TestItem(1);
|
||||
const testItem2 = new TestItem(2);
|
||||
const testItem3 = new TestItem(3);
|
||||
|
||||
const tile1 = statusBarView.addRightTile({item: testItem1, priority: 10});
|
||||
const tile2 = statusBarView.addRightTile({item: testItem2, priority: 30});
|
||||
const tile3 = statusBarView.addRightTile({item: testItem3, priority: 20});
|
||||
|
||||
const {rightPanel} = statusBarView;
|
||||
|
||||
expect(rightPanel.children[0].nodeName).toBe("ITEM-VIEW");
|
||||
expect(rightPanel.children[1].nodeName).toBe("ITEM-VIEW");
|
||||
expect(rightPanel.children[2].nodeName).toBe("ITEM-VIEW");
|
||||
|
||||
expect(rightPanel.children[0].model).toBe(testItem2);
|
||||
expect(rightPanel.children[1].model).toBe(testItem3);
|
||||
expect(rightPanel.children[2].model).toBe(testItem1);
|
||||
|
||||
expect(statusBarView.getRightTiles()).toEqual([tile2, tile3, tile1]);
|
||||
expect(tile1.getPriority()).toBe(10);
|
||||
expect(tile1.getItem()).toBe(testItem1);
|
||||
});
|
||||
|
||||
it("allows the view to be removed", function() {
|
||||
const testItem = new TestItem(1);
|
||||
const disposable = statusBarView.addRightTile({item: testItem, priority: 10});
|
||||
disposable.destroy();
|
||||
expect(statusBarView.rightPanel.children.length).toBe(0);
|
||||
|
||||
return statusBarView.addRightTile({item: testItem, priority: 11});
|
||||
});
|
||||
|
||||
describe("when no priority is given", () => it("prepends the item", function() {
|
||||
const testItem1 = new TestItem(1, {priority: 1000});
|
||||
const testItem2 = new TestItem(2);
|
||||
|
||||
statusBarView.addRightTile({item: testItem1, priority: 1000});
|
||||
statusBarView.addRightTile({item: testItem2});
|
||||
|
||||
const {rightPanel} = statusBarView;
|
||||
expect(rightPanel.children[0].model).toBe(testItem2);
|
||||
expect(rightPanel.children[1].model).toBe(testItem1);
|
||||
}));
|
||||
});
|
||||
});
|
@ -1,46 +0,0 @@
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
temp = require('temp').track()
|
||||
|
||||
fileIcons = require '../lib/default-file-icons'
|
||||
|
||||
describe 'DefaultFileIcons', ->
|
||||
it 'defaults to text', ->
|
||||
expect(fileIcons.iconClassForPath('foo.bar')).toEqual('icon-file-text')
|
||||
|
||||
it 'recognizes READMEs', ->
|
||||
expect(fileIcons.iconClassForPath('README.md')).toEqual('icon-book')
|
||||
|
||||
it 'recognizes compressed files', ->
|
||||
expect(fileIcons.iconClassForPath('foo.zip')).toEqual('icon-file-zip')
|
||||
|
||||
it 'recognizes image files', ->
|
||||
expect(fileIcons.iconClassForPath('foo.png')).toEqual('icon-file-media')
|
||||
|
||||
it 'recognizes PDF files', ->
|
||||
expect(fileIcons.iconClassForPath('foo.pdf')).toEqual('icon-file-pdf')
|
||||
|
||||
it 'recognizes binary files', ->
|
||||
expect(fileIcons.iconClassForPath('foo.exe')).toEqual('icon-file-binary')
|
||||
|
||||
describe 'symlinks', ->
|
||||
[tempDir] = []
|
||||
|
||||
beforeEach ->
|
||||
tempDir = temp.mkdirSync('atom-tree-view')
|
||||
|
||||
it 'recognizes symlinks', ->
|
||||
filePath = path.join(tempDir, 'foo.bar')
|
||||
linkPath = path.join(tempDir, 'link.bar')
|
||||
fs.writeFileSync(filePath, '')
|
||||
fs.symlinkSync(filePath, linkPath, 'junction')
|
||||
|
||||
expect(fileIcons.iconClassForPath(linkPath)).toEqual('icon-file-symlink-file')
|
||||
|
||||
it 'recognizes as symlink instead of other types', ->
|
||||
filePath = path.join(tempDir, 'foo.zip')
|
||||
linkPath = path.join(tempDir, 'link.zip')
|
||||
fs.writeFileSync(filePath, '')
|
||||
fs.symlinkSync(filePath, linkPath, 'junction')
|
||||
|
||||
expect(fileIcons.iconClassForPath(linkPath)).toEqual('icon-file-symlink-file')
|
44
packages/tree-view/spec/default-file-icons-spec.js
Normal file
44
packages/tree-view/spec/default-file-icons-spec.js
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
const fs = require('fs-plus');
|
||||
const path = require('path');
|
||||
const temp = require('temp').track();
|
||||
|
||||
const fileIcons = require('../lib/default-file-icons');
|
||||
|
||||
describe('DefaultFileIcons', () => {
|
||||
it('defaults to text', () => expect(fileIcons.iconClassForPath('foo.bar')).toEqual('icon-file-text'));
|
||||
|
||||
it('recognizes READMEs', () => expect(fileIcons.iconClassForPath('README.md')).toEqual('icon-book'));
|
||||
|
||||
it('recognizes compressed files', () => expect(fileIcons.iconClassForPath('foo.zip')).toEqual('icon-file-zip'));
|
||||
|
||||
it('recognizes image files', () => expect(fileIcons.iconClassForPath('foo.png')).toEqual('icon-file-media'));
|
||||
|
||||
it('recognizes PDF files', () => expect(fileIcons.iconClassForPath('foo.pdf')).toEqual('icon-file-pdf'));
|
||||
|
||||
it('recognizes binary files', () => expect(fileIcons.iconClassForPath('foo.exe')).toEqual('icon-file-binary'));
|
||||
|
||||
describe('symlinks', () => {
|
||||
let [tempDir] = [];
|
||||
|
||||
beforeEach(() => tempDir = temp.mkdirSync('atom-tree-view'));
|
||||
|
||||
it('recognizes symlinks', () => {
|
||||
const filePath = path.join(tempDir, 'foo.bar');
|
||||
const linkPath = path.join(tempDir, 'link.bar');
|
||||
fs.writeFileSync(filePath, '');
|
||||
fs.symlinkSync(filePath, linkPath, 'junction');
|
||||
|
||||
expect(fileIcons.iconClassForPath(linkPath)).toEqual('icon-file-symlink-file');
|
||||
});
|
||||
|
||||
it('recognizes as symlink instead of other types', () => {
|
||||
const filePath = path.join(tempDir, 'foo.zip');
|
||||
const linkPath = path.join(tempDir, 'link.zip');
|
||||
fs.writeFileSync(filePath, '');
|
||||
fs.symlinkSync(filePath, linkPath, 'junction');
|
||||
|
||||
expect(fileIcons.iconClassForPath(linkPath)).toEqual('icon-file-symlink-file');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,133 +0,0 @@
|
||||
module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget, treeView, copy = false) ->
|
||||
dataTransfer =
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values
|
||||
getData: (key) -> @data[key]
|
||||
clearData: (key) ->
|
||||
if key
|
||||
delete @data[key]
|
||||
else
|
||||
@data = {}
|
||||
setDragImage: (@image) -> return
|
||||
|
||||
Object.defineProperty(
|
||||
dataTransfer,
|
||||
'items',
|
||||
get: ->
|
||||
Object.keys(dataTransfer.data).map((key) -> {type: key})
|
||||
)
|
||||
|
||||
treeView.deselect()
|
||||
for entry in dragged
|
||||
treeView.selectMultipleEntries(entry)
|
||||
|
||||
dragStartEvent = new DragEvent('dragstart')
|
||||
Object.defineProperty(dragStartEvent, 'target', value: dragged[0])
|
||||
Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged[0])
|
||||
Object.defineProperty(dragStartEvent, 'dataTransfer', value: dataTransfer)
|
||||
|
||||
dropEvent = new DragEvent('drop')
|
||||
Object.defineProperty(dropEvent, 'target', value: dropTarget)
|
||||
Object.defineProperty(dropEvent, 'currentTarget', value: dropTarget)
|
||||
Object.defineProperty(dropEvent, 'dataTransfer', value: dataTransfer)
|
||||
if copy
|
||||
key = if process.platform is 'darwin' then 'metaKey' else 'ctrlKey'
|
||||
Object.defineProperty(dropEvent, key, value: true)
|
||||
|
||||
dragEnterEvent = new DragEvent('dragenter')
|
||||
Object.defineProperty(dragEnterEvent, 'target', value: enterTarget)
|
||||
Object.defineProperty(dragEnterEvent, 'currentTarget', value: enterTarget)
|
||||
Object.defineProperty(dragEnterEvent, 'dataTransfer', value: dataTransfer)
|
||||
|
||||
[dragStartEvent, dragEnterEvent, dropEvent]
|
||||
|
||||
module.exports.buildExternalDropEvent = (filePaths, dropTarget, copy = false) ->
|
||||
dataTransfer =
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values
|
||||
getData: (key) -> @data[key]
|
||||
clearData: (key) ->
|
||||
if key
|
||||
delete @data[key]
|
||||
else
|
||||
@data = {}
|
||||
files: []
|
||||
|
||||
Object.defineProperty(
|
||||
dataTransfer,
|
||||
'items',
|
||||
get: ->
|
||||
Object.keys(dataTransfer.data).map((key) -> {type: key, kind: 'file'})
|
||||
)
|
||||
|
||||
dropEvent = new DragEvent('drop')
|
||||
Object.defineProperty(dropEvent, 'target', value: dropTarget)
|
||||
Object.defineProperty(dropEvent, 'currentTarget', value: dropTarget)
|
||||
Object.defineProperty(dropEvent, 'dataTransfer', value: dataTransfer)
|
||||
if copy
|
||||
key = if process.platform is 'darwin' then 'metaKey' else 'ctrlKey'
|
||||
Object.defineProperty(dropEvent, key, value: true)
|
||||
|
||||
for filePath in filePaths
|
||||
dropEvent.dataTransfer.files.push({path: filePath})
|
||||
dropEvent.dataTransfer.setData(filePath, 'bla') # Not technically correct, but gets the job done
|
||||
|
||||
dropEvent
|
||||
|
||||
buildElementPositionalDragEvents = (el, dataTransfer, currentTargetSelector) ->
|
||||
if not el?
|
||||
return {}
|
||||
|
||||
currentTarget = if currentTargetSelector then el.closest(currentTargetSelector) else el
|
||||
|
||||
topEvent = new DragEvent('dragover')
|
||||
Object.defineProperty(topEvent, 'target', value: el)
|
||||
Object.defineProperty(topEvent, 'currentTarget', value: currentTarget)
|
||||
Object.defineProperty(topEvent, 'dataTransfer', value: dataTransfer)
|
||||
Object.defineProperty(topEvent, 'pageY', value: el.getBoundingClientRect().top)
|
||||
|
||||
middleEvent = new DragEvent('dragover')
|
||||
Object.defineProperty(middleEvent, 'target', value: el)
|
||||
Object.defineProperty(middleEvent, 'currentTarget', value: currentTarget)
|
||||
Object.defineProperty(middleEvent, 'dataTransfer', value: dataTransfer)
|
||||
Object.defineProperty(middleEvent, 'pageY', value: el.getBoundingClientRect().top + el.offsetHeight * 0.5)
|
||||
|
||||
bottomEvent = new DragEvent('dragover')
|
||||
Object.defineProperty(bottomEvent, 'target', value: el)
|
||||
Object.defineProperty(bottomEvent, 'currentTarget', value: currentTarget)
|
||||
Object.defineProperty(bottomEvent, 'dataTransfer', value: dataTransfer)
|
||||
Object.defineProperty(bottomEvent, 'pageY', value: el.getBoundingClientRect().bottom)
|
||||
|
||||
{top: topEvent, middle: middleEvent, bottom: bottomEvent}
|
||||
|
||||
|
||||
module.exports.buildPositionalDragEvents = (dragged, target, currentTargetSelector) ->
|
||||
dataTransfer =
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values
|
||||
getData: (key) -> @data[key]
|
||||
clearData: (key) ->
|
||||
if key
|
||||
delete @data[key]
|
||||
else
|
||||
@data = {}
|
||||
setDragImage: (@image) -> return
|
||||
|
||||
Object.defineProperty(
|
||||
dataTransfer,
|
||||
'items',
|
||||
get: ->
|
||||
Object.keys(dataTransfer.data).map((key) -> {type: key})
|
||||
)
|
||||
|
||||
dragStartEvent = new DragEvent('dragstart')
|
||||
Object.defineProperty(dragStartEvent, 'target', value: dragged)
|
||||
Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged)
|
||||
Object.defineProperty(dragStartEvent, 'dataTransfer', value: dataTransfer)
|
||||
|
||||
dragEndEvent = new DragEvent('dragend')
|
||||
Object.defineProperty(dragEndEvent, 'target', value: dragged)
|
||||
Object.defineProperty(dragEndEvent, 'currentTarget', value: dragged)
|
||||
Object.defineProperty(dragEndEvent, 'dataTransfer', value: dataTransfer)
|
||||
|
||||
[dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer, currentTargetSelector), dragEndEvent]
|
167
packages/tree-view/spec/event-helpers.js
Normal file
167
packages/tree-view/spec/event-helpers.js
Normal file
@ -0,0 +1,167 @@
|
||||
|
||||
module.exports.buildInternalDragEvents = function(dragged, enterTarget, dropTarget, treeView, copy) {
|
||||
if (copy == null) { copy = false; }
|
||||
const dataTransfer = {
|
||||
data: {},
|
||||
setData(key, value) { return this.data[key] = `${value}`; }, // Drag events stringify data values
|
||||
getData(key) { return this.data[key]; },
|
||||
clearData(key) {
|
||||
if (key) {
|
||||
return delete this.data[key];
|
||||
} else {
|
||||
return this.data = {};
|
||||
}
|
||||
},
|
||||
setDragImage(image) { this.image = image; }
|
||||
};
|
||||
|
||||
Object.defineProperty(
|
||||
dataTransfer,
|
||||
'items', {
|
||||
get() {
|
||||
return Object.keys(dataTransfer.data).map(key => ({
|
||||
type: key
|
||||
}));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
treeView.deselect();
|
||||
for (let entry of Array.from(dragged)) {
|
||||
treeView.selectMultipleEntries(entry);
|
||||
}
|
||||
|
||||
const dragStartEvent = new DragEvent('dragstart');
|
||||
Object.defineProperty(dragStartEvent, 'target', {value: dragged[0]});
|
||||
Object.defineProperty(dragStartEvent, 'currentTarget', {value: dragged[0]});
|
||||
Object.defineProperty(dragStartEvent, 'dataTransfer', {value: dataTransfer});
|
||||
|
||||
const dropEvent = new DragEvent('drop');
|
||||
Object.defineProperty(dropEvent, 'target', {value: dropTarget});
|
||||
Object.defineProperty(dropEvent, 'currentTarget', {value: dropTarget});
|
||||
Object.defineProperty(dropEvent, 'dataTransfer', {value: dataTransfer});
|
||||
if (copy) {
|
||||
const key = process.platform === 'darwin' ? 'metaKey' : 'ctrlKey';
|
||||
Object.defineProperty(dropEvent, key, {value: true});
|
||||
}
|
||||
|
||||
const dragEnterEvent = new DragEvent('dragenter');
|
||||
Object.defineProperty(dragEnterEvent, 'target', {value: enterTarget});
|
||||
Object.defineProperty(dragEnterEvent, 'currentTarget', {value: enterTarget});
|
||||
Object.defineProperty(dragEnterEvent, 'dataTransfer', {value: dataTransfer});
|
||||
|
||||
return [dragStartEvent, dragEnterEvent, dropEvent];
|
||||
};
|
||||
|
||||
module.exports.buildExternalDropEvent = function(filePaths, dropTarget, copy) {
|
||||
if (copy == null) { copy = false; }
|
||||
const dataTransfer = {
|
||||
data: {},
|
||||
setData(key, value) { return this.data[key] = `${value}`; }, // Drag events stringify data values
|
||||
getData(key) { return this.data[key]; },
|
||||
clearData(key) {
|
||||
if (key) {
|
||||
return delete this.data[key];
|
||||
} else {
|
||||
return this.data = {};
|
||||
}
|
||||
},
|
||||
files: []
|
||||
};
|
||||
|
||||
Object.defineProperty(
|
||||
dataTransfer,
|
||||
'items', {
|
||||
get() {
|
||||
return Object.keys(dataTransfer.data).map(key => ({
|
||||
type: key,
|
||||
kind: 'file'
|
||||
}));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const dropEvent = new DragEvent('drop');
|
||||
Object.defineProperty(dropEvent, 'target', {value: dropTarget});
|
||||
Object.defineProperty(dropEvent, 'currentTarget', {value: dropTarget});
|
||||
Object.defineProperty(dropEvent, 'dataTransfer', {value: dataTransfer});
|
||||
if (copy) {
|
||||
const key = process.platform === 'darwin' ? 'metaKey' : 'ctrlKey';
|
||||
Object.defineProperty(dropEvent, key, {value: true});
|
||||
}
|
||||
|
||||
for (let filePath of Array.from(filePaths)) {
|
||||
dropEvent.dataTransfer.files.push({path: filePath});
|
||||
dropEvent.dataTransfer.setData(filePath, 'bla');
|
||||
} // Not technically correct, but gets the job done
|
||||
|
||||
return dropEvent;
|
||||
};
|
||||
|
||||
const buildElementPositionalDragEvents = function(el, dataTransfer, currentTargetSelector) {
|
||||
if ((el == null)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const currentTarget = currentTargetSelector ? el.closest(currentTargetSelector) : el;
|
||||
|
||||
const topEvent = new DragEvent('dragover');
|
||||
Object.defineProperty(topEvent, 'target', {value: el});
|
||||
Object.defineProperty(topEvent, 'currentTarget', {value: currentTarget});
|
||||
Object.defineProperty(topEvent, 'dataTransfer', {value: dataTransfer});
|
||||
Object.defineProperty(topEvent, 'pageY', {value: el.getBoundingClientRect().top});
|
||||
|
||||
const middleEvent = new DragEvent('dragover');
|
||||
Object.defineProperty(middleEvent, 'target', {value: el});
|
||||
Object.defineProperty(middleEvent, 'currentTarget', {value: currentTarget});
|
||||
Object.defineProperty(middleEvent, 'dataTransfer', {value: dataTransfer});
|
||||
Object.defineProperty(middleEvent, 'pageY', {value: el.getBoundingClientRect().top + (el.offsetHeight * 0.5)});
|
||||
|
||||
const bottomEvent = new DragEvent('dragover');
|
||||
Object.defineProperty(bottomEvent, 'target', {value: el});
|
||||
Object.defineProperty(bottomEvent, 'currentTarget', {value: currentTarget});
|
||||
Object.defineProperty(bottomEvent, 'dataTransfer', {value: dataTransfer});
|
||||
Object.defineProperty(bottomEvent, 'pageY', {value: el.getBoundingClientRect().bottom});
|
||||
|
||||
return {top: topEvent, middle: middleEvent, bottom: bottomEvent};
|
||||
};
|
||||
|
||||
|
||||
module.exports.buildPositionalDragEvents = function(dragged, target, currentTargetSelector) {
|
||||
const dataTransfer = {
|
||||
data: {},
|
||||
setData(key, value) { return this.data[key] = `${value}`; }, // Drag events stringify data values
|
||||
getData(key) { return this.data[key]; },
|
||||
clearData(key) {
|
||||
if (key) {
|
||||
return delete this.data[key];
|
||||
} else {
|
||||
return this.data = {};
|
||||
}
|
||||
},
|
||||
setDragImage(image) { this.image = image; }
|
||||
};
|
||||
|
||||
Object.defineProperty(
|
||||
dataTransfer,
|
||||
'items', {
|
||||
get() {
|
||||
return Object.keys(dataTransfer.data).map(key => ({
|
||||
type: key
|
||||
}));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const dragStartEvent = new DragEvent('dragstart');
|
||||
Object.defineProperty(dragStartEvent, 'target', {value: dragged});
|
||||
Object.defineProperty(dragStartEvent, 'currentTarget', {value: dragged});
|
||||
Object.defineProperty(dragStartEvent, 'dataTransfer', {value: dataTransfer});
|
||||
|
||||
const dragEndEvent = new DragEvent('dragend');
|
||||
Object.defineProperty(dragEndEvent, 'target', {value: dragged});
|
||||
Object.defineProperty(dragEndEvent, 'currentTarget', {value: dragged});
|
||||
Object.defineProperty(dragEndEvent, 'dataTransfer', {value: dataTransfer});
|
||||
|
||||
return [dragStartEvent, buildElementPositionalDragEvents(target, dataTransfer, currentTargetSelector), dragEndEvent];
|
||||
};
|
@ -1,44 +0,0 @@
|
||||
DefaultFileIcons = require '../lib/default-file-icons'
|
||||
getIconServices = require '../lib/get-icon-services'
|
||||
{Disposable} = require 'atom'
|
||||
|
||||
describe 'IconServices', ->
|
||||
afterEach ->
|
||||
getIconServices().resetFileIcons()
|
||||
getIconServices().resetElementIcons()
|
||||
|
||||
describe 'FileIcons', ->
|
||||
it 'provides a default', ->
|
||||
expect(getIconServices().fileIcons).toBeDefined()
|
||||
expect(getIconServices().fileIcons).toBe(DefaultFileIcons)
|
||||
|
||||
it 'allows the default to be overridden', ->
|
||||
service = new Object
|
||||
getIconServices().setFileIcons service
|
||||
expect(getIconServices().fileIcons).toBe(service)
|
||||
|
||||
it 'allows the service to be reset to the default easily', ->
|
||||
service = new Object
|
||||
getIconServices().setFileIcons service
|
||||
getIconServices().resetFileIcons()
|
||||
expect(getIconServices().fileIcons).toBe(DefaultFileIcons)
|
||||
|
||||
describe 'ElementIcons', ->
|
||||
it 'does not provide a default', ->
|
||||
expect(getIconServices().elementIcons).toBe(null)
|
||||
|
||||
it 'consumes the ElementIcons service', ->
|
||||
service = ->
|
||||
getIconServices().setElementIcons service
|
||||
expect(getIconServices().elementIcons).toBe(service)
|
||||
|
||||
it 'does not call the FileIcons service when the ElementIcons service is provided', ->
|
||||
elementIcons = ->
|
||||
new Disposable ->
|
||||
fileIcons =
|
||||
iconClassForPath: ->
|
||||
spyOn(fileIcons, 'iconClassForPath').andCallThrough()
|
||||
getIconServices().setElementIcons elementIcons
|
||||
getIconServices().setFileIcons fileIcons
|
||||
getIconServices().updateFileIcon(file: {}, fileName: classList: add: ->)
|
||||
expect(fileIcons.iconClassForPath).not.toHaveBeenCalled()
|
52
packages/tree-view/spec/file-icons-spec.js
Normal file
52
packages/tree-view/spec/file-icons-spec.js
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
const DefaultFileIcons = require('../lib/default-file-icons');
|
||||
const getIconServices = require('../lib/get-icon-services');
|
||||
const {Disposable} = require('atom');
|
||||
|
||||
describe('IconServices', () => {
|
||||
afterEach(() => {
|
||||
getIconServices().resetFileIcons();
|
||||
getIconServices().resetElementIcons();
|
||||
});
|
||||
|
||||
describe('FileIcons', () => {
|
||||
it('provides a default', () => {
|
||||
expect(getIconServices().fileIcons).toBeDefined();
|
||||
expect(getIconServices().fileIcons).toBe(DefaultFileIcons);
|
||||
});
|
||||
|
||||
it('allows the default to be overridden', () => {
|
||||
const service = new Object;
|
||||
getIconServices().setFileIcons(service);
|
||||
expect(getIconServices().fileIcons).toBe(service);
|
||||
});
|
||||
|
||||
it('allows the service to be reset to the default easily', () => {
|
||||
const service = new Object;
|
||||
getIconServices().setFileIcons(service);
|
||||
getIconServices().resetFileIcons();
|
||||
expect(getIconServices().fileIcons).toBe(DefaultFileIcons);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementIcons', () => {
|
||||
it('does not provide a default', () => expect(getIconServices().elementIcons).toBe(null));
|
||||
|
||||
it('consumes the ElementIcons service', () => {
|
||||
const service = function() {};
|
||||
getIconServices().setElementIcons(service);
|
||||
expect(getIconServices().elementIcons).toBe(service);
|
||||
});
|
||||
|
||||
it('does not call the FileIcons service when the ElementIcons service is provided', () => {
|
||||
const elementIcons = () => new Disposable(function() {});
|
||||
const fileIcons =
|
||||
{iconClassForPath() {}};
|
||||
spyOn(fileIcons, 'iconClassForPath').andCallThrough();
|
||||
getIconServices().setElementIcons(elementIcons);
|
||||
getIconServices().setFileIcons(fileIcons);
|
||||
getIconServices().updateFileIcon({file: {}, fileName: {classList: {add() {}}}});
|
||||
expect(fileIcons.iconClassForPath).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,72 +0,0 @@
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
temp = require('temp').track()
|
||||
|
||||
describe "FileStats", ->
|
||||
describe "provision of filesystem stats", ->
|
||||
[file1Data, file2Data, timeStarted, treeView] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"]
|
||||
|
||||
beforeEach ->
|
||||
jasmine.useRealClock()
|
||||
timeStarted = Date.now()
|
||||
rootDirPath = fs.absolute(temp.mkdirSync("tree-view"))
|
||||
subdirPath = path.join(rootDirPath, "subdir")
|
||||
filePath1 = path.join(rootDirPath, "file1.txt")
|
||||
filePath2 = path.join(subdirPath, "file2.txt")
|
||||
|
||||
fs.makeTreeSync(subdirPath)
|
||||
fs.writeFileSync(filePath1, file1Data)
|
||||
fs.writeFileSync(filePath2, file2Data)
|
||||
atom.project.setPaths([rootDirPath])
|
||||
|
||||
waitsFor (done) ->
|
||||
atom.workspace.onDidOpen(done)
|
||||
atom.packages.activatePackage("tree-view")
|
||||
|
||||
runs ->
|
||||
treeView = atom.workspace.getLeftDock().getActivePaneItem()
|
||||
|
||||
it "passes stats to File instances", ->
|
||||
stats = treeView.roots[0].directory.entries.get("file1.txt").stats
|
||||
expect(stats).toBeDefined()
|
||||
expect(stats.mtime).toBeDefined()
|
||||
expect(stats.size).toEqual(file1Data.length)
|
||||
|
||||
it "passes stats to Directory instances", ->
|
||||
stats = treeView.roots[0].directory.entries.get("subdir").stats
|
||||
expect(stats).toBeDefined()
|
||||
expect(stats.mtime).toBeDefined()
|
||||
|
||||
it "passes stats to a root directory when initialised", ->
|
||||
expect(treeView.roots[0].directory.stats).toBeDefined()
|
||||
|
||||
it "passes stats to File instances in subdirectories", ->
|
||||
treeView.element.querySelector(".entries > li").expand()
|
||||
subdir = treeView.roots[0].directory.entries.get("subdir")
|
||||
stats = subdir.entries.get("file2.txt").stats
|
||||
expect(stats).toBeDefined()
|
||||
expect(stats.size).toEqual(file2Data.length)
|
||||
|
||||
it "converts date-stats to timestamps", ->
|
||||
stats = treeView.roots[0].directory.entries.get("file1.txt").stats
|
||||
stamp = stats.mtime
|
||||
expect(_.isDate stamp).toBe(false)
|
||||
expect(typeof stamp).toBe("number")
|
||||
expect(Number.isNaN stamp).toBe(false)
|
||||
|
||||
it "accurately converts timestamps", ->
|
||||
stats = treeView.roots[0].directory.entries.get("file1.txt").stats
|
||||
# Two minutes should be enough
|
||||
expect(Math.abs stats.mtime - timeStarted).toBeLessThan(120000)
|
||||
|
||||
describe "virtual filepaths", ->
|
||||
beforeEach ->
|
||||
atom.project.setPaths([])
|
||||
waitsForPromise -> Promise.all [
|
||||
atom.packages.activatePackage("tree-view")
|
||||
atom.packages.activatePackage("about")
|
||||
]
|
||||
|
||||
it "doesn't throw an exception when accessing virtual filepaths", ->
|
||||
atom.project.setPaths(["atom://about"])
|
90
packages/tree-view/spec/file-stats-spec.js
Normal file
90
packages/tree-view/spec/file-stats-spec.js
Normal file
@ -0,0 +1,90 @@
|
||||
|
||||
const _ = require('underscore-plus');
|
||||
const fs = require('fs-plus');
|
||||
const path = require('path');
|
||||
const temp = require('temp').track();
|
||||
|
||||
describe("FileStats", function() {
|
||||
describe("provision of filesystem stats", function() {
|
||||
let [file1Data, file2Data, timeStarted, treeView] = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ", "0123456789"];
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.useRealClock();
|
||||
timeStarted = Date.now();
|
||||
const rootDirPath = fs.absolute(temp.mkdirSync("tree-view"));
|
||||
const subdirPath = path.join(rootDirPath, "subdir");
|
||||
const filePath1 = path.join(rootDirPath, "file1.txt");
|
||||
const filePath2 = path.join(subdirPath, "file2.txt");
|
||||
|
||||
fs.makeTreeSync(subdirPath);
|
||||
fs.writeFileSync(filePath1, file1Data);
|
||||
fs.writeFileSync(filePath2, file2Data);
|
||||
atom.project.setPaths([rootDirPath]);
|
||||
|
||||
waitsFor(function(done) {
|
||||
atom.workspace.onDidOpen(done);
|
||||
return atom.packages.activatePackage("tree-view");
|
||||
});
|
||||
|
||||
runs(() => treeView = atom.workspace.getLeftDock().getActivePaneItem());
|
||||
});
|
||||
|
||||
it("passes stats to File instances", function() {
|
||||
const {
|
||||
stats
|
||||
} = treeView.roots[0].directory.entries.get("file1.txt");
|
||||
expect(stats).toBeDefined();
|
||||
expect(stats.mtime).toBeDefined();
|
||||
expect(stats.size).toEqual(file1Data.length);
|
||||
});
|
||||
|
||||
it("passes stats to Directory instances", function() {
|
||||
const {
|
||||
stats
|
||||
} = treeView.roots[0].directory.entries.get("subdir");
|
||||
expect(stats).toBeDefined();
|
||||
expect(stats.mtime).toBeDefined();
|
||||
});
|
||||
|
||||
it("passes stats to a root directory when initialised", () => expect(treeView.roots[0].directory.stats).toBeDefined());
|
||||
|
||||
it("passes stats to File instances in subdirectories", function() {
|
||||
treeView.element.querySelector(".entries > li").expand();
|
||||
const subdir = treeView.roots[0].directory.entries.get("subdir");
|
||||
const {
|
||||
stats
|
||||
} = subdir.entries.get("file2.txt");
|
||||
expect(stats).toBeDefined();
|
||||
expect(stats.size).toEqual(file2Data.length);
|
||||
});
|
||||
|
||||
it("converts date-stats to timestamps", function() {
|
||||
const {
|
||||
stats
|
||||
} = treeView.roots[0].directory.entries.get("file1.txt");
|
||||
const stamp = stats.mtime;
|
||||
expect(_.isDate(stamp)).toBe(false);
|
||||
expect(typeof stamp).toBe("number");
|
||||
expect(Number.isNaN(stamp)).toBe(false);
|
||||
});
|
||||
|
||||
it("accurately converts timestamps", function() {
|
||||
const {
|
||||
stats
|
||||
} = treeView.roots[0].directory.entries.get("file1.txt");
|
||||
// Two minutes should be enough
|
||||
expect(Math.abs(stats.mtime - timeStarted)).toBeLessThan(120000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("virtual filepaths", function() {
|
||||
beforeEach(function() {
|
||||
atom.project.setPaths([]);
|
||||
waitsForPromise(() => Promise.all([
|
||||
atom.packages.activatePackage("tree-view"),
|
||||
atom.packages.activatePackage("about")
|
||||
]));});
|
||||
|
||||
it("doesn't throw an exception when accessing virtual filepaths", () => atom.project.setPaths(["atom://about"]));
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
5599
packages/tree-view/spec/tree-view-package-spec.js
Normal file
5599
packages/tree-view/spec/tree-view-package-spec.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user