PageRenderTime 102ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/test/spec/PreferencesBase-test.js

https://gitlab.com/MichelZuniga/brackets
JavaScript | 1252 lines | 1000 code | 204 blank | 48 comment | 0 complexity | 7695ac09f29ebd4895b70e1afdd724a4 MD5 | raw file
  1. /*
  2. * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. *
  22. */
  23. /*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
  24. /*global $, define, describe, it, expect, beforeEach, afterEach, waitsFor, waitsForDone, runs, beforeFirst */
  25. /*unittests: Preferences Base*/
  26. define(function (require, exports, module) {
  27. 'use strict';
  28. // Load dependent modules
  29. var PreferencesBase = require("preferences/PreferencesBase"),
  30. FileSystem = require("filesystem/FileSystem"),
  31. SpecRunnerUtils = require("spec/SpecRunnerUtils"),
  32. _ = require("thirdparty/lodash");
  33. var testPath = SpecRunnerUtils.getTestPath("/spec/PreferencesBase-test-files");
  34. describe("Preferences Base", function () {
  35. describe("Memory Storage", function () {
  36. it("should support get and save operations", function () {
  37. var sampleData = {
  38. foo: 1,
  39. bar: 2
  40. };
  41. var storage = new PreferencesBase.MemoryStorage(sampleData);
  42. // This storage is synchronous
  43. storage.load().then(function (data) {
  44. expect(data).toEqual(sampleData);
  45. });
  46. storage.save({
  47. foo: 3
  48. }).then(function () {
  49. expect(storage.data).not.toEqual(sampleData);
  50. expect(storage.data.foo).toEqual(3);
  51. expect(storage.data.bar).not.toBeDefined();
  52. });
  53. });
  54. });
  55. describe("Path Layer", function () {
  56. it("handles a variety of glob patterns", function () {
  57. var data = {
  58. "**.html": {
  59. spaceUnits: 2
  60. },
  61. "lib/*.js": {
  62. spaceUnits: 3
  63. },
  64. "lib/**.css": {
  65. spaceUnits: 4
  66. },
  67. "*.{md,txt}": {
  68. spaceUnits: 5
  69. }
  70. };
  71. var layer = new PreferencesBase.PathLayer("/.brackets.json");
  72. expect(layer.get(data, "spaceUnits", {
  73. path: "/public/index.html"
  74. })).toBe(2);
  75. expect(layer.get(data, "spaceUnits", {
  76. path: "/lib/script.js"
  77. })).toBe(3);
  78. expect(layer.get(data, "spaceUnits", {
  79. path: "/lib/foo/script.js"
  80. })).toBeUndefined();
  81. expect(layer.get(data, "spaceUnits", {
  82. path: "/lib/foo/styles.css"
  83. })).toBe(4);
  84. expect(layer.get(data, "spaceUnits", {
  85. path: "/README.md"
  86. })).toBe(5);
  87. expect(layer.get(data, "spaceUnits", {
  88. path: "foo.js"
  89. })).toBeUndefined();
  90. });
  91. it("can retrieve the location of the pref value", function () {
  92. var data = {
  93. "**.html": {
  94. spaceUnits: 2
  95. },
  96. "lib/*.js": {
  97. soaceUnits: 3
  98. }
  99. };
  100. var layer = new PreferencesBase.PathLayer("/.brackets.json");
  101. expect(layer.getPreferenceLocation(data, "spaceUnits", {
  102. path: "/foo.txt"
  103. })).toBeUndefined();
  104. expect(layer.getPreferenceLocation(data, "spaceUnits", {
  105. path: "/index.html"
  106. })).toEqual("**.html");
  107. expect(layer.getPreferenceLocation(data, "spaceUnits", {
  108. path: "/lib/brackets.js"
  109. })).toEqual("lib/*.js");
  110. });
  111. it("can set values in any of the patterns", function () {
  112. var data = {
  113. "**.html": {
  114. spaceUnits: 2
  115. },
  116. "lib/*.js": {
  117. spaceUnits: 3
  118. }
  119. };
  120. var originalData = _.clone(data, true);
  121. var layer = new PreferencesBase.PathLayer("/.brackets.json");
  122. expect(layer.set(data, "spaceUnits", 10, {
  123. path: "/foo.txt"
  124. })).toBe(false);
  125. expect(data).toEqual(originalData);
  126. expect(layer.set(data, "spaceUnits", 11, {
  127. path: "/index.html"
  128. })).toBe(true);
  129. expect(data).toEqual({
  130. "**.html": {
  131. spaceUnits: 11
  132. },
  133. "lib/*.js": {
  134. spaceUnits: 3
  135. }
  136. });
  137. expect(layer.set(data, "spaceUnits", 12, {
  138. path: "/index.html"
  139. }, "lib/*.js")).toBe(true);
  140. expect(data).toEqual({
  141. "**.html": {
  142. spaceUnits: 11
  143. },
  144. "lib/*.js": {
  145. spaceUnits: 12
  146. }
  147. });
  148. expect(layer.set(data, "spaceUnits", 13, {}, "**.md")).toBe(true);
  149. });
  150. it("should not set the same value twice", function () {
  151. var data = {
  152. "**.html": {
  153. spaceUnits: 2
  154. },
  155. "lib/*.js": {
  156. spaceUnits: 3
  157. }
  158. };
  159. var layer = new PreferencesBase.PathLayer("/.brackets.json");
  160. expect(layer.set(data, "spaceUnits", 11, {
  161. path: "/index.html"
  162. })).toBe(true);
  163. // Try to set the same value again.
  164. expect(layer.set(data, "spaceUnits", 11, {
  165. path: "/index.html"
  166. })).toBe(false);
  167. expect(data).toEqual({
  168. "**.html": {
  169. spaceUnits: 11
  170. },
  171. "lib/*.js": {
  172. spaceUnits: 3
  173. }
  174. });
  175. });
  176. });
  177. describe("Language Layer", function () {
  178. it("returns the setting for the corresponding language", function () {
  179. var data = {
  180. "html": {
  181. spaceUnits: 2
  182. },
  183. "json": {
  184. spaceUnits: 3
  185. },
  186. "css": {
  187. spaceUnits: 4
  188. },
  189. "javascript": {
  190. spaceUnits: 5
  191. }
  192. };
  193. var layer = new PreferencesBase.LanguageLayer();
  194. expect(layer.get(data, "spaceUnits", {language: "html"})).toBe(2);
  195. expect(layer.get(data, "spaceUnits", {language: "json"})).toBe(3);
  196. expect(layer.get(data, "spaceUnits", {language: "javascript"})).toBe(5);
  197. expect(layer.get(data, "spaceUnits", {language: "cobol"})).toBeUndefined();
  198. expect(layer.get(data, "spaceUnits", {language: "css"})).toBe(4);
  199. });
  200. it("can retrieve the location of the pref value", function () {
  201. var data = {
  202. "html": {
  203. spaceUnits: 2
  204. },
  205. "javascript": {
  206. spaceUnits: 3
  207. }
  208. };
  209. var layer = new PreferencesBase.LanguageLayer();
  210. expect(layer.getPreferenceLocation(data, "spaceUnits", {language: "text"})).toBeUndefined();
  211. expect(layer.getPreferenceLocation(data, "spaceUnits", {language: "html"})).toEqual("html");
  212. expect(layer.getPreferenceLocation(data, "spaceUnits", {language: "javascript"})).toEqual("javascript");
  213. });
  214. it("can set values in any of the patterns", function () {
  215. var data = {
  216. "html": {
  217. spaceUnits: 2,
  218. niceBooleanOption: true
  219. },
  220. "css": {
  221. spaceUnits: 3
  222. }
  223. };
  224. var originalData = _.clone(data, true);
  225. var layer = new PreferencesBase.LanguageLayer();
  226. expect(layer.set(data, "spaceUnits", 10, {}, "javascript")).toBe(true);
  227. expect(data).toEqual({
  228. "html": {
  229. spaceUnits: 2,
  230. niceBooleanOption: true
  231. },
  232. "css": {
  233. spaceUnits: 3
  234. },
  235. "javascript": {
  236. spaceUnits: 10
  237. }
  238. });
  239. expect(layer.set(data, "spaceUnits", undefined, {}, "javascript")).toBe(true);
  240. expect(data).toEqual(originalData);
  241. expect(layer.set(data, "spaceUnits", 11, {}, "html")).toBe(true);
  242. expect(data).toEqual({
  243. "html": {
  244. spaceUnits: 11,
  245. niceBooleanOption: true
  246. },
  247. "css": {
  248. spaceUnits: 3
  249. }
  250. });
  251. expect(layer.set(data, "spaceUnits", 12, {language: "css"})).toBe(true);
  252. expect(data).toEqual({
  253. "html": {
  254. spaceUnits: 11,
  255. niceBooleanOption: true
  256. },
  257. "css": {
  258. spaceUnits: 12
  259. }
  260. });
  261. expect(layer.set(data, "niceBooleanOption", false, {language: "html"})).toBe(true);
  262. expect(data).toEqual({
  263. "html": {
  264. spaceUnits: 11,
  265. niceBooleanOption: false
  266. },
  267. "css": {
  268. spaceUnits: 12
  269. }
  270. });
  271. expect(layer.set(data, "niceBooleanOption", undefined, {language: "html"})).toBe(true);
  272. expect(layer.set(data, "niceBooleanOption", false, {language: "css"}, "css")).toBe(true);
  273. expect(layer.set(data, "niceBooleanOption", undefined, {language: "javascript"}, "javascript")).toBe(false);
  274. expect(layer.set(data, "niceBooleanOption", true, {language: "php"}, "php")).toBe(true);
  275. expect(data).toEqual({
  276. "html": {
  277. spaceUnits: 11
  278. },
  279. "css": {
  280. spaceUnits: 12,
  281. niceBooleanOption: false
  282. },
  283. "javascript": {
  284. },
  285. "php": {
  286. niceBooleanOption: true
  287. }
  288. });
  289. });
  290. it("should not set the same value twice", function () {
  291. var data = {
  292. "html": {
  293. spaceUnits: 2
  294. },
  295. "javascript": {
  296. spaceUnits: 4
  297. }
  298. };
  299. var layer = new PreferencesBase.LanguageLayer();
  300. expect(layer.set(data, "spaceUnits", 11, {}, "javascript")).toBe(true);
  301. // Try to set the same value again.
  302. expect(layer.set(data, "spaceUnits", 11, {}, "javascript")).toBe(false);
  303. // And again with the language set
  304. expect(layer.set(data, "spaceUnits", 11, {language: "javascript"})).toBe(false);
  305. expect(data).toEqual({
  306. "html": {
  307. spaceUnits: 2
  308. },
  309. "javascript": {
  310. spaceUnits: 11
  311. }
  312. });
  313. });
  314. });
  315. describe("Scope", function () {
  316. it("should look up a value", function () {
  317. var data = {
  318. spaceUnits: 4,
  319. useTabChar: false
  320. };
  321. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  322. // MemoryStorage operates synchronously
  323. scope.load();
  324. expect(scope.get("spaceUnits")).toBe(4);
  325. expect(scope.getKeys().sort()).toEqual(["spaceUnits", "useTabChar"].sort());
  326. });
  327. it("should not set the same value twice", function () {
  328. var data = {
  329. spaceUnits: 4,
  330. useTabChar: false
  331. };
  332. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  333. // MemoryStorage operates synchronously
  334. scope.load();
  335. expect(scope.get("spaceUnits")).toBe(4);
  336. expect(scope.set("spaceUnits", 12)).toBe(true);
  337. expect(scope._dirty).toBe(true);
  338. // Explicitly save it in order to clear dirty flag.
  339. scope.save();
  340. expect(scope._dirty).toBe(false);
  341. // Try to set the same value again and verify that the dirty flag is not set.
  342. expect(scope.set("spaceUnits", 12)).toBe(false);
  343. expect(scope.get("spaceUnits")).toBe(12);
  344. expect(scope._dirty).toBe(false);
  345. });
  346. it("should correctly handle changes on objects", function () {
  347. var foo = {
  348. value: 42
  349. };
  350. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()),
  351. pm = new PreferencesBase.PreferencesSystem();
  352. // Explicitly create a PreferencesSystem and add the scope object to it
  353. // so that we can call set/get functions on the PreferencesSystem instead
  354. // of calling directly on scope object. Note that calling 'get' function on
  355. // scope object will not clone a preference object as it does in 'get' function
  356. // on PreferencesSystem object.
  357. pm.addScope("test", scope);
  358. // MemoryStorage operates synchronously
  359. scope.load();
  360. expect(pm.set("foo", foo).stored).toBe(true);
  361. expect(pm.get("foo").value).toBe(42);
  362. expect(scope._dirty).toBe(false);
  363. // Explicitly save it in order to clear dirty flag.
  364. pm.save();
  365. expect(scope._dirty).toBe(false);
  366. foo.value = "!!!";
  367. expect(foo.value).toBe("!!!");
  368. expect(pm.set("foo", foo, undefined, true).stored).toBe(true);
  369. expect(scope._dirty).toBe(true);
  370. pm.save();
  371. expect(scope._dirty).toBe(false);
  372. var fooCopyFromPref = pm.get("foo");
  373. expect(fooCopyFromPref.value).toBe("!!!");
  374. // Add 'bar' to our local copy and then
  375. // verify that our change is not in the pref.
  376. fooCopyFromPref.bar = "'bar' should not be in pref";
  377. expect(pm.get("foo").bar).toBe(undefined);
  378. });
  379. it("should remove the preference when setting it with 'undefined' value", function () {
  380. var data = {
  381. spaceUnits: 0,
  382. useTabChar: false
  383. };
  384. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  385. // MemoryStorage operates synchronously
  386. scope.load();
  387. expect(scope.get("spaceUnits")).toBe(0);
  388. // Remove 'spaceUnits' by calling set with 'undefined' second argument
  389. expect(scope.set("spaceUnits")).toBe(true);
  390. expect(scope._dirty).toBe(true);
  391. expect(scope.getKeys()).toEqual(["useTabChar"]);
  392. expect(scope.get("useTabChar")).toBe(false);
  393. // Remove 'useTabChar' by calling set with 'undefined' second argument
  394. expect(scope.set("useTabChar")).toBe(true);
  395. expect(scope._dirty).toBe(true);
  396. expect(scope.getKeys()).toEqual([]);
  397. });
  398. it("should look up a value with a path layer", function () {
  399. var data = {
  400. spaceUnits: 4,
  401. path: {
  402. "src/*js": {
  403. spaceUnits: 2
  404. }
  405. }
  406. };
  407. var layer = new PreferencesBase.PathLayer("/");
  408. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  409. scope.load();
  410. scope.addLayer(layer);
  411. expect(scope.get("path")).toBeUndefined();
  412. expect(scope.get("spaceUnits")).toBe(4);
  413. expect(scope.get("spaceUnits", {
  414. path: "/src/foo.js"
  415. })).toBe(2);
  416. expect(scope.get("spaceUnits", {
  417. path: "/top.js"
  418. })).toBe(4);
  419. });
  420. it("should look up a value using language layer", function () {
  421. var data = {
  422. spaceUnits: 4,
  423. language: {
  424. html: {
  425. spaceUnits: 2
  426. },
  427. javascript: {
  428. someBooleanOption: false
  429. }
  430. }
  431. };
  432. var layer = new PreferencesBase.LanguageLayer();
  433. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  434. scope.load();
  435. expect(scope.get("language")).toBeDefined();
  436. scope.addLayer(layer);
  437. expect(scope.get("language")).toBeUndefined();
  438. expect(scope.get("spaceUnits")).toBe(4);
  439. expect(scope.get("spaceUnits", {language: "html"})).toBe(2);
  440. expect(scope.get("someBooleanOption", {language: "javascript"})).toBe(false);
  441. });
  442. it("can look up the location of a preference", function () {
  443. var data = {
  444. spaceUnits: 4,
  445. path: {
  446. "src/*js": {
  447. spaceUnits: 2
  448. }
  449. },
  450. language: {
  451. "cobol": {
  452. spaceUnits: 5
  453. }
  454. }
  455. };
  456. var pathLayer = new PreferencesBase.PathLayer("/.brackets.json");
  457. var languageLayer = new PreferencesBase.LanguageLayer();
  458. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  459. scope.load();
  460. scope.addLayer(pathLayer);
  461. scope.addLayer(languageLayer);
  462. expect(scope.getPreferenceLocation("unknown")).toBeUndefined();
  463. expect(scope.getPreferenceLocation("path")).toBeUndefined();
  464. expect(scope.getPreferenceLocation("language")).toBeUndefined();
  465. expect(scope.getPreferenceLocation("spaceUnits")).toEqual({});
  466. expect(scope.getPreferenceLocation("spaceUnits", {
  467. path: "/src/brackets.js"
  468. })).toEqual({
  469. layer: "path",
  470. layerID: "src/*js"
  471. });
  472. expect(scope.getPreferenceLocation("spaceUnits", {
  473. path: "/index.md"
  474. })).toEqual({});
  475. expect(scope.getPreferenceLocation("spaceUnits", {language: "cobol"})).toEqual({
  476. layer: "language",
  477. layerID: "cobol"
  478. });
  479. });
  480. it("can set a preference at any layer", function () {
  481. var data = {
  482. spaceUnits: 4,
  483. path: {
  484. "src/*js": {
  485. spaceUnits: 2
  486. },
  487. "*.html": {
  488. spaceUnits: 1
  489. }
  490. },
  491. language: {
  492. html: {
  493. spaceUnits: 11,
  494. niceBooleanOption: false
  495. },
  496. css: {
  497. spaceUnits: 13
  498. }
  499. }
  500. };
  501. var pathLayer = new PreferencesBase.PathLayer("/.brackets.json");
  502. var languageLayer = new PreferencesBase.LanguageLayer();
  503. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  504. scope.load();
  505. scope.addLayer(pathLayer);
  506. scope.addLayer(languageLayer);
  507. expect(scope.set("spaceUnits", 5)).toBe(true);
  508. expect(data.spaceUnits).toBe(5);
  509. expect(scope._dirty).toBe(true);
  510. scope._dirty = false;
  511. expect(scope.set("spaceUnits", 6, {
  512. path: "/src/brackets.js"
  513. })).toBe(true);
  514. expect(data.spaceUnits).toBe(5);
  515. expect(data.path["src/*js"].spaceUnits).toBe(6);
  516. expect(scope._dirty).toBe(true);
  517. scope._dirty = false;
  518. expect(scope.set("spaceUnits", 7, {
  519. path: "/foo.md"
  520. }, {
  521. layer: "path"
  522. })).toBe(false);
  523. expect(data.spaceUnits).toBe(5);
  524. expect(data.path["src/*js"].spaceUnits).toBe(6);
  525. expect(scope._dirty).toBe(false);
  526. expect(scope.set("spaceUnits", 8, { }, {
  527. layer: "path",
  528. layerID: "**.md"
  529. })).toBe(true);
  530. expect(data.spaceUnits).toBe(5);
  531. expect(data.path["src/*js"].spaceUnits).toBe(6);
  532. expect(data.path["**.md"].spaceUnits).toBe(8);
  533. expect(scope._dirty).toBe(true);
  534. scope._dirty = false;
  535. expect(scope.set("spaceUnits", 9, {
  536. path: "index.html"
  537. }, { })).toBe(true);
  538. expect(data.spaceUnits).toBe(9);
  539. expect(data.path["*.html"].spaceUnits).toBe(1);
  540. expect(scope._dirty).toBe(true);
  541. expect(scope.set("spaceUnits", 12, {}, {
  542. layer: "language",
  543. layerID: "html"
  544. })).toBe(true);
  545. expect(data.spaceUnits).toBe(9);
  546. expect(data.path["*.html"].spaceUnits).toBe(1);
  547. expect(data.language.html.spaceUnits).toBe(12);
  548. expect(scope._dirty).toBe(true);
  549. expect(scope.set("spaceUnits", undefined, {}, {
  550. layer: "language",
  551. layerID: "css"
  552. })).toBe(true);
  553. expect(data.spaceUnits).toBe(9);
  554. expect(data.path["*.html"].spaceUnits).toBe(1);
  555. expect(data.language.html.spaceUnits).toBe(12);
  556. expect(data.language.css).toBeUndefined();
  557. expect(scope._dirty).toBe(true);
  558. expect(scope.set("spaceUnits", undefined, {}, {})).toBe(true);
  559. expect(data.spaceUnits).toBeUndefined();
  560. expect(Object.keys(data)).toEqual(["path", "language"]);
  561. expect(scope.set("spaceUnits", undefined, {}, {
  562. layer: "path",
  563. layerID: "*.html"
  564. })).toBe(true);
  565. expect(Object.keys(data.path["*.html"])).toEqual([]);
  566. expect(scope.get("niceBooleanOption", {language: "html"})).toBe(false);
  567. });
  568. it("can return its keys", function () {
  569. var data = {
  570. spaceUnits: 4,
  571. useEmojiForTabs: true,
  572. path: {
  573. "**.js": {
  574. spaceUnits: 3,
  575. showNonWhitespace: false
  576. },
  577. "README.md": {
  578. markdown: false,
  579. useEmojiForTabs: false
  580. }
  581. },
  582. language: {
  583. "html": {
  584. niceHTMLOption: true
  585. }
  586. }
  587. };
  588. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  589. scope.addLayer(new PreferencesBase.PathLayer("/"));
  590. scope.addLayer(new PreferencesBase.LanguageLayer());
  591. scope.load();
  592. var keys = scope.getKeys();
  593. var expected = ["spaceUnits", "useEmojiForTabs", "showNonWhitespace", "markdown", "niceHTMLOption"];
  594. expect(keys.sort()).toEqual(expected.sort());
  595. keys = scope.getKeys({
  596. path: "/coffeescript.ts"
  597. });
  598. expected = ["spaceUnits", "useEmojiForTabs"];
  599. expect(keys.sort()).toEqual(expected.sort());
  600. keys = scope.getKeys({
  601. path: "/README.md"
  602. });
  603. expected = ["spaceUnits", "useEmojiForTabs", "markdown"];
  604. expect(keys.sort()).toEqual(expected.sort());
  605. keys = scope.getKeys({
  606. path: "/test.js",
  607. language: "html"
  608. });
  609. expected = ["spaceUnits", "useEmojiForTabs", "showNonWhitespace", "niceHTMLOption"];
  610. expect(keys.sort()).toEqual(expected.sort());
  611. });
  612. it("notifies of changes", function () {
  613. var data1 = {
  614. spaceUnits: 4,
  615. cursorSize: 27,
  616. path: {
  617. "**.js": {
  618. statusBarElephants: false
  619. }
  620. }
  621. };
  622. var storage = new PreferencesBase.MemoryStorage(data1);
  623. var scope = new PreferencesBase.Scope(storage);
  624. scope.addLayer(new PreferencesBase.PathLayer("/"));
  625. scope.addLayer(new PreferencesBase.LanguageLayer());
  626. scope.load();
  627. var data2 = {
  628. spaceUnits: 4,
  629. path: {
  630. "**.js": {
  631. trafficLight: "green"
  632. },
  633. "**.md": {
  634. statusBarElephants: true
  635. }
  636. },
  637. language: {
  638. javascript: {
  639. niceBooleanOption: true
  640. }
  641. }
  642. };
  643. storage.data = data2;
  644. var events1 = [];
  645. scope.on("change", function (e, data) {
  646. events1.push(data);
  647. });
  648. scope.load();
  649. expect(events1.length).toBe(1);
  650. expect(events1[0].ids.sort()).toEqual(
  651. ["spaceUnits", "niceBooleanOption", "cursorSize", "statusBarElephants", "trafficLight"].sort()
  652. );
  653. var data3 = {
  654. spaceUnits: 4,
  655. path: {
  656. "**.js": {
  657. trafficLight: "green"
  658. },
  659. "**.md": {
  660. statusBarElephants: true
  661. }
  662. },
  663. language: {
  664. html: {
  665. spaceshipColor: "silver"
  666. }
  667. }
  668. };
  669. storage.data = data3;
  670. var events2 = [];
  671. scope.on("change", function (e, data) {
  672. events2.push(data);
  673. });
  674. scope.load();
  675. expect(events2.length).toBe(1);
  676. expect(events2[0].ids.sort()).toEqual(["niceBooleanOption", "spaceUnits", "statusBarElephants", "trafficLight", "spaceshipColor"].sort());
  677. });
  678. });
  679. describe("PreferencesSystem", function () {
  680. it("should yield an error if a preference is redefined", function () {
  681. var pm = new PreferencesBase.PreferencesSystem();
  682. pm.definePreference("foo.bar", "string");
  683. try {
  684. pm.definePreference("foo.bar", "string");
  685. expect("We should have gotten an exception").toEqual("but we didn't");
  686. } catch (e) {
  687. }
  688. });
  689. it("will automatically wrap a Storage with a Scope", function () {
  690. var pm = new PreferencesBase.PreferencesSystem();
  691. pm.addScope("test", new PreferencesBase.MemoryStorage());
  692. pm.set("testval", 27);
  693. expect(pm.get("testval")).toBe(27);
  694. });
  695. it("should find the default values", function () {
  696. var pm = new PreferencesBase.PreferencesSystem();
  697. pm.definePreference("foo.bar", "number", 0);
  698. expect(pm.get("nonexistent")).toBeUndefined();
  699. expect(pm.get("foo.bar")).toBe(0);
  700. });
  701. it("should produce an error for setValue on undefined scope", function () {
  702. var pm = new PreferencesBase.PreferencesSystem();
  703. expect(pm.set("foo", false, {
  704. location: {
  705. scope: "nonscope"
  706. }
  707. }).stored).toBe(false);
  708. });
  709. it("supports nested scopes", function () {
  710. var pm = new PreferencesBase.PreferencesSystem();
  711. pm.definePreference("useTabChar", "boolean", false);
  712. pm.definePreference("tabSize", "number", 4);
  713. pm.definePreference("spaceUnits", "number", 4);
  714. var userScope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage());
  715. var projectScope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage());
  716. pm.addScope("user", userScope);
  717. pm.addScope("project", projectScope);
  718. expect(pm.get("spaceUnits")).toEqual(4);
  719. var userLocation = {
  720. location: {
  721. scope: "user"
  722. }
  723. };
  724. pm.set("useTabChar", true, userLocation);
  725. pm.set("tabSize", 8, userLocation);
  726. pm.set("spaceUnits", 8, userLocation);
  727. pm.set("spaceUnits", 2, {
  728. location: {
  729. scope: "project"
  730. }
  731. });
  732. expect(pm.get("spaceUnits")).toBe(2);
  733. expect(pm.get("useTabChar")).toBe(true);
  734. expect(pm.get("tabSize")).toBe(8);
  735. });
  736. it("handles asynchronously loaded scopes", function () {
  737. var storage1 = new PreferencesBase.MemoryStorage({
  738. testKey: 1
  739. });
  740. var deferred1 = $.Deferred();
  741. storage1.load = function () {
  742. return deferred1;
  743. };
  744. var storage2 = new PreferencesBase.MemoryStorage({
  745. testKey: 2
  746. });
  747. var deferred2 = $.Deferred();
  748. storage2.load = function () {
  749. return deferred2;
  750. };
  751. var pm = new PreferencesBase.PreferencesSystem();
  752. pm.definePreference("testKey", "number", 0);
  753. pm.addScope("storage1", new PreferencesBase.Scope(storage1), {
  754. before: "storage2"
  755. });
  756. pm.addScope("storage2", new PreferencesBase.Scope(storage2), {
  757. before: "default"
  758. });
  759. expect(pm.get("testKey")).toBe(0);
  760. deferred1.resolve(storage1.data);
  761. expect(pm.get("testKey")).toBe(0);
  762. deferred2.resolve(storage2.data);
  763. expect(pm.get("testKey")).toBe(1);
  764. });
  765. it("can notify of preference changes through set", function () {
  766. var pm = new PreferencesBase.PreferencesSystem();
  767. pm.definePreference("spaceUnits", "number", 4);
  768. pm.addScope("user", new PreferencesBase.MemoryStorage());
  769. var eventData;
  770. pm.on("change", function (e, data) {
  771. eventData = data;
  772. });
  773. pm.set("testing", true);
  774. expect(eventData).toEqual({
  775. ids: ["testing"]
  776. });
  777. eventData = undefined;
  778. pm.set("spaceUnits", 4);
  779. expect(eventData).toEqual({
  780. ids: ["spaceUnits"]
  781. });
  782. });
  783. it("can notify of preference changes via scope changes and scope changes", function () {
  784. var pm = new PreferencesBase.PreferencesSystem();
  785. pm.definePreference("spaceUnits", "number", 4);
  786. var eventData = [],
  787. scopeEvents = [];
  788. pm.on("change", function (e, data) {
  789. eventData.push(data);
  790. });
  791. pm.on("scopeOrderChange", function (e, data) {
  792. scopeEvents.push(data);
  793. });
  794. pm.addScope("user", new PreferencesBase.MemoryStorage({
  795. spaceUnits: 4,
  796. elephants: "charging"
  797. }));
  798. expect(pm._defaults.scopeOrder).toEqual(["user", "default"]);
  799. expect(eventData).toEqual([{
  800. ids: ["spaceUnits", "elephants"]
  801. }]);
  802. expect(scopeEvents).toEqual([{
  803. id: "user",
  804. action: "added"
  805. }]);
  806. scopeEvents = [];
  807. eventData = [];
  808. pm.removeScope("user");
  809. expect(pm._defaults.scopeOrder).toEqual(["default"]);
  810. expect(eventData).toEqual([{
  811. ids: ["spaceUnits", "elephants"]
  812. }]);
  813. expect(scopeEvents).toEqual([{
  814. id: "user",
  815. action: "removed"
  816. }]);
  817. });
  818. it("notifies when there are layer changes", function () {
  819. var pm = new PreferencesBase.PreferencesSystem();
  820. var data = {
  821. spaceUnits: 4,
  822. useTabChar: false,
  823. path: {
  824. "*.txt": {
  825. spaceUnits: 2,
  826. alpha: "bravo"
  827. }
  828. }
  829. };
  830. var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage(data));
  831. pm.addScope("user", scope);
  832. var eventData = [];
  833. pm.on("change", function (e, data) {
  834. eventData.push(data);
  835. });
  836. scope.addLayer(new PreferencesBase.PathLayer("/"));
  837. expect(eventData).toEqual([{
  838. ids: ["spaceUnits", "alpha"]
  839. }]);
  840. // Extra verification that layer keys works correctly
  841. var keys = scope._layers[0].getKeys(scope.data.path, {
  842. path: "/bar.md"
  843. });
  844. expect(keys).toEqual([]);
  845. keys = scope._layers[0].getKeys(scope.data.path, {
  846. path: "/foo.txt"
  847. });
  848. expect(keys.sort()).toEqual(["spaceUnits", "alpha"].sort());
  849. expect(pm.get("spaceUnits")).toBe(4);
  850. expect(pm.get("spaceUnits", {path: "/foo.txt"})).toBe(2);
  851. eventData = [];
  852. pm.signalContextChanged({path: "/README.md"}, {path: "/README.txt"});
  853. expect(eventData).toEqual([{
  854. ids: ["spaceUnits", "alpha"]
  855. }]);
  856. eventData = [];
  857. pm.signalContextChanged({path: "/foo.txt"}, {path: "/README.txt"});
  858. expect(eventData).toEqual([]);
  859. // Test to make sure there are no exceptions when there is no path data
  860. delete data.path;
  861. scope.load();
  862. expect(scope.data).toEqual(data);
  863. });
  864. it("can notify changes for single preference objects", function () {
  865. var pm = new PreferencesBase.PreferencesSystem();
  866. var pref = pm.definePreference("spaceUnits", "number", 4);
  867. var retrievedPref = pm.getPreference("spaceUnits");
  868. expect(retrievedPref).toBe(pref);
  869. var changes = 0;
  870. pref.on("change", function (e) {
  871. changes++;
  872. });
  873. var newScope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage({
  874. spaceUnits: 2
  875. }));
  876. pm.addScope("new", newScope);
  877. expect(changes).toEqual(1);
  878. pref.off("change");
  879. pm.set("spaceUnits", 10, {
  880. location: {
  881. scope: "new"
  882. }
  883. });
  884. expect(changes).toEqual(1);
  885. changes = 0;
  886. pm.on("change", "spaceUnits", function () {
  887. changes++;
  888. });
  889. pm.set("spaceUnits", 11, {
  890. location: {
  891. scope: "new"
  892. }
  893. });
  894. expect(changes).toEqual(1);
  895. });
  896. it("can pause and resume broadcast of events", function () {
  897. var pm = new PreferencesBase.PreferencesSystem();
  898. pm.addScope("user", new PreferencesBase.MemoryStorage());
  899. pm.pauseChangeEvents();
  900. var spaceUnitChanges = 0,
  901. fooChanges = 0,
  902. globalChangeMessages = [];
  903. pm.definePreference("spaceUnits", "number", 4).on("change", function () {
  904. spaceUnitChanges++;
  905. });
  906. pm.definePreference("foo", "string", "bar").on("change", function () {
  907. fooChanges++;
  908. });
  909. pm.on("change", function (e, data) {
  910. globalChangeMessages.push(data);
  911. });
  912. pm.set("spaceUnits", 8);
  913. pm.set("foo", "baz");
  914. expect(spaceUnitChanges).toBe(0);
  915. expect(fooChanges).toBe(0);
  916. expect(globalChangeMessages).toEqual([]);
  917. pm.resumeChangeEvents();
  918. expect(spaceUnitChanges).toBe(1);
  919. expect(fooChanges).toBe(1);
  920. expect(globalChangeMessages).toEqual([{
  921. ids: ["spaceUnits", "foo"]
  922. }]);
  923. pm.set("foo", "zippy");
  924. expect(fooChanges).toBe(2);
  925. });
  926. it("can dynamically modify the default scope order", function () {
  927. var pm = new PreferencesBase.PreferencesSystem();
  928. pm.addScope("user", new PreferencesBase.MemoryStorage({
  929. spaceUnits: 1
  930. }));
  931. pm.addScope("project", new PreferencesBase.MemoryStorage({
  932. spaceUnits: 2
  933. }));
  934. pm.addScope("session", new PreferencesBase.MemoryStorage());
  935. expect(pm.get("spaceUnits")).toBe(2);
  936. expect(pm._defaults.scopeOrder).toEqual(["session", "project", "user", "default"]);
  937. var eventData = [];
  938. pm.on("change", function (e, data) {
  939. eventData.push(data);
  940. });
  941. pm.removeFromScopeOrder("project");
  942. expect(pm._defaults.scopeOrder).toEqual(["session", "user", "default"]);
  943. expect(eventData).toEqual([{
  944. ids: ["spaceUnits"]
  945. }]);
  946. expect(pm.get("spaceUnits")).toBe(1);
  947. expect(pm.get("spaceUnits", {
  948. scopeOrder: ["session", "project", "user", "default"]
  949. })).toBe(2);
  950. eventData = [];
  951. pm.addToScopeOrder("project", "user");
  952. expect(pm._defaults.scopeOrder).toEqual(["session", "project", "user", "default"]);
  953. expect(eventData).toEqual([{
  954. ids: ["spaceUnits"]
  955. }]);
  956. expect(pm.get("spaceUnits")).toBe(2);
  957. });
  958. it("can set preference values at any level", function () {
  959. var pm = new PreferencesBase.PreferencesSystem(),
  960. pref = pm.definePreference("spaceUnits", "number", 4),
  961. user = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage()),
  962. project = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage({
  963. path: {
  964. "**.html": {
  965. spaceUnits: 2
  966. }
  967. }
  968. })),
  969. session = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage());
  970. project.addLayer(new PreferencesBase.PathLayer("/"));
  971. pm.addScope("user", user);
  972. pm.addScope("project", project);
  973. pm.addScope("session", session);
  974. var changes = 0;
  975. pref.on("change", function (e) {
  976. changes++;
  977. });
  978. expect(pm.set("spaceUnits", 5, {
  979. location: {
  980. scope: "doesNotExist"
  981. }
  982. }).stored).toBe(false);
  983. expect(pm.get("spaceUnits")).toBe(4);
  984. expect(changes).toBe(0);
  985. expect(pm.getPreferenceLocation("spaceUnits")).toEqual({
  986. scope: "default"
  987. });
  988. expect(pm.set("spaceUnits", 6).stored).toBe(true);
  989. expect(user.data).toEqual({
  990. spaceUnits: 6
  991. });
  992. expect(changes).toBe(1);
  993. expect(pm.set("spaceUnits", 7).stored).toBe(true);
  994. expect(user.data).toEqual({
  995. spaceUnits: 7
  996. });
  997. expect(changes).toBe(2);
  998. expect(pm.set("spaceUnits", 8, {
  999. location: {
  1000. scope: "session"
  1001. }
  1002. }).stored).toBe(true);
  1003. expect(user.data).toEqual({
  1004. spaceUnits: 7
  1005. });
  1006. expect(session.data).toEqual({
  1007. spaceUnits: 8
  1008. });
  1009. expect(changes).toBe(3);
  1010. expect(pm.set("spaceUnits", 9).stored).toBe(true);
  1011. expect(changes).toBe(4);
  1012. expect(session.data).toEqual({
  1013. spaceUnits: 9
  1014. });
  1015. expect(pm.set("spaceUnits", undefined, {
  1016. location: {
  1017. scope: "session"
  1018. }
  1019. }).stored).toBe(true);
  1020. expect(changes).toBe(5);
  1021. expect(session.data.spaceUnits).toBeUndefined();
  1022. expect(pm.get("spaceUnits")).toBe(7);
  1023. expect(Object.keys(session.data)).toEqual([]);
  1024. pm.signalContextChanged({}, {path: "/index.html"});
  1025. expect(changes).toBe(6);
  1026. expect(pm.get("spaceUnits", {path: "/index.html"})).toBe(2);
  1027. expect(pm.set("spaceUnits", 10, {context: {path: "/index.html"}}).stored).toBe(true);
  1028. expect(changes).toBe(7);
  1029. expect(project.data.path["**.html"].spaceUnits).toBe(10);
  1030. pm.signalContextChanged({path: "/index.html"}, {path: "/foo.txt"});
  1031. expect(pm.getPreferenceLocation("spaceUnits")).toEqual({
  1032. scope: "user"
  1033. });
  1034. expect(pm.set("spaceUnits", 11, {
  1035. location: {
  1036. scope: "project"
  1037. }
  1038. }).stored).toBe(true);
  1039. expect(pm.getPreferenceLocation("spaceUnits")).toEqual({
  1040. scope: "project"
  1041. });
  1042. expect(pm.set("spaceUnits", 12).stored).toBe(true);
  1043. expect(project.data.spaceUnits).toBe(12);
  1044. expect(pm.set("spaceUnits", 13, {
  1045. location: {
  1046. scope: "project",
  1047. layer: "path",
  1048. layerID: "**.js"