mirror of
https://github.com/adobe/brackets.git
synced 2024-11-20 18:02:54 +01:00
2954858128
* Added Extension Sorting capability according to downloads and last published date * Passed sort criteria to sortRegistry function in registy_utils * Added some tests * Added check for existence of downloadCount in extension-manager * Added test for checking downloadCount in ExtensionManager view * Added svg image for download icon in Extension Manager
2288 lines
121 KiB
JavaScript
2288 lines
121 KiB
JavaScript
/*
|
|
* Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
/*jslint regexp: true */
|
|
/*global describe, it, expect, beforeEach, afterEach, waitsFor, runs, waitsForDone, spyOn, jasmine */
|
|
/*unittests: ExtensionManager*/
|
|
|
|
define(function (require, exports, module) {
|
|
"use strict";
|
|
|
|
require("thirdparty/jquery.mockjax.js");
|
|
|
|
var _ = require("thirdparty/lodash");
|
|
|
|
var ExtensionManager = require("extensibility/ExtensionManager"),
|
|
ExtensionManagerView = require("extensibility/ExtensionManagerView").ExtensionManagerView,
|
|
ExtensionManagerViewModel = require("extensibility/ExtensionManagerViewModel"),
|
|
ExtensionManagerDialog = require("extensibility/ExtensionManagerDialog"),
|
|
InstallExtensionDialog = require("extensibility/InstallExtensionDialog"),
|
|
Package = require("extensibility/Package"),
|
|
ExtensionLoader = require("utils/ExtensionLoader"),
|
|
SpecRunnerUtils = require("spec/SpecRunnerUtils"),
|
|
NativeApp = require("utils/NativeApp"),
|
|
Dialogs = require("widgets/Dialogs"),
|
|
CommandManager = require("command/CommandManager"),
|
|
Commands = require("command/Commands"),
|
|
FileSystem = require("filesystem/FileSystem"),
|
|
Strings = require("strings"),
|
|
StringUtils = require("utils/StringUtils"),
|
|
LocalizationUtils = require("utils/LocalizationUtils"),
|
|
PreferencesManager = require("preferences/PreferencesManager"),
|
|
mockRegistryText = require("text!spec/ExtensionManager-test-files/mockRegistry.json"),
|
|
mockRegistryThemesText = require("text!spec/ExtensionManager-test-files/mockRegistryThemes.json"),
|
|
mockRegistryForSearch = require("text!spec/ExtensionManager-test-files/mockRegistryForSearch.json"),
|
|
mockExtensionList = require("text!spec/ExtensionManager-test-files/mockExtensionList.json"),
|
|
mockRegistry;
|
|
|
|
describe("ExtensionManager", function () {
|
|
var mockId, mockSettings, origRegistryURL, origExtensionUrl, removedPath,
|
|
view, model, fakeLoadDeferred, modelDisposed, disabledFilePath;
|
|
|
|
beforeEach(function () {
|
|
// Use fake URLs for the registry (useful if the registry isn't actually currently
|
|
// configured).
|
|
origRegistryURL = brackets.config.extension_registry;
|
|
origExtensionUrl = brackets.config.extension_url;
|
|
brackets.config.extension_registry = "http://fake-registry.com/registry.json";
|
|
brackets.config.extension_url = "http://fake-repository.com/{0}/{0}-{1}.zip";
|
|
|
|
// Return a canned registry when requested. Individual tests can override this
|
|
// at any point before the request is actually made.
|
|
mockRegistry = JSON.parse(mockRegistryText);
|
|
mockSettings = {
|
|
url: brackets.config.extension_registry,
|
|
dataType: "json",
|
|
contentType: "application/json",
|
|
response: function () {
|
|
this.responseText = mockRegistry;
|
|
}
|
|
};
|
|
spyOn(mockSettings, "response").andCallThrough();
|
|
mockId = $.mockjax(mockSettings);
|
|
|
|
// Set a fake path for user extensions.
|
|
var mockPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files");
|
|
spyOn(ExtensionLoader, "getUserExtensionPath").andCallFake(function () {
|
|
return mockPath + "/user";
|
|
});
|
|
|
|
// Fake package removal.
|
|
removedPath = null;
|
|
spyOn(Package, "remove").andCallFake(function (path) {
|
|
removedPath = path;
|
|
return new $.Deferred().resolve().promise();
|
|
});
|
|
|
|
// Fake enabling/disabling
|
|
disabledFilePath = null;
|
|
spyOn(Package, "disable").andCallFake(function (path) {
|
|
disabledFilePath = path + "/.disabled";
|
|
return new $.Deferred().resolve().promise();
|
|
});
|
|
spyOn(Package, "enable").andCallFake(function (path) {
|
|
disabledFilePath = path + "/.disabled";
|
|
return new $.Deferred().resolve().promise();
|
|
});
|
|
});
|
|
|
|
afterEach(function () {
|
|
$.mockjaxClear(mockId);
|
|
ExtensionManager._reset();
|
|
ExtensionManager.off(".unit-test");
|
|
brackets.config.extension_registry = origRegistryURL;
|
|
brackets.config.extension_url = origExtensionUrl;
|
|
});
|
|
|
|
function mockLoadExtensions(names, fail) {
|
|
var numStatusChanges = 0,
|
|
shouldFail = false,
|
|
shouldDisable = false;
|
|
if (typeof fail === "boolean") {
|
|
shouldFail = true;
|
|
} else if (typeof fail === "string") {
|
|
shouldDisable = true;
|
|
}
|
|
runs(function () {
|
|
ExtensionManager.on("statusChange.mock-load", function () {
|
|
numStatusChanges++;
|
|
});
|
|
var mockPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files");
|
|
names = names || ["default/mock-extension-1", "dev/mock-extension-2", "user/mock-legacy-extension"];
|
|
names.forEach(function (name) {
|
|
ExtensionLoader.trigger(shouldFail ? "loadFailed" : (shouldDisable ? "disabled" : "load"), mockPath + "/" + name);
|
|
});
|
|
});
|
|
|
|
// Make sure the ExtensionManager has finished reading all the package.jsons before continuing.
|
|
waitsFor(function () { return numStatusChanges === names.length; }, "ExtensionManager status changes");
|
|
|
|
runs(function () {
|
|
ExtensionManager.off(".mock-load");
|
|
});
|
|
}
|
|
|
|
function makeMockInstalledVersion(mockRegistryExtension, installedVersion) {
|
|
var ref = _.find(mockRegistryExtension.versions, { version: installedVersion });
|
|
return {
|
|
locationType: ExtensionManager.LOCATION_USER,
|
|
metadata: {
|
|
name: mockRegistryExtension.metadata.name,
|
|
title: mockRegistryExtension.metadata.title,
|
|
version: ref.version,
|
|
engines: { brackets: ref.brackets }
|
|
},
|
|
owner: mockRegistryExtension.owner
|
|
};
|
|
}
|
|
|
|
function makeMockExtension(versionRequirements) {
|
|
var FAKE_DATE = "2013-04-10T18:28:20.530Z",
|
|
versions = [];
|
|
versionRequirements.forEach(function (verReq, i) {
|
|
versions.push({ version: (i + 1) + ".0.0", brackets: verReq, published: FAKE_DATE });
|
|
});
|
|
var latestVer = versions[versions.length - 1];
|
|
return {
|
|
metadata: {
|
|
name: "mock-extension",
|
|
title: "Mock Extension",
|
|
version: latestVer.version,
|
|
engines: { brackets: latestVer.brackets }
|
|
},
|
|
owner: "github:someuser",
|
|
versions: versions
|
|
};
|
|
}
|
|
|
|
function setupExtensionManagerViewTests(context) {
|
|
context.addMatchers({
|
|
toHaveText: function (expected) {
|
|
var notText = this.isNot ? " not" : "";
|
|
this.message = function () {
|
|
return "Expected view" + notText + " to contain text " + expected;
|
|
};
|
|
return SpecRunnerUtils.findDOMText(this.actual.$el, expected);
|
|
},
|
|
toHaveLink: function (expected) {
|
|
var notText = this.isNot ? " not" : "";
|
|
this.message = function () {
|
|
return "Expected view" + notText + " to contain link " + expected;
|
|
};
|
|
return SpecRunnerUtils.findDOMText(this.actual.$el, expected, true);
|
|
}
|
|
});
|
|
spyOn(InstallExtensionDialog, "installUsingDialog").andCallFake(function (url) {
|
|
var id = url.match(/fake-repository\.com\/([^\/]+)/)[1];
|
|
mockLoadExtensions(["user/" + id]);
|
|
});
|
|
}
|
|
|
|
function cleanupExtensionManagerViewTests() {
|
|
if (view) {
|
|
view.$el.remove();
|
|
view = null;
|
|
}
|
|
if (model) {
|
|
model.dispose();
|
|
}
|
|
}
|
|
|
|
// Sets up the view using the normal (mock) ExtensionManager data.
|
|
function setupViewWithMockData(ModelClass) {
|
|
runs(function () {
|
|
view = new ExtensionManagerView();
|
|
model = new ModelClass();
|
|
modelDisposed = false;
|
|
waitsForDone(view.initialize(model), "view initializing");
|
|
view.$el.appendTo(window.document.body);
|
|
});
|
|
runs(function () {
|
|
spyOn(view.model, "dispose").andCallThrough();
|
|
});
|
|
}
|
|
|
|
describe("ExtensionManager", function () {
|
|
it("should download the extension list from the registry", function () {
|
|
runs(function () {
|
|
waitsForDone(ExtensionManager.downloadRegistry(), "fetching registry");
|
|
});
|
|
|
|
runs(function () {
|
|
expect(mockSettings.response).toHaveBeenCalled();
|
|
Object.keys(ExtensionManager.extensions).forEach(function (id) {
|
|
expect(ExtensionManager.extensions[id].registryInfo).toEqual(mockRegistry[id]);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should trigger a registryUpdate event when updating the extension list from the registry", function () {
|
|
var registryUpdateSpy;
|
|
runs(function () {
|
|
registryUpdateSpy = jasmine.createSpy();
|
|
ExtensionManager.on("registryUpdate", registryUpdateSpy);
|
|
waitsForDone(ExtensionManager.downloadRegistry(), "fetching registry");
|
|
});
|
|
mockLoadExtensions();
|
|
runs(function () {
|
|
expect(registryUpdateSpy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("should fail if it can't access the registry", function () {
|
|
var gotDone = false, gotFail = false;
|
|
runs(function () {
|
|
$.mockjaxClear(mockId);
|
|
mockId = $.mockjax({
|
|
url: brackets.config.extension_registry,
|
|
isTimeout: true
|
|
});
|
|
ExtensionManager.downloadRegistry()
|
|
.done(function () {
|
|
gotDone = true;
|
|
})
|
|
.fail(function () {
|
|
gotFail = true;
|
|
});
|
|
});
|
|
waitsFor(function () { return gotDone || gotFail; }, "mock failure");
|
|
|
|
runs(function () {
|
|
expect(gotFail).toBe(true);
|
|
expect(gotDone).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should fail if registry content is malformed", function () {
|
|
var gotDone = false, gotFail = false;
|
|
runs(function () {
|
|
mockRegistry = "{malformed json";
|
|
ExtensionManager.downloadRegistry()
|
|
.done(function () {
|
|
gotDone = true;
|
|
})
|
|
.fail(function () {
|
|
gotFail = true;
|
|
});
|
|
});
|
|
waitsFor(function () { return gotDone || gotFail; }, "bad mock data");
|
|
|
|
runs(function () {
|
|
expect(gotFail).toBe(true);
|
|
expect(gotDone).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should correctly list which extensions are installed", function () {
|
|
runs(function () {
|
|
waitsForDone(ExtensionManager.downloadRegistry(), "loading registry");
|
|
});
|
|
mockLoadExtensions();
|
|
runs(function () {
|
|
Object.keys(mockRegistry).forEach(function (extId) {
|
|
if (extId === "mock-extension-1" || extId === "mock-extension-2") {
|
|
expect(ExtensionManager.extensions[extId].installInfo.status).toEqual(ExtensionManager.ENABLED);
|
|
} else {
|
|
expect(ExtensionManager.extensions[extId].installInfo).toBeUndefined();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should list an extension that is installed but failed to load", function () {
|
|
runs(function () {
|
|
waitsForDone(ExtensionManager.downloadRegistry(), "loading registry");
|
|
});
|
|
mockLoadExtensions(["user/mock-extension-3"], true);
|
|
runs(function () {
|
|
expect(ExtensionManager.extensions["mock-extension-3"].installInfo.status).toEqual(ExtensionManager.START_FAILED);
|
|
});
|
|
});
|
|
|
|
it("should list an extension that is installed but disabled", function () {
|
|
runs(function () {
|
|
waitsForDone(ExtensionManager.downloadRegistry(), "loading registry");
|
|
});
|
|
mockLoadExtensions(["user/mock-extension-3"], "disabled");
|
|
runs(function () {
|
|
expect(ExtensionManager.extensions["mock-extension-3"].installInfo.status).toEqual(ExtensionManager.DISABLED);
|
|
});
|
|
});
|
|
|
|
it("should set the title for a legacy extension based on its folder name", function () {
|
|
mockLoadExtensions();
|
|
runs(function () {
|
|
expect(ExtensionManager.extensions["mock-legacy-extension"].installInfo.metadata.title).toEqual("mock-legacy-extension");
|
|
});
|
|
});
|
|
|
|
it("should determine the location type for installed extensions", function () {
|
|
mockLoadExtensions();
|
|
runs(function () {
|
|
expect(ExtensionManager.extensions["mock-extension-1"].installInfo.locationType).toEqual(ExtensionManager.LOCATION_DEFAULT);
|
|
expect(ExtensionManager.extensions["mock-extension-2"].installInfo.locationType).toEqual(ExtensionManager.LOCATION_DEV);
|
|
expect(ExtensionManager.extensions["mock-legacy-extension"].installInfo.locationType).toEqual(ExtensionManager.LOCATION_USER);
|
|
});
|
|
});
|
|
|
|
it("should raise a statusChange event when an extension is loaded", function () {
|
|
var spy = jasmine.createSpy();
|
|
runs(function () {
|
|
ExtensionManager.on("statusChange.unit-test", spy);
|
|
mockLoadExtensions(["default/mock-extension-1"]);
|
|
});
|
|
runs(function () {
|
|
expect(spy).toHaveBeenCalledWith(jasmine.any(Object), "mock-extension-1");
|
|
});
|
|
});
|
|
|
|
it("should raise a statusChange event when a legacy extension is loaded, with its path as the id", function () {
|
|
var spy = jasmine.createSpy();
|
|
runs(function () {
|
|
ExtensionManager.on("statusChange.unit-test", spy);
|
|
mockLoadExtensions(["user/mock-legacy-extension"]);
|
|
});
|
|
runs(function () {
|
|
expect(spy).toHaveBeenCalledWith(jasmine.any(Object), "mock-legacy-extension");
|
|
});
|
|
});
|
|
|
|
it("should remove an extension and raise a statusChange event", function () {
|
|
var spy = jasmine.createSpy();
|
|
runs(function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
});
|
|
runs(function () {
|
|
ExtensionManager.on("statusChange.unit-test", spy);
|
|
waitsForDone(ExtensionManager.remove("mock-extension-3"));
|
|
});
|
|
runs(function () {
|
|
var mockPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files");
|
|
expect(removedPath).toBe(mockPath + "/user/mock-extension-3");
|
|
expect(spy).toHaveBeenCalledWith(jasmine.any(Object), "mock-extension-3");
|
|
expect(ExtensionManager.extensions["mock-extension-3"].installInfo).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
it("should disable an extension and raise a statusChange event", function () {
|
|
var spy = jasmine.createSpy();
|
|
runs(function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
});
|
|
runs(function () {
|
|
ExtensionManager.on("statusChange.unit-test", spy);
|
|
waitsForDone(ExtensionManager.disable("mock-extension-3"));
|
|
});
|
|
runs(function () {
|
|
var mockPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files");
|
|
expect(disabledFilePath).toBe(mockPath + "/user/mock-extension-3" + "/.disabled");
|
|
expect(spy).toHaveBeenCalledWith(jasmine.any(Object), "mock-extension-3");
|
|
expect(ExtensionManager.extensions["mock-extension-3"].installInfo.status).toEqual(ExtensionManager.DISABLED);
|
|
});
|
|
});
|
|
|
|
it("should enable an extension and raise a statusChange event", function () {
|
|
var spy = jasmine.createSpy();
|
|
runs(function () {
|
|
mockLoadExtensions(["user/mock-extension-2"], "disable");
|
|
});
|
|
runs(function () {
|
|
ExtensionManager.on("statusChange.unit-test", spy);
|
|
waitsForDone(ExtensionManager.enable("mock-extension-2"));
|
|
});
|
|
runs(function () {
|
|
var mockPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files");
|
|
expect(disabledFilePath).toBe(mockPath + "/user/mock-extension-2" + "/.disabled");
|
|
expect(spy).toHaveBeenCalledWith(jasmine.any(Object), "mock-extension-2");
|
|
expect(ExtensionManager.extensions["mock-extension-2"].installInfo.status).toEqual(ExtensionManager.ENABLED);
|
|
});
|
|
});
|
|
|
|
it("should fail when trying to remove an extension that's not installed", function () {
|
|
var finished = false;
|
|
runs(function () {
|
|
ExtensionManager.remove("mock-extension-3")
|
|
.done(function () {
|
|
finished = true;
|
|
expect("tried to remove a nonexistent extension").toBe(false);
|
|
})
|
|
.fail(function () {
|
|
finished = true;
|
|
});
|
|
});
|
|
waitsFor(function () { return finished; }, "finish removal");
|
|
});
|
|
|
|
it("should fail when trying to disable an extension that's not installed", function () {
|
|
var finished = false;
|
|
runs(function () {
|
|
ExtensionManager.disable("mock-extension-3")
|
|
.done(function () {
|
|
finished = true;
|
|
expect("tried to disable a nonexistent extension").toBe(false);
|
|
})
|
|
.fail(function () {
|
|
finished = true;
|
|
});
|
|
});
|
|
waitsFor(function () { return finished; }, "finish disabling");
|
|
});
|
|
|
|
it("should fail when trying to enable an extension that's not installed", function () {
|
|
var finished = false;
|
|
runs(function () {
|
|
ExtensionManager.enable("mock-extension-3")
|
|
.done(function () {
|
|
finished = true;
|
|
expect("tried to enable a nonexistent extension").toBe(false);
|
|
})
|
|
.fail(function () {
|
|
finished = true;
|
|
});
|
|
});
|
|
waitsFor(function () { return finished; }, "finish enabling");
|
|
});
|
|
|
|
it("should calculate compatibility info for installed extensions", function () {
|
|
function fakeEntry(version) {
|
|
return { metadata: { engines: { brackets: version } } };
|
|
}
|
|
|
|
// Missing version requirement data
|
|
expect(ExtensionManager.getCompatibilityInfo({ metadata: {} }, "1.0.0"))
|
|
.toEqual({isCompatible: true, isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry(null), "1.0.0"))
|
|
.toEqual({isCompatible: true, isLatestVersion: true});
|
|
|
|
// With version requirement data
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry(">0.5.0"), "0.6.0"))
|
|
.toEqual({isCompatible: true, isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry(">0.6.0"), "0.6.0"))
|
|
.toEqual({isCompatible: false, requiresNewer: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry(">0.7.0"), "0.6.0"))
|
|
.toEqual({isCompatible: false, requiresNewer: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("<0.5.0"), "0.4.0"))
|
|
.toEqual({isCompatible: true, isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("<0.4.0"), "0.4.0"))
|
|
.toEqual({isCompatible: false, requiresNewer: false});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("<0.3.0"), "0.4.0"))
|
|
.toEqual({isCompatible: false, requiresNewer: false});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("~1.2"), "1.2.0"))
|
|
.toEqual({isCompatible: true, isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("~1.2"), "1.2.1"))
|
|
.toEqual({isCompatible: true, isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("~1.2"), "1.3.0"))
|
|
.toEqual({isCompatible: false, requiresNewer: false});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("~1.2"), "1.3.1"))
|
|
.toEqual({isCompatible: false, requiresNewer: false});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry("~1.2"), "1.1.0"))
|
|
.toEqual({isCompatible: false, requiresNewer: true});
|
|
});
|
|
|
|
it("should calculate compatibility info for registry extensions", function () {
|
|
// Use the fakeEntry name for consistency with the tests above
|
|
var fakeEntry = makeMockExtension;
|
|
|
|
var curVer = "0.33.0";
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.24"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "1.0.0", isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.24", ">=0.33"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "2.0.0", isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry(["<=0.24", "<=0.29", ">=0.30"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "3.0.0", isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.40", ">=0.50", ">=0.30"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "3.0.0", isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry(["<=0.29", "<=0.29"]), curVer))
|
|
.toEqual({isCompatible: false, requiresNewer: false});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.40", ">=0.50"]), curVer))
|
|
.toEqual({isCompatible: false, requiresNewer: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.15", ">=0.17", "<=0.20"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "2.0.0", isLatestVersion: false, requiresNewer: false});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.24", ">=0.29", ">=0.50"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "2.0.0", isLatestVersion: false, requiresNewer: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry(["<=0.20", ">=0.30", ">=0.50"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "2.0.0", isLatestVersion: false, requiresNewer: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.50", ">=0.30", "<=0.20"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "2.0.0", isLatestVersion: false, requiresNewer: false});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.25", "<=0.40", ">=0.40", "<=0.40", ">=0.42"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "4.0.0", isLatestVersion: false, requiresNewer: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.25", "<=0.30", ">=0.30", "<=0.40", ">=0.32"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "5.0.0", isLatestVersion: true});
|
|
expect(ExtensionManager.getCompatibilityInfo(fakeEntry([">=0.25", ">=0.26", ">=0.30", ">=0.32", ">=0.40", ">=0.50"]), curVer))
|
|
.toEqual({isCompatible: true, compatibleVersion: "4.0.0", isLatestVersion: false, requiresNewer: true});
|
|
});
|
|
|
|
it("should return the correct download URL for an extension", function () {
|
|
expect(ExtensionManager.getExtensionURL("my-cool-extension", "1.2.3"))
|
|
.toBe("http://fake-repository.com/my-cool-extension/my-cool-extension-1.2.3.zip");
|
|
});
|
|
});
|
|
|
|
describe("Auto-Install Extensions", function () {
|
|
|
|
function lookupFileName(zipArray, fileName) {
|
|
return zipArray.some(function (zip) {
|
|
return (zip.file.name === fileName);
|
|
});
|
|
}
|
|
|
|
function addZipFilesToArray(array, result) {
|
|
result.installZips.every(function (zip) {
|
|
array[zip.info.metadata.name] = zip.info.metadata.version;
|
|
});
|
|
result.updateZips.every(function (zip) {
|
|
array[zip.info.metadata.name] = zip.info.metadata.version;
|
|
});
|
|
}
|
|
|
|
it("should correctly handle auto-install extension zip files", function () {
|
|
var promiseFail = false,
|
|
promiseResult = null,
|
|
autoExtensions = {},
|
|
dirPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files/auto-install-extensions1");
|
|
|
|
// Should find 1 install zip
|
|
runs(function () {
|
|
ExtensionManager._getAutoInstallFiles(dirPath, autoExtensions)
|
|
.done(function (result) {
|
|
addZipFilesToArray(autoExtensions, result);
|
|
promiseResult = result;
|
|
})
|
|
.fail(function (err) {
|
|
promiseFail = true;
|
|
expect("[_getAutoInstallFiles] promise rejected with: " + err).toBe("(expected resolved instead)");
|
|
});
|
|
});
|
|
|
|
waitsFor(function () {
|
|
return promiseResult || promiseFail;
|
|
}, "_getAutoInstallFiles success [_getAutoInstallFiles]", 1000);
|
|
|
|
runs(function () {
|
|
expect(promiseResult).toBeTruthy();
|
|
if (promiseResult) {
|
|
expect(promiseResult.installZips.length).toBe(1);
|
|
expect(promiseResult.updateZips.length).toBe(0);
|
|
|
|
// Extension
|
|
expect(lookupFileName(promiseResult.installZips, "mock-extension-v1.0.0.zip")).toBeTruthy();
|
|
|
|
// Not extensions
|
|
expect(lookupFileName(promiseResult.installZips, "ignore-this-folder")).toBeFalsy();
|
|
expect(lookupFileName(promiseResult.installZips, "not-an-extension.zip")).toBeFalsy();
|
|
expect(lookupFileName(promiseResult.installZips, "should-be-ignored.txt")).toBeFalsy();
|
|
}
|
|
});
|
|
|
|
// Subsequent run of first folder should find 0 install zips
|
|
runs(function () {
|
|
promiseFail = false;
|
|
promiseResult = null;
|
|
ExtensionManager._getAutoInstallFiles(dirPath, autoExtensions)
|
|
.done(function (result) {
|
|
addZipFilesToArray(autoExtensions, result);
|
|
promiseResult = result;
|
|
})
|
|
.fail(function (err) {
|
|
promiseFail = true;
|
|
expect("[_getAutoInstallFiles] promise rejected with: " + err).toBe("(expected resolved instead)");
|
|
});
|
|
});
|
|
|
|
waitsFor(function () {
|
|
return promiseResult || promiseFail;
|
|
}, "_getAutoInstallFiles success [_getAutoInstallFiles]", 1000);
|
|
|
|
runs(function () {
|
|
expect(promiseResult).toBeTruthy();
|
|
if (promiseResult) {
|
|
expect(promiseResult.installZips.length).toBe(0);
|
|
expect(promiseResult.updateZips.length).toBe(0);
|
|
|
|
// Extension already installed
|
|
expect(lookupFileName(promiseResult.installZips, "mock-extension-v1.0.0.zip")).toBeFalsy();
|
|
|
|
// Not extensions
|
|
expect(lookupFileName(promiseResult.installZips, "ignore-this-folder")).toBeFalsy();
|
|
expect(lookupFileName(promiseResult.installZips, "not-an-extension.zip")).toBeFalsy();
|
|
expect(lookupFileName(promiseResult.installZips, "should-be-ignored.txt")).toBeFalsy();
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
it("should detect auto-install extension is an update", function () {
|
|
var promiseFail = false,
|
|
promiseResult = null,
|
|
autoExtensions = {},
|
|
dirPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files/auto-install-extensions2");
|
|
|
|
runs(function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">0.1"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
|
|
ExtensionManager._getAutoInstallFiles(dirPath, autoExtensions)
|
|
.done(function (result) {
|
|
addZipFilesToArray(autoExtensions, result);
|
|
promiseResult = result;
|
|
})
|
|
.fail(function (err) {
|
|
promiseFail = true;
|
|
expect("[_getAutoInstallFiles] promise rejected with: " + err).toBe("(expected resolved instead)");
|
|
});
|
|
});
|
|
|
|
waitsFor(function () {
|
|
return promiseResult || promiseFail;
|
|
}, "_getAutoInstallFiles success [_getAutoInstallFiles]", 1000);
|
|
|
|
runs(function () {
|
|
expect(promiseResult).toBeTruthy();
|
|
if (promiseResult) {
|
|
expect(promiseResult.installZips.length).toBe(0);
|
|
expect(promiseResult.updateZips.length).toBe(1);
|
|
|
|
// Extension
|
|
expect(lookupFileName(promiseResult.updateZips, "mock-extension-v1.1.1.zip")).toBeTruthy();
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should correctly handle multiple auto-install extension zip files of same extension", function () {
|
|
var promiseFail = false,
|
|
promiseResult = null,
|
|
autoExtensions = {},
|
|
dirPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files/auto-install-extensions3");
|
|
|
|
// There are 3 zips of same extension using 3 different versions. Only the latest should be returned.
|
|
runs(function () {
|
|
ExtensionManager._getAutoInstallFiles(dirPath, autoExtensions)
|
|
.done(function (result) {
|
|
addZipFilesToArray(autoExtensions, result);
|
|
promiseResult = result;
|
|
})
|
|
.fail(function (err) {
|
|
promiseFail = true;
|
|
expect("[_getAutoInstallFiles] promise rejected with: " + err).toBe("(expected resolved instead)");
|
|
});
|
|
});
|
|
|
|
waitsFor(function () {
|
|
return promiseResult || promiseFail;
|
|
}, "_getAutoInstallFiles success [_getAutoInstallFiles]", 1000);
|
|
|
|
runs(function () {
|
|
expect(promiseResult).toBeTruthy();
|
|
if (promiseResult) {
|
|
expect(promiseResult.installZips.length).toBe(1);
|
|
expect(promiseResult.updateZips.length).toBe(0);
|
|
|
|
// Latest version
|
|
// Note that files are named to try to force code to hit desired branches, but order is arbitrary.
|
|
expect(lookupFileName(promiseResult.installZips, "b-mock-extension-v1.1.1.zip")).toBeTruthy();
|
|
|
|
// Older versions
|
|
expect(lookupFileName(promiseResult.installZips, "a-mock-extension-v1.0.0.zip")).toBeFalsy();
|
|
expect(lookupFileName(promiseResult.installZips, "c-mock-extension-v1.1.0.zip")).toBeFalsy();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("ExtensionManagerView Model", function () {
|
|
describe("when initialized from registry", function () {
|
|
var model;
|
|
|
|
beforeEach(function () {
|
|
runs(function () {
|
|
mockRegistry = JSON.parse(mockRegistryForSearch);
|
|
model = new ExtensionManagerViewModel.RegistryViewModel();
|
|
waitsForDone(model.initialize(), "model initialization");
|
|
});
|
|
runs(function () {
|
|
// Mock load some extensions, so we can make sure they don't show up in the filtered model in this case.
|
|
mockLoadExtensions();
|
|
});
|
|
});
|
|
|
|
afterEach(function () {
|
|
model.dispose();
|
|
model = null;
|
|
});
|
|
|
|
it("should initialize itself from the extension list", function () {
|
|
expect(model.extensions).toEqual(ExtensionManager.extensions);
|
|
});
|
|
|
|
it("should start with the full set sorted in reverse download count order", function () {
|
|
PreferencesManager.set("extensions.sort", "downloadCount");
|
|
model._setSortedExtensionList(ExtensionManager.extensions, false);
|
|
expect(model.filterSet).toEqual(["item-6", "item-4", "item-3", "find-uniq1-in-name", "item-2", "item-5"]);
|
|
});
|
|
|
|
it("should start with the full set sorted in reverse publish date order", function () {
|
|
PreferencesManager.set("extensions.sort", "publishedDate");
|
|
model._setSortedExtensionList(ExtensionManager.extensions, false);
|
|
expect(model.filterSet).toEqual(["item-5", "item-6", "item-2", "find-uniq1-in-name", "item-4", "item-3"]);
|
|
});
|
|
|
|
it("should search case-insensitively for a keyword in the metadata for a given list of registry ids", function () {
|
|
model.filter("uniq1");
|
|
expect(model.filterSet).toEqual(["find-uniq1-in-name"]);
|
|
model.filter("uniq2");
|
|
expect(model.filterSet).toEqual(["item-2"]);
|
|
model.filter("uniq3");
|
|
expect(model.filterSet).toEqual(["item-3"]);
|
|
model.filter("uniq4");
|
|
expect(model.filterSet).toEqual(["item-4"]);
|
|
model.filter("uniq5");
|
|
expect(model.filterSet).toEqual(["item-5"]);
|
|
model.filter("uniq6");
|
|
expect(model.filterSet).toEqual(["item-6"]);
|
|
model.filter("uniqin1and5");
|
|
expect(model.filterSet).toEqual(["item-5", "find-uniq1-in-name"]); // sorted in reverse publish date order
|
|
});
|
|
|
|
it("should 'AND' space-separated search terms", function () {
|
|
model.filter("UNIQ2 in author name");
|
|
expect(model.filterSet).toEqual(["item-2"]);
|
|
model.filter("UNIQ2 name");
|
|
expect(model.filterSet).toEqual(["item-2"]);
|
|
model.filter("UNIQ2 name author");
|
|
expect(model.filterSet).toEqual(["item-2"]);
|
|
model.filter("UNIQ2 uniq3");
|
|
expect(model.filterSet).toEqual([]);
|
|
});
|
|
|
|
it("should return correct results when subsequent queries are longer versions of previous queries", function () {
|
|
model.filter("uniqin1and5");
|
|
model.filter("uniqin1and5-2");
|
|
expect(model.filterSet).toEqual(["item-5"]);
|
|
});
|
|
|
|
it("should go back to the full sorted set when cleared", function () {
|
|
model.filter("uniq1");
|
|
model.filter("");
|
|
expect(model.filterSet).toEqual(["item-5", "item-6", "item-2", "find-uniq1-in-name", "item-4", "item-3"]);
|
|
});
|
|
|
|
it("longer versions of previous queries, and not, should also work with spaces", function () {
|
|
model.filter("name");
|
|
expect(model.filterSet).toEqual(["item-2", "find-uniq1-in-name"]);
|
|
model.filter("name uniq");
|
|
expect(model.filterSet).toEqual(["item-2", "find-uniq1-in-name"]);
|
|
model.filter("name uniq2");
|
|
expect(model.filterSet).toEqual(["item-2"]);
|
|
model.filter("name uniq");
|
|
expect(model.filterSet).toEqual(["item-2", "find-uniq1-in-name"]);
|
|
model.filter("name");
|
|
expect(model.filterSet).toEqual(["item-2", "find-uniq1-in-name"]);
|
|
});
|
|
|
|
it("should trigger filter event when filter changes", function () {
|
|
var gotEvent = false;
|
|
model.on("filter", function () {
|
|
gotEvent = true;
|
|
});
|
|
model.filter("uniq1");
|
|
expect(gotEvent).toBe(true);
|
|
});
|
|
});
|
|
|
|
|
|
describe("when initialized themes from registry", function () {
|
|
var model;
|
|
|
|
beforeEach(function () {
|
|
runs(function () {
|
|
mockRegistry = JSON.parse(mockRegistryThemesText);
|
|
model = new ExtensionManagerViewModel.ThemesViewModel();
|
|
waitsForDone(model.initialize(), "model initialization");
|
|
});
|
|
runs(function () {
|
|
// Mock load some extensions, so we can make sure they don't show up in the filtered model in this case.
|
|
mockLoadExtensions();
|
|
});
|
|
});
|
|
|
|
afterEach(function () {
|
|
model.dispose();
|
|
model = null;
|
|
});
|
|
|
|
it("should initialize itself from the extension list", function () {
|
|
expect(model.extensions).toEqual(ExtensionManager.extensions);
|
|
});
|
|
|
|
it("should start with the full set sorted in reverse publish date order", function () {
|
|
expect(model.filterSet).toEqual(["theme-1", "theme-2"]);
|
|
});
|
|
|
|
it("should start with the full set sorted in reverse download count order", function () {
|
|
PreferencesManager.set("extensions.sort", "downloadCount");
|
|
model._setSortedExtensionList(ExtensionManager.extensions, true);
|
|
expect(model.filterSet).toEqual(["theme-2", "theme-1"]);
|
|
});
|
|
});
|
|
|
|
|
|
describe("when initialized from local extension list", function () {
|
|
var model, origExtensions;
|
|
|
|
beforeEach(function () {
|
|
runs(function () {
|
|
origExtensions = ExtensionManager.extensions;
|
|
ExtensionManager._setExtensions(JSON.parse(mockExtensionList));
|
|
model = new ExtensionManagerViewModel.InstalledViewModel();
|
|
waitsForDone(model.initialize());
|
|
});
|
|
});
|
|
|
|
afterEach(function () {
|
|
model.dispose();
|
|
model = null;
|
|
ExtensionManager._setExtensions(origExtensions);
|
|
});
|
|
|
|
it("should initialize itself from the extension list", function () {
|
|
expect(model.extensions).toEqual(ExtensionManager.extensions);
|
|
});
|
|
|
|
it("should only contain dev and user extensions, sorted case-insensitively on the extension title or name (or last segment of path name for legacy extensions)", function () {
|
|
expect(model.filterSet).toEqual(["registered-extension", "dev-extension", "/path/to/extensions/user/legacy-extension", "unregistered-extension", "Z-capital-extension"]);
|
|
});
|
|
|
|
it("should include a newly-installed extension", function () {
|
|
mockLoadExtensions(["user/install-later-extension"]);
|
|
runs(function () {
|
|
expect(model.filterSet.indexOf("install-later-extension")).toBe(2);
|
|
});
|
|
});
|
|
|
|
it("should include a newly-installed disabled extension", function () {
|
|
mockLoadExtensions(["user/another-great-extension"], "disabled");
|
|
runs(function () {
|
|
expect(model.filterSet.indexOf("another-great-extension")).toBe(1);
|
|
});
|
|
});
|
|
|
|
it("should raise an event when an extension is installed", function () {
|
|
var calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
});
|
|
mockLoadExtensions(["user/install-later-extension"]);
|
|
runs(function () {
|
|
expect(calledId).toBe("install-later-extension");
|
|
});
|
|
});
|
|
|
|
it("should raise an event when an extension is disabled", function () {
|
|
var calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
});
|
|
mockLoadExtensions(["user/another-great-extension"], "disabled");
|
|
runs(function () {
|
|
expect(calledId).toBe("another-great-extension");
|
|
});
|
|
});
|
|
|
|
it("should not include a removed extension", function () {
|
|
runs(function () {
|
|
waitsForDone(ExtensionManager.remove("registered-extension"));
|
|
});
|
|
runs(function () {
|
|
expect(model.filterSet.indexOf("registered-extension")).toBe(-1);
|
|
});
|
|
});
|
|
|
|
it("should raise an event when an extension is removed", function () {
|
|
var calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
waitsForDone(ExtensionManager.remove("registered-extension"));
|
|
});
|
|
runs(function () {
|
|
expect(calledId).toBe("registered-extension");
|
|
});
|
|
});
|
|
|
|
it("should mark an extension for removal and raise an event without actually removing it", function () {
|
|
var id = "registered-extension", calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
ExtensionManager.markForRemoval(id, true);
|
|
expect(calledId).toBe(id);
|
|
expect(ExtensionManager.isMarkedForRemoval(id)).toBe(true);
|
|
expect(model.filterSet.indexOf(id)).not.toBe(-1);
|
|
expect(ExtensionManager.hasExtensionsToRemove()).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should unmark an extension previously marked for removal and raise an event", function () {
|
|
var id = "registered-extension", calledId;
|
|
runs(function () {
|
|
ExtensionManager.markForRemoval(id, true);
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
ExtensionManager.markForRemoval(id, false);
|
|
expect(calledId).toBe(id);
|
|
expect(ExtensionManager.isMarkedForRemoval(id)).toBe(false);
|
|
expect(ExtensionManager.hasExtensionsToRemove()).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should remove extensions previously marked for removal", function () {
|
|
var removedIds = {}, removedPaths = {};
|
|
runs(function () {
|
|
ExtensionManager.markForRemoval("registered-extension", true);
|
|
ExtensionManager.markForRemoval("Z-capital-extension", false);
|
|
model.on("change", function (e, id) {
|
|
removedIds[id] = true;
|
|
removedPaths[removedPath] = true;
|
|
});
|
|
waitsForDone(ExtensionManager.removeMarkedExtensions());
|
|
});
|
|
runs(function () {
|
|
// Test a removed extension, an extension that was unmarked for removal, and an extension that was never marked.
|
|
expect(removedIds["registered-extension"]).toBe(true);
|
|
expect(removedPaths["/path/to/extensions/user/registered-extension"]).toBe(true);
|
|
expect(removedIds["Z-capital-extension"]).toBeUndefined();
|
|
expect(removedPaths["/path/to/extensions/user/Z-capital-extension"]).toBeUndefined();
|
|
expect(removedIds["unregistered-extension"]).toBeUndefined();
|
|
expect(removedPaths["/path/to/extensions/user/unregistered-extension"]).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("should mark an extension for disabling and raise an event", function () {
|
|
var id = "registered-extension", calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
ExtensionManager.markForDisabling(id, true);
|
|
expect(calledId).toBe(id);
|
|
expect(ExtensionManager.isMarkedForDisabling(id)).toBe(true);
|
|
expect(model.filterSet.indexOf(id)).not.toBe(-1);
|
|
expect(ExtensionManager.hasExtensionsToDisable()).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should unmark an extension previously marked for disabling and raise an event", function () {
|
|
var id = "registered-extension", calledId;
|
|
runs(function () {
|
|
ExtensionManager.markForDisabling(id, true);
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
ExtensionManager.markForDisabling(id, false);
|
|
expect(calledId).toBe(id);
|
|
expect(ExtensionManager.isMarkedForRemoval(id)).toBe(false);
|
|
expect(ExtensionManager.hasExtensionsToRemove()).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should disable extensions previously marked for disabling and not remove them from the model", function () {
|
|
var disabledIds = {}, disabledPaths = {};
|
|
runs(function () {
|
|
ExtensionManager.markForDisabling("registered-extension", true);
|
|
ExtensionManager.markForDisabling("Z-capital-extension", false);
|
|
model.on("change", function (e, id) {
|
|
disabledIds[id] = true;
|
|
disabledPaths[disabledFilePath] = true;
|
|
});
|
|
waitsForDone(ExtensionManager.disableMarkedExtensions());
|
|
});
|
|
runs(function () {
|
|
// Test the enabled extension, the extension that was unmarked for disabling, and an extension that was never marked.
|
|
expect(disabledIds["registered-extension"]).toBe(true);
|
|
expect(disabledPaths["/path/to/extensions/user/registered-extension/.disabled"]).toBe(true);
|
|
expect(model.filterSet.indexOf("registered-extension")).toBe(0);
|
|
expect(disabledIds["Z-capital-extension"]).toBeUndefined();
|
|
expect(disabledPaths["/path/to/extensions/user/Z-capital-extension/.disabled"]).toBeUndefined();
|
|
expect(disabledIds["unregistered-extension"]).toBeUndefined();
|
|
expect(disabledPaths["/path/to/extensions/user/unregistered-extension/.disabled"]).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("should delete the .disabled file, enable the extension and raise an event", function () {
|
|
var extension = "registered-extension",
|
|
calledId;
|
|
runs(function () {
|
|
mockLoadExtensions(["registered-extension"], "disabled");
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
});
|
|
runs(function () {
|
|
waitsForDone(ExtensionManager.enable(extension));
|
|
});
|
|
runs(function () {
|
|
expect(calledId).toBe(extension);
|
|
});
|
|
});
|
|
|
|
it("should mark an extension for update and raise an event", function () {
|
|
var id = "registered-extension", calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
ExtensionManager.updateFromDownload({
|
|
localPath: "/path/to/downloaded/file.zip",
|
|
name: id,
|
|
installationStatus: "NEEDS_UPDATE"
|
|
});
|
|
expect(calledId).toBe(id);
|
|
expect(ExtensionManager.isMarkedForUpdate(id)).toBe(true);
|
|
expect(ExtensionManager.hasExtensionsToUpdate()).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should unmark an extension for update, deleting the package and raising an event", function () {
|
|
var id = "registered-extension",
|
|
filename = "/path/to/downloaded/file.zip",
|
|
file = FileSystem.getFileForPath(filename),
|
|
calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
ExtensionManager.updateFromDownload({
|
|
localPath: filename,
|
|
name: id,
|
|
installationStatus: "NEEDS_UPDATE"
|
|
});
|
|
calledId = null;
|
|
spyOn(file, "unlink");
|
|
ExtensionManager.removeUpdate(id);
|
|
expect(calledId).toBe(id);
|
|
expect(file.unlink).toHaveBeenCalled();
|
|
expect(ExtensionManager.isMarkedForUpdate()).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should change an extension marked for removal to update raise an event", function () {
|
|
var id = "registered-extension", calledId;
|
|
runs(function () {
|
|
model.on("change", function (e, id) {
|
|
calledId = id;
|
|
});
|
|
ExtensionManager.markForRemoval(id, true);
|
|
expect(calledId).toBe(id);
|
|
calledId = null;
|
|
ExtensionManager.updateFromDownload({
|
|
localPath: "/path/to/downloaded/file.zip",
|
|
name: id,
|
|
installationStatus: "NEEDS_UPDATE"
|
|
});
|
|
expect(calledId).toBe(id);
|
|
expect(ExtensionManager.isMarkedForRemoval()).toBe(false);
|
|
expect(ExtensionManager.hasExtensionsToRemove()).toBe(false);
|
|
expect(ExtensionManager.isMarkedForUpdate(id)).toBe(true);
|
|
expect(ExtensionManager.hasExtensionsToUpdate()).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should update extensions marked for update", function () {
|
|
var id = "registered-extension",
|
|
filename = "/path/to/downloaded/file.zip",
|
|
file = FileSystem.getFileForPath("/path/to/downloaded/file.zip");
|
|
runs(function () {
|
|
ExtensionManager.updateFromDownload({
|
|
localPath: filename,
|
|
name: id,
|
|
installationStatus: "NEEDS_UPDATE"
|
|
});
|
|
expect(ExtensionManager.isMarkedForUpdate()).toBe(false);
|
|
spyOn(file, "unlink");
|
|
var d = $.Deferred();
|
|
spyOn(Package, "installUpdate").andReturn(d.promise());
|
|
d.resolve();
|
|
waitsForDone(ExtensionManager.updateExtensions());
|
|
});
|
|
runs(function () {
|
|
expect(file.unlink).toHaveBeenCalled();
|
|
expect(Package.installUpdate).toHaveBeenCalledWith(filename, id);
|
|
});
|
|
});
|
|
|
|
it("should recognize when an update is available", function () {
|
|
var id = "registered-extension";
|
|
runs(function () {
|
|
console.log(model.extensions[id]);
|
|
expect(model._getEntry(id).updateAvailable).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Local File Install", function () {
|
|
var didReload;
|
|
|
|
function clickOk() {
|
|
var $okBtn = $(".install-extension-dialog.instance .dialog-button[data-button-id='ok']");
|
|
$okBtn.click();
|
|
}
|
|
|
|
beforeEach(function () {
|
|
// Mock reloading the app so we don't actually reload :)
|
|
didReload = false;
|
|
spyOn(CommandManager, "execute").andCallFake(function (id) {
|
|
if (id === Commands.APP_RELOAD) {
|
|
didReload = true;
|
|
} else {
|
|
CommandManager.execute.apply(this, arguments);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should set flag to keep local files for new installs", function () {
|
|
var filename = "/path/to/downloaded/file.zip",
|
|
file = FileSystem.getFileForPath(filename),
|
|
result;
|
|
|
|
runs(function () {
|
|
spyOn(file, "unlink");
|
|
|
|
// Mock install
|
|
var d = $.Deferred().resolve({});
|
|
spyOn(Package, "installFromPath").andReturn(d.promise());
|
|
|
|
var promise = InstallExtensionDialog.installUsingDialog(file);
|
|
promise.done(function (_result) {
|
|
result = _result;
|
|
});
|
|
|
|
clickOk();
|
|
waitsForDone(promise);
|
|
});
|
|
|
|
runs(function () {
|
|
expect(Package.installFromPath).toHaveBeenCalledWith(filename);
|
|
expect(result.keepFile).toBe(true);
|
|
expect(file.unlink).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("should set flag to keep local files for updates", function () {
|
|
var id = "mock-extension",
|
|
filename = "/path/to/downloaded/file.zip",
|
|
file = FileSystem.getFileForPath(filename),
|
|
result,
|
|
dialogDeferred = new $.Deferred(),
|
|
$mockDlg,
|
|
didClose;
|
|
|
|
// Mock update
|
|
var installResult = {
|
|
installationStatus: Package.InstallationStatuses.NEEDS_UPDATE,
|
|
name: id,
|
|
localPath: filename
|
|
};
|
|
|
|
// Mock dialog
|
|
spyOn(Dialogs, "showModalDialog").andCallFake(function () {
|
|
$mockDlg = $("<div/>");
|
|
didClose = false;
|
|
|
|
return {
|
|
getElement: function () { return $mockDlg; },
|
|
close: function () { didClose = true; }
|
|
};
|
|
});
|
|
|
|
spyOn(file, "unlink");
|
|
|
|
// Mock installs to avoid calling into node ExtensionManagerDomain
|
|
spyOn(Package, "installFromPath").andCallFake(function () {
|
|
return new $.Deferred().resolve(installResult).promise();
|
|
});
|
|
spyOn(Package, "installUpdate").andCallFake(function () {
|
|
return new $.Deferred().resolve(installResult).promise();
|
|
});
|
|
|
|
runs(function () {
|
|
// Mimic drag and drop
|
|
InstallExtensionDialog.updateUsingDialog(file)
|
|
.done(function (_result) {
|
|
result = _result;
|
|
|
|
// Mark for update
|
|
ExtensionManager.updateFromDownload(result);
|
|
|
|
dialogDeferred.resolve();
|
|
})
|
|
.fail(dialogDeferred.reject);
|
|
|
|
clickOk();
|
|
waitsForDone(dialogDeferred.promise(), "InstallExtensionDialog.updateUsingDialog");
|
|
});
|
|
|
|
runs(function () {
|
|
// InstallExtensionDialog should set keepFile=true
|
|
expect(result.keepFile).toBe(true);
|
|
|
|
// Run update, creates dialog DIALOG_ID_CHANGE_EXTENSIONS
|
|
ExtensionManagerDialog._performChanges();
|
|
$mockDlg.triggerHandler("buttonClick", Dialogs.DIALOG_BTN_OK);
|
|
});
|
|
|
|
waitsFor(function () {
|
|
return didClose;
|
|
}, "DIALOG_ID_CHANGE_EXTENSIONS closed");
|
|
|
|
runs(function () {
|
|
expect(file.unlink).not.toHaveBeenCalled();
|
|
expect(didReload).toBe(true);
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
describe("ExtensionManagerView", function () {
|
|
|
|
beforeEach(function () {
|
|
setupExtensionManagerViewTests(this);
|
|
spyOn(brackets, "getLocale").andReturn("en");
|
|
});
|
|
|
|
|
|
afterEach(function () {
|
|
cleanupExtensionManagerViewTests();
|
|
});
|
|
|
|
describe("when showing registry entries", function () {
|
|
it("should populate itself with registry entries and display their fields when created", function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
runs(function () {
|
|
_.forEach(mockRegistry, function (item) {
|
|
// Should show the title if specified, otherwise the bare name.
|
|
if (item.metadata.title) {
|
|
expect(view).toHaveText(item.metadata.title);
|
|
} else {
|
|
expect(view).toHaveText(item.metadata.name);
|
|
}
|
|
|
|
// Simple fields
|
|
[item.metadata.version,
|
|
item.metadata.author && item.metadata.author.name,
|
|
item.totalDownloads]
|
|
.forEach(function (value) {
|
|
if (value) {
|
|
expect(view).toHaveText(value);
|
|
}
|
|
});
|
|
if (item.metadata.homepage) {
|
|
expect(view).toHaveLink(item.metadata.homepage);
|
|
}
|
|
|
|
// Array-valued fields
|
|
[item.metadata.categories].forEach(function (arr) {
|
|
if (arr) {
|
|
arr.forEach(function (value) {
|
|
expect(view).toHaveText(value);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should display original description", function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
runs(function () {
|
|
_.forEach(mockRegistry, function (item) {
|
|
if (item.metadata.description) {
|
|
if (StringUtils.truncate(item.metadata.description, 200) === undefined) {
|
|
expect(view).toHaveText(item.metadata.description);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should display shortened description", function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
runs(function () {
|
|
_.forEach(mockRegistry, function (item) {
|
|
if (item.metadata.description) {
|
|
var shortDescription = StringUtils.truncate(item.metadata.description, 200);
|
|
if (shortDescription !== undefined) {
|
|
expect(view).toHaveText(shortDescription);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should display owner even for installed items", function () {
|
|
ExtensionManager._setExtensions(JSON.parse(mockExtensionList));
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
console.log(view);
|
|
_.forEach(JSON.parse(mockExtensionList), function (item) {
|
|
if (item.installInfo && item.registryInfo) {
|
|
// Owner--should show only the owner name, not the authenticator
|
|
expect(view).toHaveText(item.registryInfo.owner.split(":")[1]);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should show an install button for each item", function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
runs(function () {
|
|
_.forEach(mockRegistry, function (item) {
|
|
var $button = $("button.install[data-extension-id=" + item.metadata.name + "]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
// 'Install' button state
|
|
it("should show disabled install buttons for items that are already installed", function () {
|
|
mockLoadExtensions(["user/mock-extension-3", "user/mock-extension-4"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
runs(function () {
|
|
_.forEach(mockRegistry, function (item) {
|
|
var $button = $("button.install[data-extension-id=" + item.metadata.name + "]", view.$el);
|
|
if (item.metadata.name === "mock-extension-3" || item.metadata.name === "mock-extension-4") {
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
} else {
|
|
expect($button.prop("disabled")).toBeFalsy();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should show disabled install button if requires newer API version", function () { // isCompatible: false, requiresNewer: true
|
|
runs(function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">100.0"]) };
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.install[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
expect($("button.update[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
|
|
var $warning = $(".alert.warning", view.$el);
|
|
expect($warning.length).toBe(1);
|
|
expect($warning.text().trim()).toBe(Strings.EXTENSION_INCOMPATIBLE_NEWER);
|
|
});
|
|
});
|
|
|
|
it("should show disabled install button if requires older API version", function () { // isCompatible: false, requiresNewer: false
|
|
runs(function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension(["<0.1"]) };
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.install[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
expect($("button.update[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
|
|
var $warning = $(".alert.warning", view.$el);
|
|
expect($warning.length).toBe(1);
|
|
expect($warning.text().trim()).toBe(Strings.EXTENSION_INCOMPATIBLE_OLDER);
|
|
});
|
|
});
|
|
|
|
it("should show enabled install button if latest requires newer API version", function () { // isCompatible: true, isLatestVersion: false, requiresNewer: true
|
|
runs(function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">100.0"]) };
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.install[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeFalsy();
|
|
expect($("button.update[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
|
|
var $warning = $(".alert.warning", view.$el);
|
|
expect($warning.length).toBe(1);
|
|
expect($warning.text().trim()).toBe(StringUtils.format(Strings.EXTENSION_LATEST_INCOMPATIBLE_NEWER, "2.0.0", "1.0.0"));
|
|
});
|
|
});
|
|
|
|
it("should show enabled install button if latest requires older API version", function () { // isCompatible: true, isLatestVersion: false, requiresNewer: false
|
|
runs(function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", "<0.2"]) };
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.install[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeFalsy();
|
|
expect($("button.update[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
|
|
var $warning = $(".alert.warning", view.$el);
|
|
expect($warning.length).toBe(1);
|
|
expect($warning.text().trim()).toBe(StringUtils.format(Strings.EXTENSION_LATEST_INCOMPATIBLE_OLDER, "2.0.0", "1.0.0"));
|
|
});
|
|
});
|
|
|
|
// 'Install' button action
|
|
it("should bring up the install dialog and install an item when install button is clicked", function () {
|
|
runs(function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.install[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
$button.click();
|
|
expect(InstallExtensionDialog.installUsingDialog)
|
|
.toHaveBeenCalledWith("http://fake-repository.com/mock-extension-3/mock-extension-3-1.0.0.zip");
|
|
});
|
|
});
|
|
|
|
it("should install latest compatible version", function () {
|
|
runs(function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">0.2", ">100.0"]) };
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.install[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
$button.click();
|
|
expect(InstallExtensionDialog.installUsingDialog)
|
|
.toHaveBeenCalledWith("http://fake-repository.com/mock-extension/mock-extension-2.0.0.zip");
|
|
});
|
|
});
|
|
|
|
it("should disable the install button for an item immediately after installing it", function () {
|
|
var $button;
|
|
runs(function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
$button = $("button.install[data-extension-id=mock-extension-3]", view.$el);
|
|
$button.click();
|
|
});
|
|
runs(function () {
|
|
// Have to get the button again since the view may have created a new button when re-rendering.
|
|
$button = $("button.install[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
});
|
|
|
|
});
|
|
|
|
// 'Update' button state (see also similar tests for InstalledViewModel below)
|
|
it("should show enabled update button for items that have a compatible update available", function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">0.1"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
|
|
runs(function () {
|
|
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeFalsy();
|
|
|
|
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
expect($(".alert.warning", view.$el).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should show disabled update button for items whose available update requires newer API version", function () { // isLatestVersion: false, requiresNewer: true
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">100.0"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
|
|
runs(function () {
|
|
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
|
|
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
expect($(".alert.warning", view.$el).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should show disabled update button for items whose available update requires older API version", function () { // isLatestVersion: false, requiresNewer: false
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", "<0.2"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
|
|
runs(function () {
|
|
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
|
|
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
expect($(".alert.warning", view.$el).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should show disabled update button for items that are in dev folder and have a compatible update available", function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">0.1"]) };
|
|
var mockInfo = makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0");
|
|
mockInfo.locationType = ExtensionManager.LOCATION_DEV;
|
|
ExtensionManager._setExtensions({
|
|
"mock-extension": {
|
|
installInfo: mockInfo
|
|
}
|
|
});
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
|
|
runs(function () {
|
|
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
// Info links action
|
|
it("should open links in the native browser instead of in Brackets", function () {
|
|
runs(function () {
|
|
mockRegistry = {
|
|
"basic-valid-extension": {
|
|
"metadata": {
|
|
"name": "basic-valid-extension",
|
|
"title": "Basic Valid Extension",
|
|
"version": "1.0.0"
|
|
},
|
|
"owner": "github:someuser",
|
|
"versions": [
|
|
{
|
|
"version": "1.0.0",
|
|
"published": "2013-04-10T18:28:20.530Z"
|
|
}
|
|
]
|
|
}
|
|
};
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
});
|
|
runs(function () {
|
|
var origHref = window.location.href;
|
|
spyOn(NativeApp, "openURLInDefaultBrowser");
|
|
|
|
var event = new window.Event("click", { bubbles: false, cancelable: true });
|
|
window.document.querySelector("a[href='https://github.com/someuser']").dispatchEvent(event);
|
|
|
|
expect(NativeApp.openURLInDefaultBrowser).toHaveBeenCalledWith("https://github.com/someuser");
|
|
expect(window.location.href).toBe(origHref);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when showing installed extensions", function () {
|
|
|
|
it("should show the 'no extensions' message when there are no extensions installed", function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
expect($(".empty-message", view.$el).css("display")).not.toBe("none");
|
|
expect($(".empty-message", view.$el).html()).toEqual(Strings.NO_EXTENSIONS);
|
|
expect($("table", view.$el).css("display")).toBe("none");
|
|
});
|
|
});
|
|
|
|
it("should show the 'no extensions match' message when there are extensions installed but none match the search query", function () {
|
|
mockLoadExtensions(["user/mock-extension-3", "user/mock-extension-4", "user/mock-legacy-extension"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
view.filter("DON'T_FIND_ME_IN_THE_EXTENSION_LIST");
|
|
expect($(".empty-message", view.$el).css("display")).not.toBe("none");
|
|
expect($(".empty-message", view.$el).html()).toEqual(Strings.NO_EXTENSION_MATCHES);
|
|
expect($("table", view.$el).css("display")).toBe("none");
|
|
});
|
|
});
|
|
|
|
it("should show only items that are already installed and have remove and disable/enable buttons for each", function () {
|
|
mockLoadExtensions(["user/mock-extension-3", "user/mock-extension-4", "user/mock-legacy-extension"]);
|
|
mockLoadExtensions(["user/mock-extension-5"], "disabled");
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
expect($(".empty-message", view.$el).css("display")).toBe("none");
|
|
expect($("table", view.$el).css("display")).not.toBe("none");
|
|
_.forEach(mockRegistry, function (item) {
|
|
var $removeButton = $("button.remove[data-extension-id=" + item.metadata.name + "]", view.$el);
|
|
if (item.metadata.name === "mock-extension-3" ||
|
|
item.metadata.name === "mock-extension-4" ||
|
|
item.metadata.name === "mock-legacy-extension" ||
|
|
item.metadata.name === "mock-extension-5") {
|
|
expect(view).toHaveText(item.metadata.name);
|
|
expect($removeButton.length).toBe(1);
|
|
|
|
// should also have disable/enable button
|
|
var isDisable = item.metadata.name === "mock-extension-5" ? false : true,
|
|
$disableButton = $("button." + (isDisable ? "disable" : "enable") + "[data-extension-id=" +
|
|
item.metadata.name + "]", view.$el);
|
|
|
|
expect($disableButton.length).toBe(1);
|
|
|
|
// But no update button
|
|
var $updateButton = $("button.update[data-extension-id=" + item.metadata.name + "]", view.$el);
|
|
expect($updateButton.length).toBe(0);
|
|
} else {
|
|
expect(view).not.toHaveText(item.metadata.name);
|
|
expect($removeButton.length).toBe(0);
|
|
}
|
|
});
|
|
|
|
// And no overall update icon overlay
|
|
expect(model.notifyCount).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should show a newly installed extension", function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
expect(view).not.toHaveText("mock-extension-3");
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
});
|
|
runs(function () {
|
|
expect(view).toHaveText("mock-extension-3");
|
|
});
|
|
});
|
|
|
|
it("should not show extensions in the default folder", function () {
|
|
mockLoadExtensions(["default/mock-extension-1"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
expect(view).not.toHaveText("mock-extension-1");
|
|
});
|
|
});
|
|
|
|
// 'Remove' button state
|
|
it("should show extensions that failed to load with a 'remove' link", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"], true);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
expect(view).toHaveText("mock-extension-3");
|
|
var $removeLink = $("a.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($removeLink.length).toBe(1);
|
|
expect($removeLink.prop("disabled")).toBeFalsy();
|
|
|
|
$removeLink.click();
|
|
expect(ExtensionManager.isMarkedForRemoval("mock-extension-3")).toBe(true);
|
|
var $undoLink = $("a.undo-remove[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($undoLink.length).toBe(1);
|
|
$removeLink = $("a.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($removeLink.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should not have a 'remove' link for extensions in the dev folder that failed to load", function () {
|
|
mockLoadExtensions(["dev/mock-failed-in-dev-folder"], true);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
expect(view).toHaveText("mock-failed-in-dev-folder");
|
|
var $removeLink = $("a.remove[data-extension-id=mock-failed-in-dev-folder]", view.$el);
|
|
expect($removeLink.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should disable the Remove button for extensions in the dev folder", function () {
|
|
mockLoadExtensions(["dev/mock-extension-2"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.remove[data-extension-id=mock-extension-2]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
// Disable button action
|
|
it("should mark the given extension for disabling, hide the buttons and show the undo link", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $disableButton = $("button.disable[data-extension-id=mock-extension-3]", view.$el),
|
|
$removeButton,
|
|
$undoLink;
|
|
$disableButton.click();
|
|
$removeButton = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$undoLink = $("a.undo-disable[data-extension-id=mock-extension-3]", view.$el);
|
|
$disableButton = $("button.disable[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($removeButton.length).toBe(0);
|
|
expect($undoLink.length).toBe(1);
|
|
expect($disableButton.length).toBe(0);
|
|
expect(ExtensionManager.isMarkedForDisabling("mock-extension-3")).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should undo mark for disabling and make the buttons available again", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $disableButton = $("button.disable[data-extension-id=mock-extension-3]", view.$el),
|
|
$removeButton,
|
|
$undoLink;
|
|
$disableButton.click();
|
|
$undoLink = $("a.undo-disable[data-extension-id=mock-extension-3]", view.$el);
|
|
$undoLink.click();
|
|
$removeButton = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$disableButton = $("button.disable[data-extension-id=mock-extension-3]", view.$el);
|
|
$undoLink = $("a.undo-disable[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($removeButton.length).toBe(1);
|
|
expect($undoLink.length).toBe(0);
|
|
expect($disableButton.length).toBe(1);
|
|
expect(ExtensionManager.isMarkedForDisabling("mock-extension-3")).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should be able to disable an extension from the dev folder", function () {
|
|
mockLoadExtensions(["dev/mock-extension-6"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $disableButton = $("button.disable[data-extension-id=mock-extension-6]", view.$el);
|
|
expect($disableButton.prop("disabled")).toBeFalsy();
|
|
$disableButton.click();
|
|
expect(ExtensionManager.isMarkedForDisabling("mock-extension-6")).toBe(true);
|
|
});
|
|
});
|
|
|
|
// 'Remove' button action
|
|
it("should mark the given extension for removal, hide the remove button, and show an undo link", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $removeButton = $("button.remove[data-extension-id=mock-extension-3]", view.$el),
|
|
$disableButton;
|
|
$removeButton.click();
|
|
$disableButton = $("button.disable[data-extension-id=mock-extension-3]", view.$el);
|
|
expect(ExtensionManager.isMarkedForRemoval("mock-extension-3")).toBe(true);
|
|
expect($disableButton.length).toBe(0);
|
|
var $undoLink = $("a.undo-remove[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($undoLink.length).toBe(1);
|
|
$removeButton = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($removeButton.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should undo marking an extension for removal", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$button.click();
|
|
var $undoLink = $("a.undo-remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$undoLink.click();
|
|
expect(ExtensionManager.isMarkedForRemoval("mock-extension-3")).toBe(false);
|
|
$button = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
it("should mark a legacy extension for removal", function () {
|
|
var id = "mock-legacy-extension";
|
|
mockLoadExtensions(["user/" + id]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.remove[data-extension-id='" + id + "']", view.$el);
|
|
$button.click();
|
|
expect(ExtensionManager.isMarkedForRemoval(id)).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should no longer show a fully removed extension", function () {
|
|
mockLoadExtensions(["user/mock-extension-3", "user/mock-extension-4", "user/mock-legacy-extension"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
expect(view).toHaveText("mock-extension-3");
|
|
waitsForDone(ExtensionManager.remove("mock-extension-3"));
|
|
});
|
|
runs(function () {
|
|
expect(view).not.toHaveText("mock-extension-3");
|
|
});
|
|
});
|
|
|
|
// 'Update' button state (see also similar tests for RegistryViewModel above)
|
|
it("should show enabled update button for items that have a compatible update available", function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">0.1"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
waitsForDone(ExtensionManager.downloadRegistry()); // ensure mockRegistry integrated in
|
|
runs(function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeFalsy();
|
|
expect(model.notifyCount).toBe(1);
|
|
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should show disabled update button for items whose available update requires newer API version", function () { // isLatestVersion: false, requiresNewer: true
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">100.0"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
waitsForDone(ExtensionManager.downloadRegistry()); // ensure mockRegistry integrated in
|
|
runs(function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
|
|
// notify count doesn't show extensions that cannot be updated
|
|
expect(model.notifyCount).toBe(0);
|
|
|
|
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
expect($(".alert.warning", view.$el).length).toBe(0);
|
|
});
|
|
});
|
|
it("should show disabled update button for items whose available update requires older API version", function () { // isLatestVersion: false, requiresNewer: false
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", "<0.2"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
waitsForDone(ExtensionManager.downloadRegistry()); // ensure mockRegistry integrated in
|
|
runs(function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
});
|
|
runs(function () {
|
|
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
expect($button.prop("disabled")).toBeTruthy();
|
|
|
|
// notify count doesn't show extensions that cannot be updated
|
|
expect(model.notifyCount).toBe(0);
|
|
|
|
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
|
|
expect($(".alert.warning", view.$el).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
// 'Update' button action
|
|
it("should mark the given extension for update, hide the remove button, and show an undo link", function () {
|
|
var id = "mock-extension-3";
|
|
mockLoadExtensions(["user/" + id]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
ExtensionManager.updateFromDownload({
|
|
name: id,
|
|
installationStatus: "NEEDS_UPDATE"
|
|
});
|
|
expect(ExtensionManager.isMarkedForUpdate(id)).toBe(true);
|
|
var $undoLink = $("a.undo-update[data-extension-id=" + id + "]", view.$el);
|
|
expect($undoLink.length).toBe(1);
|
|
var $button = $("button.remove[data-extension-id=" + id + "]", view.$el);
|
|
expect($button.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should undo marking an extension for update", function () {
|
|
var id = "mock-extension-3",
|
|
filename = "/path/to/downloaded/file.zip",
|
|
file = FileSystem.getFileForPath(filename);
|
|
mockLoadExtensions(["user/" + id]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
ExtensionManager.updateFromDownload({
|
|
name: id,
|
|
installationStatus: "NEEDS_UPDATE",
|
|
localPath: filename
|
|
});
|
|
spyOn(file, "unlink");
|
|
var $undoLink = $("a.undo-update[data-extension-id=" + id + "]", view.$el);
|
|
$undoLink.click();
|
|
expect(ExtensionManager.isMarkedForUpdate(id)).toBe(false);
|
|
expect(file.unlink).toHaveBeenCalled();
|
|
var $button = $("button.remove[data-extension-id=" + id + "]", view.$el);
|
|
expect($button.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
it("should properly return information about available updates and clean it after updates are installed", function () {
|
|
mockRegistry = { "mock-extension": makeMockExtension([">0.1", ">0.1"]) };
|
|
var mockInstallInfo = { "mock-extension": { installInfo: makeMockInstalledVersion(mockRegistry["mock-extension"], "1.0.0") } };
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
waitsForDone(ExtensionManager.downloadRegistry()); // ensure mockRegistry integrated in
|
|
runs(function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
});
|
|
runs(function () {
|
|
var availableUpdates = ExtensionManager.getAvailableUpdates();
|
|
expect(availableUpdates.length).toBe(1);
|
|
// cleanAvailableUpdates shouldn't clean the array
|
|
expect(ExtensionManager.cleanAvailableUpdates(availableUpdates).length).toBe(1);
|
|
// now simulate that update is installed and see if cleanAvailableUpdates() method will work
|
|
ExtensionManager.extensions["mock-extension"].installInfo.metadata.version =
|
|
ExtensionManager.extensions["mock-extension"].registryInfo.metadata.version;
|
|
expect(ExtensionManager.cleanAvailableUpdates(availableUpdates).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
// i18n info
|
|
it("should show correct i18n info if the extension is translated in user's lang", function () {
|
|
var mockInstallInfo = { "mock-extension-4": { installInfo: mockRegistry["mock-extension-4"] } };
|
|
mockInstallInfo["mock-extension-4"].installInfo.metadata.i18n = ["zh-cn", "foo", "en"];
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $extTranslated = $(".ext-translated", view.$el),
|
|
languages = [LocalizationUtils.getLocalizedLabel("en"), "foo", LocalizationUtils.getLocalizedLabel("zh-cn")];
|
|
expect($extTranslated.length).toBe(1);
|
|
expect($extTranslated.text()).toBe(StringUtils.format(Strings.EXTENSION_TRANSLATED_USER_LANG, languages.length));
|
|
expect($extTranslated.attr("title")).toBe(StringUtils.format(Strings.EXTENSION_TRANSLATED_LANGS, languages.join(", ")));
|
|
});
|
|
});
|
|
it("should show correct i18n info if the extension is translated in some other languages", function () {
|
|
var mockInstallInfo = { "mock-extension-4": { installInfo: mockRegistry["mock-extension-4"] } };
|
|
mockInstallInfo["mock-extension-4"].installInfo.metadata.i18n = ["zh-cn"];
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $extTranslated = $(".ext-translated", view.$el),
|
|
languages = [LocalizationUtils.getLocalizedLabel("zh-cn")];
|
|
expect($extTranslated.length).toBe(1);
|
|
expect($extTranslated.text()).toBe(StringUtils.format(Strings.EXTENSION_TRANSLATED_GENERAL, languages.length));
|
|
expect($extTranslated.attr("title")).toBe(StringUtils.format(Strings.EXTENSION_TRANSLATED_LANGS, languages.join(", ")));
|
|
});
|
|
});
|
|
it("should not show i18n info if the extension isn't translated", function () {
|
|
var mockInstallInfo = { "mock-extension-4": { installInfo: mockRegistry["mock-extension-4"] } };
|
|
mockInstallInfo["mock-extension-4"].installInfo.metadata.i18n = [];
|
|
ExtensionManager._setExtensions(mockInstallInfo);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $extTranslated = $(".ext-translated", view.$el);
|
|
expect($extTranslated.length).toBe(0);
|
|
expect($extTranslated.attr("title")).toBe(undefined);
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
|
|
describe("ExtensionManagerDialog", function () {
|
|
var dialogClassShown, didReload, didClose, $mockDlg;
|
|
|
|
describe("_performChanges", function () {
|
|
|
|
beforeEach(function () {
|
|
// Mock popping up dialogs
|
|
dialogClassShown = null;
|
|
didClose = false;
|
|
spyOn(Dialogs, "showModalDialog").andCallFake(function (dlgClass, title, message) {
|
|
dialogClassShown = dlgClass;
|
|
$mockDlg = $("<div/>").addClass(dlgClass);
|
|
// The test will resolve the promise.
|
|
return {
|
|
getElement: function () { return $mockDlg; },
|
|
close: function () { didClose = true; }
|
|
};
|
|
});
|
|
|
|
// Mock reloading the app so we don't actually reload :)
|
|
didReload = false;
|
|
spyOn(CommandManager, "execute").andCallFake(function (id) {
|
|
if (id === Commands.APP_RELOAD) {
|
|
didReload = true;
|
|
} else {
|
|
CommandManager.execute.apply(this, arguments);
|
|
}
|
|
});
|
|
});
|
|
|
|
afterEach(function () {
|
|
ExtensionManager._reset();
|
|
});
|
|
|
|
it("should not show a removal confirmation dialog if no extensions were removed", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
runs(function () {
|
|
ExtensionManagerDialog._performChanges();
|
|
expect(dialogClassShown).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
it("should not show a removal confirmation dialog if an extension was marked for removal and then unmarked", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$button.click();
|
|
var $undoLink = $("a.undo-remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$undoLink.click();
|
|
ExtensionManagerDialog._performChanges();
|
|
expect(dialogClassShown).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
it("should show a removal confirmation dialog if an extension was removed", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$button.click();
|
|
});
|
|
runs(function () {
|
|
// Don't expect the model to be disposed until after the dialog is dismissed.
|
|
ExtensionManagerDialog._performChanges();
|
|
expect(dialogClassShown).toBe("change-marked-extensions");
|
|
$mockDlg.triggerHandler("buttonClick", Dialogs.DIALOG_BTN_CANCEL);
|
|
});
|
|
});
|
|
|
|
it("should remove extensions and quit if the user hits Remove and Quit on the removal confirmation dialog", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$button.click();
|
|
});
|
|
runs(function () {
|
|
// Don't expect the model to be disposed until after the dialog is dismissed.
|
|
ExtensionManagerDialog._performChanges();
|
|
$mockDlg.triggerHandler("buttonClick", Dialogs.DIALOG_BTN_OK);
|
|
});
|
|
waitsFor(function () { return didReload; }, "mock reload");
|
|
runs(function () {
|
|
var mockPath = SpecRunnerUtils.getTestPath("/spec/ExtensionManager-test-files");
|
|
expect(removedPath).toBe(mockPath + "/user/mock-extension-3");
|
|
expect(didClose).toBe(true);
|
|
expect(didReload).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should not remove extensions or quit if the user hits Cancel on the removal confirmation dialog", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.remove[data-extension-id=mock-extension-3]", view.$el);
|
|
$button.click();
|
|
});
|
|
runs(function () {
|
|
// Don't expect the model to be disposed until after the dialog is dismissed.
|
|
ExtensionManagerDialog._performChanges();
|
|
$mockDlg.triggerHandler("buttonClick", Dialogs.DIALOG_BTN_CANCEL);
|
|
expect(removedPath).toBeFalsy();
|
|
expect(ExtensionManager.isMarkedForRemoval("mock-extension-3")).toBe(false);
|
|
expect(didClose).toBe(true);
|
|
expect(didReload).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should not show a disabling confirmation dialog if an extension was marked and then unmarked", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.disable[data-extension-id=mock-extension-3]", view.$el),
|
|
$undoLink;
|
|
$button.click();
|
|
$undoLink = $("a.undo-disable[data-extension-id=mock-extension-3]");
|
|
$undoLink.click();
|
|
ExtensionManagerDialog._performChanges();
|
|
expect(dialogClassShown).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
it("should show a disabling confirmation dialog if an extension was marked for disabling", function () {
|
|
mockLoadExtensions(["user/mock-extension-3"]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
var $button = $("button.disable[data-extension-id=mock-extension-3]", view.$el);
|
|
$button.click();
|
|
});
|
|
runs(function () {
|
|
ExtensionManagerDialog._performChanges();
|
|
expect(dialogClassShown).toBe("change-marked-extensions");
|
|
$mockDlg.triggerHandler("buttonClick", Dialogs.DIALOG_BTN_CANCEL);
|
|
});
|
|
});
|
|
|
|
it("should update extensions and quit if the user hits Update and Quit on the removal confirmation dialog", function () {
|
|
var id = "mock-extension-3",
|
|
filename = "/path/to/downloaded/mock-extension-3.zip";
|
|
mockLoadExtensions(["user/" + id]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
var installDeferred = $.Deferred();
|
|
spyOn(Package, "installUpdate").andReturn(installDeferred.promise());
|
|
runs(function () {
|
|
ExtensionManager.updateFromDownload({
|
|
installationStatus: Package.InstallationStatuses.NEEDS_UPDATE,
|
|
localPath: filename,
|
|
name: id
|
|
});
|
|
// Don't expect the model to be disposed until after the dialog is dismissed.
|
|
ExtensionManagerDialog._performChanges();
|
|
$mockDlg.triggerHandler("buttonClick", Dialogs.DIALOG_BTN_OK);
|
|
installDeferred.resolve({
|
|
installationStatus: "INSTALLED"
|
|
});
|
|
});
|
|
waitsFor(function () { return didReload; }, "mock reload");
|
|
runs(function () {
|
|
expect(Package.installUpdate).toHaveBeenCalledWith(filename, id);
|
|
expect(didClose).toBe(true);
|
|
expect(didReload).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should not update extensions or quit if the user hits Cancel on the confirmation dialog", function () {
|
|
var id = "mock-extension-3",
|
|
filename = "/path/to/downloaded/file.zip",
|
|
file = FileSystem.getFileForPath(filename);
|
|
mockLoadExtensions(["user/" + id]);
|
|
setupViewWithMockData(ExtensionManagerViewModel.InstalledViewModel);
|
|
runs(function () {
|
|
ExtensionManager.updateFromDownload({
|
|
name: id,
|
|
localPath: filename,
|
|
installationStatus: Package.InstallationStatuses.NEEDS_UPDATE
|
|
});
|
|
expect(ExtensionManager.isMarkedForUpdate(id)).toBe(true);
|
|
spyOn(file, "unlink");
|
|
// Don't expect the model to be disposed until after the dialog is dismissed.
|
|
ExtensionManagerDialog._performChanges();
|
|
$mockDlg.triggerHandler("buttonClick", Dialogs.DIALOG_BTN_CANCEL);
|
|
expect(removedPath).toBeFalsy();
|
|
expect(ExtensionManager.isMarkedForUpdate("mock-extension-3")).toBe(false);
|
|
expect(didClose).toBe(true);
|
|
expect(didReload).toBe(false);
|
|
expect(file.unlink).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
describe("initialization", function () {
|
|
|
|
var dialog, $dlg, originalRegistry;
|
|
|
|
// Sets up a view without actually loading any data--just for testing how we
|
|
// respond to the notifications.
|
|
beforeEach(function () {
|
|
runs(function () {
|
|
fakeLoadDeferred = new $.Deferred();
|
|
spyOn(ExtensionManager, "downloadRegistry").andCallFake(function () {
|
|
return fakeLoadDeferred.promise();
|
|
});
|
|
});
|
|
});
|
|
|
|
afterEach(function () {
|
|
runs(function () {
|
|
dialog.close();
|
|
waitsForDone(dialog.getPromise(), "ExtensionManagerDialog.close");
|
|
});
|
|
|
|
runs(function () {
|
|
brackets.config.extension_registry = originalRegistry;
|
|
dialog = null;
|
|
$dlg = null;
|
|
});
|
|
});
|
|
|
|
function openDialog() {
|
|
// this command is synchronous
|
|
CommandManager.execute(Commands.FILE_EXTENSION_MANAGER)
|
|
.done(function (dialogResult) {
|
|
dialog = dialogResult;
|
|
$dlg = dialog.getElement();
|
|
});
|
|
}
|
|
|
|
function setRegistryURL(url) {
|
|
originalRegistry = brackets.config.extension_registry;
|
|
brackets.config.extension_registry = url;
|
|
}
|
|
|
|
it("should show the spinner before the registry appears successfully and hide it after", function () {
|
|
runs(function () {
|
|
openDialog();
|
|
expect($(".spinner", $dlg).length).toBe(1);
|
|
fakeLoadDeferred.resolve();
|
|
expect($(".spinner", $dlg).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should show an error and remove the spinner if there is an error fetching the registry", function () {
|
|
runs(function () {
|
|
openDialog();
|
|
fakeLoadDeferred.reject();
|
|
expect($(".spinner", $dlg).length).toBe(0);
|
|
expect($("#registry .empty-message").text()).toBe(Strings.EXTENSION_MANAGER_ERROR_LOAD);
|
|
});
|
|
});
|
|
|
|
it("should hide the registry tab when no URL is specified", function () {
|
|
runs(function () {
|
|
setRegistryURL(null);
|
|
openDialog();
|
|
fakeLoadDeferred.resolve();
|
|
expect($(".registry", $dlg).length).toBe(0);
|
|
});
|
|
});
|
|
|
|
it("should show the registry tab when a URL is specified", function () {
|
|
runs(function () {
|
|
setRegistryURL("not null");
|
|
openDialog();
|
|
fakeLoadDeferred.resolve();
|
|
expect($(".registry", $dlg).length).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("ExtensionManagerView-i18n", function () {
|
|
|
|
beforeEach(function () {
|
|
setupExtensionManagerViewTests(this);
|
|
spyOn(brackets, "getLocale").andReturn("fr");
|
|
});
|
|
|
|
afterEach(function () {
|
|
cleanupExtensionManagerViewTests();
|
|
});
|
|
|
|
it("should display localized description", function () {
|
|
setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel);
|
|
runs(function () {
|
|
_.forEach(mockRegistry, function (item) {
|
|
if (item.metadata["package-i18n"] &&
|
|
item.metadata["package-i18n"].hasOwnProperty("fr") &&
|
|
item.metadata["package-i18n"].fr.hasOwnProperty("description")) {
|
|
expect(view).toHaveText(item.metadata["package-i18n"].fr.description);
|
|
expect(view).toHaveText(item.metadata["package-i18n"].fr.title);
|
|
expect(view).not.toHaveText(item.metadata["package-i18n"].fr.warnings);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|