/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/spec/PreferencesBase-test.js
1 609 строк
60 KB
Martin Zagora
eslint stylistic rules (#13177)
13 мар 2017, 22:27
13 мар 2017, 22:27
d00e464
Код
Авторство
О чём код?
/* * 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. * */ /*global describe, it, expect, beforeEach, afterEach, waitsFor, waitsForDone, runs, beforeFirst */ /*unittests: Preferences Base*/ define(function (require, exports, module) { 'use strict'; // Load dependent modules var PreferencesBase = require("preferences/PreferencesBase"), FileSystem = require("filesystem/FileSystem"), SpecRunnerUtils = require("spec/SpecRunnerUtils"), _ = require("thirdparty/lodash"); var testPath = SpecRunnerUtils.getTestPath("/spec/PreferencesBase-test-files"); describe("Preferences Base", function () { describe("Memory Storage", function () { it("should support get and save operations", function () { var sampleData = { foo: 1, bar: 2 }; var storage = new PreferencesBase.MemoryStorage(sampleData); // This storage is synchronous storage.load().then(function (data) { expect(data).toEqual(sampleData); }); storage.save({ foo: 3 }).then(function () { expect(storage.data).not.toEqual(sampleData); expect(storage.data.foo).toEqual(3); expect(storage.data.bar).not.toBeDefined(); }); }); }); describe("Path Layer", function () { it("handles a variety of glob patterns", function () { var data = { "**.html": { spaceUnits: 2 }, "lib/*.js": { spaceUnits: 3 }, "lib/**.css": { spaceUnits: 4 }, "*.{md,txt}": { spaceUnits: 5 } }; var layer = new PreferencesBase.PathLayer("/.brackets.json"); expect(layer.get(data, "spaceUnits", { path: "/public/index.html" })).toBe(2); expect(layer.get(data, "spaceUnits", { path: "/lib/script.js" })).toBe(3); expect(layer.get(data, "spaceUnits", { path: "/lib/foo/script.js" })).toBeUndefined(); expect(layer.get(data, "spaceUnits", { path: "/lib/foo/styles.css" })).toBe(4); expect(layer.get(data, "spaceUnits", { path: "/README.md" })).toBe(5); expect(layer.get(data, "spaceUnits", { path: "foo.js" })).toBeUndefined(); }); it("can retrieve the location of the pref value", function () { var data = { "**.html": { spaceUnits: 2 }, "lib/*.js": { soaceUnits: 3 } }; var layer = new PreferencesBase.PathLayer("/.brackets.json"); expect(layer.getPreferenceLocation(data, "spaceUnits", { path: "/foo.txt" })).toBeUndefined(); expect(layer.getPreferenceLocation(data, "spaceUnits", { path: "/index.html" })).toEqual("**.html"); expect(layer.getPreferenceLocation(data, "spaceUnits", { path: "/lib/brackets.js" })).toEqual("lib/*.js"); }); it("can set values in any of the patterns", function () { var data = { "**.html": { spaceUnits: 2 }, "lib/*.js": { spaceUnits: 3 } }; var originalData = _.clone(data, true); var layer = new PreferencesBase.PathLayer("/.brackets.json"); expect(layer.set(data, "spaceUnits", 10, { path: "/foo.txt" })).toBe(false); expect(data).toEqual(originalData); expect(layer.set(data, "spaceUnits", 11, { path: "/index.html" })).toBe(true); expect(data).toEqual({ "**.html": { spaceUnits: 11 }, "lib/*.js": { spaceUnits: 3 } }); expect(layer.set(data, "spaceUnits", 12, { path: "/index.html" }, "lib/*.js")).toBe(true); expect(data).toEqual({ "**.html": { spaceUnits: 11 }, "lib/*.js": { spaceUnits: 12 } }); expect(layer.set(data, "spaceUnits", 13, {}, "**.md")).toBe(true); }); it("should not set the same value twice", function () { var data = { "**.html": { spaceUnits: 2 }, "lib/*.js": { spaceUnits: 3 } }; var layer = new PreferencesBase.PathLayer("/.brackets.json"); expect(layer.set(data, "spaceUnits", 11, { path: "/index.html" })).toBe(true); // Try to set the same value again. expect(layer.set(data, "spaceUnits", 11, { path: "/index.html" })).toBe(false); expect(data).toEqual({ "**.html": { spaceUnits: 11 }, "lib/*.js": { spaceUnits: 3 } }); }); }); describe("Language Layer", function () { it("returns the setting for the corresponding language", function () { var data = { "html": { spaceUnits: 2 }, "json": { spaceUnits: 3 }, "css": { spaceUnits: 4 }, "javascript": { spaceUnits: 5 } }; var layer = new PreferencesBase.LanguageLayer(); expect(layer.get(data, "spaceUnits", {language: "html"})).toBe(2); expect(layer.get(data, "spaceUnits", {language: "json"})).toBe(3); expect(layer.get(data, "spaceUnits", {language: "javascript"})).toBe(5); expect(layer.get(data, "spaceUnits", {language: "cobol"})).toBeUndefined(); expect(layer.get(data, "spaceUnits", {language: "css"})).toBe(4); }); it("can retrieve the location of the pref value", function () { var data = { "html": { spaceUnits: 2 }, "javascript": { spaceUnits: 3 } }; var layer = new PreferencesBase.LanguageLayer(); expect(layer.getPreferenceLocation(data, "spaceUnits", {language: "text"})).toBeUndefined(); expect(layer.getPreferenceLocation(data, "spaceUnits", {language: "html"})).toEqual("html"); expect(layer.getPreferenceLocation(data, "spaceUnits", {language: "javascript"})).toEqual("javascript"); }); it("can set values in any of the patterns", function () { var data = { "html": { spaceUnits: 2, niceBooleanOption: true }, "css": { spaceUnits: 3 } }; var originalData = _.clone(data, true); var layer = new PreferencesBase.LanguageLayer(); expect(layer.set(data, "spaceUnits", 10, {}, "javascript")).toBe(true); expect(data).toEqual({ "html": { spaceUnits: 2, niceBooleanOption: true }, "css": { spaceUnits: 3 }, "javascript": { spaceUnits: 10 } }); expect(layer.set(data, "spaceUnits", undefined, {}, "javascript")).toBe(true); expect(data).toEqual(originalData); expect(layer.set(data, "spaceUnits", 11, {}, "html")).toBe(true); expect(data).toEqual({ "html": { spaceUnits: 11, niceBooleanOption: true }, "css": { spaceUnits: 3 } }); expect(layer.set(data, "spaceUnits", 12, {language: "css"})).toBe(true); expect(data).toEqual({ "html": { spaceUnits: 11, niceBooleanOption: true }, "css": { spaceUnits: 12 } }); expect(layer.set(data, "niceBooleanOption", false, {language: "html"})).toBe(true); expect(data).toEqual({ "html": { spaceUnits: 11, niceBooleanOption: false }, "css": { spaceUnits: 12 } }); expect(layer.set(data, "niceBooleanOption", undefined, {language: "html"})).toBe(true); expect(layer.set(data, "niceBooleanOption", false, {language: "css"}, "css")).toBe(true); expect(layer.set(data, "niceBooleanOption", undefined, {language: "javascript"}, "javascript")).toBe(false); expect(layer.set(data, "niceBooleanOption", true, {language: "php"}, "php")).toBe(true); expect(data).toEqual({ "html": { spaceUnits: 11 }, "css": { spaceUnits: 12, niceBooleanOption: false }, "javascript": { }, "php": { niceBooleanOption: true } }); }); it("should not set the same value twice", function () { var data = { "html": { spaceUnits: 2 }, "javascript": { spaceUnits: 4 } }; var layer = new PreferencesBase.LanguageLayer(); expect(layer.set(data, "spaceUnits", 11, {}, "javascript")).toBe(true); // Try to set the same value again. expect(layer.set(data, "spaceUnits", 11, {}, "javascript")).toBe(false); // And again with the language set expect(layer.set(data, "spaceUnits", 11, {language: "javascript"})).toBe(false); expect(data).toEqual({ "html": { spaceUnits: 2 }, "javascript": { spaceUnits: 11 } }); }); }); describe("Scope", function () { it("should look up a value", function () { var data = { spaceUnits: 4, useTabChar: false }; var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); // MemoryStorage operates synchronously scope.load(); expect(scope.get("spaceUnits")).toBe(4); expect(scope.getKeys().sort()).toEqual(["spaceUnits", "useTabChar"].sort()); }); it("should not set the same value twice", function () { var data = { spaceUnits: 4, useTabChar: false }; var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); // MemoryStorage operates synchronously scope.load(); expect(scope.get("spaceUnits")).toBe(4); expect(scope.set("spaceUnits", 12)).toBe(true); expect(scope._dirty).toBe(true); // Explicitly save it in order to clear dirty flag. scope.save(); expect(scope._dirty).toBe(false); // Try to set the same value again and verify that the dirty flag is not set. expect(scope.set("spaceUnits", 12)).toBe(false); expect(scope.get("spaceUnits")).toBe(12); expect(scope._dirty).toBe(false); }); it("should correctly handle changes on objects", function () { var foo = { value: 42 }; var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()), pm = new PreferencesBase.PreferencesSystem(); // Explicitly create a PreferencesSystem and add the scope object to it // so that we can call set/get functions on the PreferencesSystem instead // of calling directly on scope object. Note that calling 'get' function on // scope object will not clone a preference object as it does in 'get' function // on PreferencesSystem object. pm.addScope("test", scope); // MemoryStorage operates synchronously scope.load(); expect(pm.set("foo", foo).stored).toBe(true); expect(pm.get("foo").value).toBe(42); expect(scope._dirty).toBe(false); // Explicitly save it in order to clear dirty flag. pm.save(); expect(scope._dirty).toBe(false); foo.value = "!!!"; expect(foo.value).toBe("!!!"); expect(pm.set("foo", foo, undefined, true).stored).toBe(true); expect(scope._dirty).toBe(true); pm.save(); expect(scope._dirty).toBe(false); var fooCopyFromPref = pm.get("foo"); expect(fooCopyFromPref.value).toBe("!!!"); // Add 'bar' to our local copy and then // verify that our change is not in the pref. fooCopyFromPref.bar = "'bar' should not be in pref"; expect(pm.get("foo").bar).toBe(undefined); }); it("should remove the preference when setting it with 'undefined' value", function () { var data = { spaceUnits: 0, useTabChar: false }; var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); // MemoryStorage operates synchronously scope.load(); expect(scope.get("spaceUnits")).toBe(0); // Remove 'spaceUnits' by calling set with 'undefined' second argument expect(scope.set("spaceUnits")).toBe(true); expect(scope._dirty).toBe(true); expect(scope.getKeys()).toEqual(["useTabChar"]); expect(scope.get("useTabChar")).toBe(false); // Remove 'useTabChar' by calling set with 'undefined' second argument expect(scope.set("useTabChar")).toBe(true); expect(scope._dirty).toBe(true); expect(scope.getKeys()).toEqual([]); }); it("should look up a value with a path layer", function () { var data = { spaceUnits: 4, path: { "src/*js": { spaceUnits: 2 } } }; var layer = new PreferencesBase.PathLayer("/"); var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); scope.load(); scope.addLayer(layer); expect(scope.get("path")).toBeUndefined(); expect(scope.get("spaceUnits")).toBe(4); expect(scope.get("spaceUnits", { path: "/src/foo.js" })).toBe(2); expect(scope.get("spaceUnits", { path: "/top.js" })).toBe(4); }); it("should look up a value using language layer", function () { var data = { spaceUnits: 4, language: { html: { spaceUnits: 2 }, javascript: { someBooleanOption: false } } }; var layer = new PreferencesBase.LanguageLayer(); var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); scope.load(); expect(scope.get("language")).toBeDefined(); scope.addLayer(layer); expect(scope.get("language")).toBeUndefined(); expect(scope.get("spaceUnits")).toBe(4); expect(scope.get("spaceUnits", {language: "html"})).toBe(2); expect(scope.get("someBooleanOption", {language: "javascript"})).toBe(false); }); it("can look up the location of a preference", function () { var data = { spaceUnits: 4, path: { "src/*js": { spaceUnits: 2 } }, language: { "cobol": { spaceUnits: 5 } } }; var pathLayer = new PreferencesBase.PathLayer("/.brackets.json"); var languageLayer = new PreferencesBase.LanguageLayer(); var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); scope.load(); scope.addLayer(pathLayer); scope.addLayer(languageLayer); expect(scope.getPreferenceLocation("unknown")).toBeUndefined(); expect(scope.getPreferenceLocation("path")).toBeUndefined(); expect(scope.getPreferenceLocation("language")).toBeUndefined(); expect(scope.getPreferenceLocation("spaceUnits")).toEqual({}); expect(scope.getPreferenceLocation("spaceUnits", { path: "/src/brackets.js" })).toEqual({ layer: "path", layerID: "src/*js" }); expect(scope.getPreferenceLocation("spaceUnits", { path: "/index.md" })).toEqual({}); expect(scope.getPreferenceLocation("spaceUnits", {language: "cobol"})).toEqual({ layer: "language", layerID: "cobol" }); }); it("can set a preference at any layer", function () { var data = { spaceUnits: 4, path: { "src/*js": { spaceUnits: 2 }, "*.html": { spaceUnits: 1 } }, language: { html: { spaceUnits: 11, niceBooleanOption: false }, css: { spaceUnits: 13 } } }; var pathLayer = new PreferencesBase.PathLayer("/.brackets.json"); var languageLayer = new PreferencesBase.LanguageLayer(); var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); scope.load(); scope.addLayer(pathLayer); scope.addLayer(languageLayer); expect(scope.set("spaceUnits", 5)).toBe(true); expect(data.spaceUnits).toBe(5); expect(scope._dirty).toBe(true); scope._dirty = false; expect(scope.set("spaceUnits", 6, { path: "/src/brackets.js" })).toBe(true); expect(data.spaceUnits).toBe(5); expect(data.path["src/*js"].spaceUnits).toBe(6); expect(scope._dirty).toBe(true); scope._dirty = false; expect(scope.set("spaceUnits", 7, { path: "/foo.md" }, { layer: "path" })).toBe(false); expect(data.spaceUnits).toBe(5); expect(data.path["src/*js"].spaceUnits).toBe(6); expect(scope._dirty).toBe(false); expect(scope.set("spaceUnits", 8, { }, { layer: "path", layerID: "**.md" })).toBe(true); expect(data.spaceUnits).toBe(5); expect(data.path["src/*js"].spaceUnits).toBe(6); expect(data.path["**.md"].spaceUnits).toBe(8); expect(scope._dirty).toBe(true); scope._dirty = false; expect(scope.set("spaceUnits", 9, { path: "index.html" }, { })).toBe(true); expect(data.spaceUnits).toBe(9); expect(data.path["*.html"].spaceUnits).toBe(1); expect(scope._dirty).toBe(true); expect(scope.set("spaceUnits", 12, {}, { layer: "language", layerID: "html" })).toBe(true); expect(data.spaceUnits).toBe(9); expect(data.path["*.html"].spaceUnits).toBe(1); expect(data.language.html.spaceUnits).toBe(12); expect(scope._dirty).toBe(true); expect(scope.set("spaceUnits", undefined, {}, { layer: "language", layerID: "css" })).toBe(true); expect(data.spaceUnits).toBe(9); expect(data.path["*.html"].spaceUnits).toBe(1); expect(data.language.html.spaceUnits).toBe(12); expect(data.language.css).toBeUndefined(); expect(scope._dirty).toBe(true); expect(scope.set("spaceUnits", undefined, {}, {})).toBe(true); expect(data.spaceUnits).toBeUndefined(); expect(Object.keys(data)).toEqual(["path", "language"]); expect(scope.set("spaceUnits", undefined, {}, { layer: "path", layerID: "*.html" })).toBe(true); expect(Object.keys(data.path["*.html"])).toEqual([]); expect(scope.get("niceBooleanOption", {language: "html"})).toBe(false); }); it("can return its keys", function () { var data = { spaceUnits: 4, useEmojiForTabs: true, path: { "**.js": { spaceUnits: 3, showNonWhitespace: false }, "README.md": { markdown: false, useEmojiForTabs: false } }, language: { "html": { niceHTMLOption: true } } }; var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); scope.addLayer(new PreferencesBase.PathLayer("/")); scope.addLayer(new PreferencesBase.LanguageLayer()); scope.load(); var keys = scope.getKeys(); var expected = ["spaceUnits", "useEmojiForTabs", "showNonWhitespace", "markdown", "niceHTMLOption"]; expect(keys.sort()).toEqual(expected.sort()); keys = scope.getKeys({ path: "/coffeescript.ts" }); expected = ["spaceUnits", "useEmojiForTabs"]; expect(keys.sort()).toEqual(expected.sort()); keys = scope.getKeys({ path: "/README.md" }); expected = ["spaceUnits", "useEmojiForTabs", "markdown"]; expect(keys.sort()).toEqual(expected.sort()); keys = scope.getKeys({ path: "/test.js", language: "html" }); expected = ["spaceUnits", "useEmojiForTabs", "showNonWhitespace", "niceHTMLOption"]; expect(keys.sort()).toEqual(expected.sort()); }); it("notifies of changes", function () { var data1 = { spaceUnits: 4, cursorSize: 27, path: { "**.js": { statusBarElephants: false } } }; var storage = new PreferencesBase.MemoryStorage(data1); var scope = new PreferencesBase.Scope(storage); scope.addLayer(new PreferencesBase.PathLayer("/")); scope.addLayer(new PreferencesBase.LanguageLayer()); scope.load(); var data2 = { spaceUnits: 4, path: { "**.js": { trafficLight: "green" }, "**.md": { statusBarElephants: true } }, language: { javascript: { niceBooleanOption: true } } }; storage.data = data2; var events1 = []; scope.on("change", function (e, data) { events1.push(data); }); scope.load(); expect(events1.length).toBe(1); expect(events1[0].ids.sort()).toEqual( ["spaceUnits", "niceBooleanOption", "cursorSize", "statusBarElephants", "trafficLight"].sort() ); var data3 = { spaceUnits: 4, path: { "**.js": { trafficLight: "green" }, "**.md": { statusBarElephants: true } }, language: { html: { spaceshipColor: "silver" } } }; storage.data = data3; var events2 = []; scope.on("change", function (e, data) { events2.push(data); }); scope.load(); expect(events2.length).toBe(1); expect(events2[0].ids.sort()).toEqual(["niceBooleanOption", "spaceUnits", "statusBarElephants", "trafficLight", "spaceshipColor"].sort()); }); }); describe("PreferencesSystem", function () { it("should yield an error if a preference is redefined", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.definePreference("foo.bar", "string"); try { pm.definePreference("foo.bar", "string"); expect("We should have gotten an exception").toEqual("but we didn't"); } catch (e) { } }); it("will automatically wrap a Storage with a Scope", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.addScope("test", new PreferencesBase.MemoryStorage()); pm.set("testval", 27); expect(pm.get("testval")).toBe(27); }); it("should find the default values", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.definePreference("foo.bar", "number", 0); expect(pm.get("nonexistent")).toBeUndefined(); expect(pm.get("foo.bar")).toBe(0); }); it("should produce an error for setValue on undefined scope", function () { var pm = new PreferencesBase.PreferencesSystem(); expect(pm.set("foo", false, { location: { scope: "nonscope" } }).stored).toBe(false); }); it("supports nested scopes", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.definePreference("useTabChar", "boolean", false); pm.definePreference("tabSize", "number", 4); pm.definePreference("spaceUnits", "number", 4); var userScope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()); var projectScope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()); pm.addScope("user", userScope); pm.addScope("project", projectScope); expect(pm.get("spaceUnits")).toEqual(4); var userLocation = { location: { scope: "user" } }; pm.set("useTabChar", true, userLocation); pm.set("tabSize", 8, userLocation); pm.set("spaceUnits", 8, userLocation); pm.set("spaceUnits", 2, { location: { scope: "project" } }); expect(pm.get("spaceUnits")).toBe(2); expect(pm.get("useTabChar")).toBe(true); expect(pm.get("tabSize")).toBe(8); }); it("should extend Preference Objects from base", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.definePreference("closeTags", "object", { "dontCloseTags": [], "indentTags": [], "whenClosing": true, "whenOpening": true }); var userScope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()); pm.addScope("user", userScope); var userLocation = { location: { scope: "user" } }; pm.set("closeTags", { "whenOpening": false }, userLocation); expect(pm.get("closeTags")).toEqual({ "dontCloseTags": [], "indentTags": [], "whenClosing": true, "whenOpening": false }); }); it("handles asynchronously loaded scopes", function () { var storage1 = new PreferencesBase.MemoryStorage({ testKey: 1 }); var deferred1 = $.Deferred(); storage1.load = function () { return deferred1; }; var storage2 = new PreferencesBase.MemoryStorage({ testKey: 2 }); var deferred2 = $.Deferred(); storage2.load = function () { return deferred2; }; var pm = new PreferencesBase.PreferencesSystem(); pm.definePreference("testKey", "number", 0); pm.addScope("storage1", new PreferencesBase.Scope(storage1), { before: "storage2" }); pm.addScope("storage2", new PreferencesBase.Scope(storage2), { before: "default" }); expect(pm.get("testKey")).toBe(0); deferred1.resolve(storage1.data); expect(pm.get("testKey")).toBe(0); deferred2.resolve(storage2.data); expect(pm.get("testKey")).toBe(1); }); it("can notify of preference changes through set", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.definePreference("spaceUnits", "number", 4); pm.addScope("user", new PreferencesBase.MemoryStorage()); var eventData; pm.on("change", function (e, data) { eventData = data; }); pm.set("testing", true); expect(eventData).toEqual({ ids: ["testing"] }); eventData = undefined; pm.set("spaceUnits", 4); expect(eventData).toEqual({ ids: ["spaceUnits"] }); }); it("can notify of preference changes via scope changes and scope changes", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.definePreference("spaceUnits", "number", 4); var eventData = [], scopeEvents = []; pm.on("change", function (e, data) { eventData.push(data); }); pm.on("scopeOrderChange", function (e, data) { scopeEvents.push(data); }); pm.addScope("user", new PreferencesBase.MemoryStorage({ spaceUnits: 4, elephants: "charging" })); expect(pm._defaults.scopeOrder).toEqual(["user", "default"]); expect(eventData).toEqual([{ ids: ["spaceUnits", "elephants"] }]); expect(scopeEvents).toEqual([{ id: "user", action: "added" }]); scopeEvents = []; eventData = []; pm.removeScope("user"); expect(pm._defaults.scopeOrder).toEqual(["default"]); expect(eventData).toEqual([{ ids: ["spaceUnits", "elephants"] }]); expect(scopeEvents).toEqual([{ id: "user", action: "removed" }]); }); it("notifies when there are layer changes", function () { var pm = new PreferencesBase.PreferencesSystem(); var data = { spaceUnits: 4, useTabChar: false, path: { "*.txt": { spaceUnits: 2, alpha: "bravo" } } }; var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data)); pm.addScope("user", scope); var eventData = []; pm.on("change", function (e, data) { eventData.push(data); }); scope.addLayer(new PreferencesBase.PathLayer("/")); expect(eventData).toEqual([{ ids: ["spaceUnits", "alpha"] }]); // Extra verification that layer keys works correctly var keys = scope._layers[0].getKeys(scope.data.path, { path: "/bar.md" }); expect(keys).toEqual([]); keys = scope._layers[0].getKeys(scope.data.path, { path: "/foo.txt" }); expect(keys.sort()).toEqual(["spaceUnits", "alpha"].sort()); expect(pm.get("spaceUnits")).toBe(4); expect(pm.get("spaceUnits", {path: "/foo.txt"})).toBe(2); eventData = []; pm.signalContextChanged({path: "/README.md"}, {path: "/README.txt"}); expect(eventData).toEqual([{ ids: ["spaceUnits", "alpha"] }]); eventData = []; pm.signalContextChanged({path: "/foo.txt"}, {path: "/README.txt"}); expect(eventData).toEqual([]); // Test to make sure there are no exceptions when there is no path data delete data.path; scope.load(); expect(scope.data).toEqual(data); }); it("can notify changes for single preference objects", function () { var pm = new PreferencesBase.PreferencesSystem(); var pref = pm.definePreference("spaceUnits", "number", 4); var retrievedPref = pm.getPreference("spaceUnits"); expect(retrievedPref).toBe(pref); var changes = 0; pref.on("change", function (e) { changes++; }); var newScope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage({ spaceUnits: 2 })); pm.addScope("new", newScope); expect(changes).toEqual(1); pref.off("change"); pm.set("spaceUnits", 10, { location: { scope: "new" } }); expect(changes).toEqual(1); changes = 0; pm.on("change", "spaceUnits", function () { changes++; }); pm.set("spaceUnits", 11, { location: { scope: "new" } }); expect(changes).toEqual(1); }); it("can pause and resume broadcast of events", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.addScope("user", new PreferencesBase.MemoryStorage()); pm.pauseChangeEvents(); var spaceUnitChanges = 0, fooChanges = 0, globalChangeMessages = []; pm.definePreference("spaceUnits", "number", 4).on("change", function () { spaceUnitChanges++; }); pm.definePreference("foo", "string", "bar").on("change", function () { fooChanges++; }); pm.on("change", function (e, data) { globalChangeMessages.push(data); }); pm.set("spaceUnits", 8); pm.set("foo", "baz"); expect(spaceUnitChanges).toBe(0); expect(fooChanges).toBe(0); expect(globalChangeMessages).toEqual([]); pm.resumeChangeEvents(); expect(spaceUnitChanges).toBe(1); expect(fooChanges).toBe(1); expect(globalChangeMessages).toEqual([{ ids: ["spaceUnits", "foo"] }]); pm.set("foo", "zippy"); expect(fooChanges).toBe(2); }); it("can dynamically modify the default scope order", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.addScope("user", new PreferencesBase.MemoryStorage({ spaceUnits: 1 })); pm.addScope("project", new PreferencesBase.MemoryStorage({ spaceUnits: 2 })); pm.addScope("session", new PreferencesBase.MemoryStorage()); expect(pm.get("spaceUnits")).toBe(2); expect(pm._defaults.scopeOrder).toEqual(["session", "project", "user", "default"]); var eventData = []; pm.on("change", function (e, data) { eventData.push(data); }); pm.removeFromScopeOrder("project"); expect(pm._defaults.scopeOrder).toEqual(["session", "user", "default"]); expect(eventData).toEqual([{ ids: ["spaceUnits"] }]); expect(pm.get("spaceUnits")).toBe(1); expect(pm.get("spaceUnits", { scopeOrder: ["session", "project", "user", "default"] })).toBe(2); eventData = []; pm.addToScopeOrder("project", "user"); expect(pm._defaults.scopeOrder).toEqual(["session", "project", "user", "default"]); expect(eventData).toEqual([{ ids: ["spaceUnits"] }]); expect(pm.get("spaceUnits")).toBe(2); }); it("can set preference values at any level", function () { var pm = new PreferencesBase.PreferencesSystem(), pref = pm.definePreference("spaceUnits", "number", 4), user = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()), project = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage({ path: { "**.html": { spaceUnits: 2 } } })), session = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()); project.addLayer(new PreferencesBase.PathLayer("/")); pm.addScope("user", user); pm.addScope("project", project); pm.addScope("session", session); var changes = 0; pref.on("change", function (e) { changes++; }); expect(pm.set("spaceUnits", 5, { location: { scope: "doesNotExist" } }).stored).toBe(false); expect(pm.get("spaceUnits")).toBe(4); expect(changes).toBe(0); expect(pm.getPreferenceLocation("spaceUnits")).toEqual({ scope: "default" }); expect(pm.set("spaceUnits", 6).stored).toBe(true); expect(user.data).toEqual({ spaceUnits: 6 }); expect(changes).toBe(1); expect(pm.set("spaceUnits", 7).stored).toBe(true); expect(user.data).toEqual({ spaceUnits: 7 }); expect(changes).toBe(2); expect(pm.set("spaceUnits", 8, { location: { scope: "session" } }).stored).toBe(true); expect(user.data).toEqual({ spaceUnits: 7 }); expect(session.data).toEqual({ spaceUnits: 8 }); expect(changes).toBe(3); expect(pm.set("spaceUnits", 9).stored).toBe(true); expect(changes).toBe(4); expect(session.data).toEqual({ spaceUnits: 9 }); expect(pm.set("spaceUnits", undefined, { location: { scope: "session" } }).stored).toBe(true); expect(changes).toBe(5); expect(session.data.spaceUnits).toBeUndefined(); expect(pm.get("spaceUnits")).toBe(7); expect(Object.keys(session.data)).toEqual([]); pm.signalContextChanged({}, {path: "/index.html"}); expect(changes).toBe(6); expect(pm.get("spaceUnits", {path: "/index.html"})).toBe(2); expect(pm.set("spaceUnits", 10, {context: {path: "/index.html"}}).stored).toBe(true); expect(changes).toBe(7); expect(project.data.path["**.html"].spaceUnits).toBe(10); pm.signalContextChanged({path: "/index.html"}, {path: "/foo.txt"}); expect(pm.getPreferenceLocation("spaceUnits")).toEqual({ scope: "user" }); expect(pm.set("spaceUnits", 11, { location: { scope: "project" } }).stored).toBe(true); expect(pm.getPreferenceLocation("spaceUnits")).toEqual({ scope: "project" }); expect(pm.set("spaceUnits", 12).stored).toBe(true); expect(project.data.spaceUnits).toBe(12); expect(pm.set("spaceUnits", 13, { location: { scope: "project", layer: "path", layerID: "**.js" } }).stored).toBe(true); expect(pm.getPreferenceLocation("spaceUnits")).toEqual({ scope: "project" }); expect(pm.getPreferenceLocation("spaceUnits", { path: "/Gruntfile.js" })).toEqual({ scope: "project", layer: "path", layerID: "**.js" }); expect(pm.get("spaceUnits", { path: "/Gruntfile.js" })).toBe(13); }); it("supports removal of scopes", function () { var pm = new PreferencesBase.PreferencesSystem(); var events = []; pm.on("change", function (e, data) { events.push(data); }); pm.addScope("first", new PreferencesBase.MemoryStorage({ spaceUnits: 1 })); pm.addScope("second", new PreferencesBase.MemoryStorage({ spaceUnits: 2 })); events = []; expect(pm.get("spaceUnits")).toBe(2); pm.removeScope("second"); expect(pm.get("spaceUnits")).toBe(1); expect(events).toEqual([ { ids: ["spaceUnits"] } ]); }); it("can provide an automatically prefixed version of itself", function () { var pm = new PreferencesBase.PreferencesSystem(); var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()); pm.addScope("user", scope); pm.set("spaceUnits", 10); pm.set("linting.enabled", true); var prefixedPM = pm.getPrefixedSystem("linting"); prefixedPM.definePreference("collapsed", "boolean", true); var events = []; prefixedPM.on("change", function (e, data) { events.push(data); }); expect(prefixedPM.get("spaceUnits")).toBeUndefined(); expect(prefixedPM.getPreferenceLocation("spaceUnits")).toBeUndefined(); expect(prefixedPM.get("enabled")).toBe(true); expect(prefixedPM.getPreferenceLocation("enabled")).toEqual({ scope: "user" }); // set the value with doNotSave set. Will check to verify that save did not occur. prefixedPM.set("collapsed", false, undefined, true); expect(events).toEqual([{ ids: ["collapsed"] }]); expect(prefixedPM.get("collapsed")).toBe(false); // verify that the scope was not saved automatically expect(scope._dirty).toBe(true); expect(pm.get("collapsed")).toBeUndefined(); expect(pm.get("linting.collapsed")).toBe(false); var pref = prefixedPM.getPreference("collapsed"); expect(pref).toBeDefined(); expect(pm.getPreference("linting.collapsed")).toBeDefined(); var prefEvents = 0; pref.on("change", function (e, data) { prefEvents += 1; }); events = []; prefixedPM.set("collapsed", true); expect(prefEvents).toBe(1); expect(events.length).toBe(1); var saveDone = false; prefixedPM.save().done(function () { saveDone = true; }); expect(saveDone).toBe(true); }); it("should support validator to ignore invalid values", function () { var pm = new PreferencesBase.PreferencesSystem(); pm.addScope("user", new PreferencesBase.MemoryStorage()); pm.definePreference("spaceUnits", "number", 4, { validator: function (value) { return (value >= 0 && value <= 10); } }); expect(pm.set("spaceUnits", 12).valid).toBe(false); // fail: out-of-range upper expect(pm.get("spaceUnits")).toBe(4); // expect default expect(pm.set("spaceUnits", -1).valid).toBe(false); // fail: out-of-range lower expect(pm.get("spaceUnits")).toBe(4); // expect default }); it("should handle context normalization", function () { var normalizer = function (context) { if (typeof context === "string") { return { scopeOrder: [context] }; } return context; }; var pm = new PreferencesBase.PreferencesSystem(normalizer); pm.addScope("user", new PreferencesBase.MemoryStorage({ value: 1 })); pm.addScope("session", new PreferencesBase.MemoryStorage({ value: 2 })); expect(pm.get("value")).toBe(2); // Test passing in a string for the get context expect(pm.get("value", "user")).toBe(1); // This will set in the scope in which the value was set. Without a context, // that means "session". pm.set("value", 3); expect(pm.get("value")).toBe(3); expect(pm.get("value", "user")).toBe(1); // Now, set with a context. This should cause the value to be set in "user" // scope. pm.set("value", 4, { context: "user" }); expect(pm.get("value")).toBe(3); expect(pm.get("value", "user")).toBe(4); expect(pm.getPreferenceLocation("value").scope).toBe("session"); expect(pm.getPreferenceLocation("value", "user").scope).toBe("user"); }); }); describe("File Storage", function () { var settingsFile = FileSystem.getFileForPath(testPath + "/.brackets.json"), newSettingsFile = FileSystem.getFileForPath(testPath + "/new.prefs"), emptySettingsFile = FileSystem.getFileForPath(testPath + "/empty.json"), filestorage, originalText; beforeFirst(function () { var deferred = $.Deferred(); settingsFile.read({}, function (err, text) { if (err) { deferred.reject(err); return; } originalText = text; deferred.resolve(); }); waitsForDone(deferred.promise()); }); beforeEach(function () { filestorage = new PreferencesBase.FileStorage(settingsFile.fullPath); }); afterEach(function () { var deferred = $.Deferred(); settingsFile.write(originalText, {}, function (err) { if (err) { deferred.reject(err); } else { deferred.resolve(); } }); waitsForDone(deferred.promise()); var deleted = false; runs(function () { newSettingsFile.unlink(function () { deleted = true; }); }); waitsFor(function () { return deleted; }); }); it("can load preferences from disk", function () { var filestorage = new PreferencesBase.FileStorage(settingsFile.fullPath); var pm = new PreferencesBase.PreferencesSystem(); var projectScope = new PreferencesBase.Scope(filestorage); waitsForDone(pm.addScope("project", projectScope)); runs(function () { projectScope.addLayer(new PreferencesBase.PathLayer("/")); expect(pm.get("spaceUnits")).toBe(9); expect(pm.get("spaceUnits", { scopeOrder: ["project"], path: "/foo.go" })).toBe(7); }); }); it("can validate preferences loaded from disk", function () { var filestorage = new PreferencesBase.FileStorage(settingsFile.fullPath); var pm = new PreferencesBase.PreferencesSystem(); var projectScope = new PreferencesBase.Scope(filestorage); waitsForDone(pm.addScope("project", projectScope)); runs(function () { projectScope.addLayer(new PreferencesBase.PathLayer("/")); pm.definePreference("spaceUnits", "number", 3, { validator: function (value) { return (value >= 0 && value <= 8); } }); // Value on disk (9) is out-of-range, so expect default (3) expect(pm.get("spaceUnits")).toBe(3); }); }); it("can save preferences", function () { var filestorage = new PreferencesBase.FileStorage(settingsFile.fullPath); var pm = new PreferencesBase.PreferencesSystem(); var projectScope = new PreferencesBase.Scope(filestorage); waitsForDone(pm.addScope("project", projectScope)); runs(function () { var memstorage = new PreferencesBase.MemoryStorage(); pm.addScope("session", new PreferencesBase.Scope(memstorage)); pm.set("unicorn-filled", true, { location: { scope: "session" } }); pm.set("unicorn-filled", false, { location: { scope: "project" } }); waitsForDone(pm.save()); runs(function () { expect(memstorage.data["unicorn-filled"]).toBe(true); }); }); }); it("can create a new pref file", function () { var filestorage = new PreferencesBase.FileStorage(newSettingsFile.fullPath, true); var pm = new PreferencesBase.PreferencesSystem(); var newScope = new PreferencesBase.Scope(filestorage); waitsForDone(pm.addScope("new", newScope), "adding scope"); runs(function () { pm.set("unicorn-filled", true, { location: { scope: "new" } }); expect(pm.get("unicorn-filled")).toBe(true); waitsForDone(pm.save(), "saving prefs"); var deferred = $.Deferred(); runs(function () { newSettingsFile.exists(function (err, exists) { if (err || !exists) { deferred.reject(err); } else { deferred.resolve(); } }); }); waitsForDone(deferred.promise(), "checking file"); }); }); it("can load preferences later", function () { var filestorage = new PreferencesBase.FileStorage(); var pm = new PreferencesBase.PreferencesSystem(); var newScope = new PreferencesBase.Scope(filestorage); newScope.addLayer(new PreferencesBase.PathLayer("/")); var changes = []; waitsForDone(pm.addScope("new", newScope), "adding scope"); pm.on("change", function (change, data) { changes.push(data); }); runs(function () { expect(pm.get("spaceUnits")).toBeUndefined(); filestorage.setPath(settingsFile.fullPath); }); waitsFor(function () { return changes.length > 0; }); runs(function () { expect(pm.get("spaceUnits")).toBe(9); expect(changes).toEqual([{ ids: ["spaceUnits"] }]); }); }); it("is fine with empty preferences files", function () { var filestorage = new PreferencesBase.FileStorage(emptySettingsFile.fullPath), promise = filestorage.load(); waitsForDone(promise, "loading empty JSON file"); runs(function () { promise.then(function (data) { expect(data).toEqual({}); }); }); }); }); }); });