settingregistry.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import expect = require('expect.js');
  4. import {
  5. IDataConnector, ISettingRegistry, SettingRegistry, Settings, StateDB
  6. } from '@jupyterlab/coreutils';
  7. import {
  8. JSONObject
  9. } from '@phosphor/coreutils';
  10. export
  11. class TestConnector extends StateDB implements IDataConnector<ISettingRegistry.IPlugin, JSONObject> {
  12. constructor(public schemas: { [key: string]: ISettingRegistry.ISchema } = { }) {
  13. super({ namespace: 'setting-registry-tests' });
  14. }
  15. fetch(id: string): Promise<ISettingRegistry.IPlugin | null> {
  16. return super.fetch(id).then(user => {
  17. if (!user && !this.schemas[id]) {
  18. return null;
  19. }
  20. user = user || { };
  21. const schema = this.schemas[id] || { type: 'object' };
  22. const result = { data: { composite: { }, user }, id, schema };
  23. return result;
  24. });
  25. }
  26. }
  27. describe('@jupyterlab/coreutils', () => {
  28. describe('SettingRegistry', () => {
  29. const connector = new TestConnector();
  30. let registry: SettingRegistry;
  31. afterEach(() => {
  32. connector.schemas = { };
  33. return connector.clear();
  34. });
  35. beforeEach(() => { registry = new SettingRegistry({ connector }); });
  36. describe('#constructor()', () => {
  37. it('should create a new setting registry', () => {
  38. expect(registry).to.be.a(SettingRegistry);
  39. });
  40. });
  41. describe('#pluginChanged', () => {
  42. it('should emit when a plugin changes', done => {
  43. const id = 'foo';
  44. const key = 'bar';
  45. const value = 'baz';
  46. connector.schemas[id] = { type: 'object' };
  47. registry.pluginChanged.connect((sender: any, plugin: string) => {
  48. expect(id).to.be(plugin);
  49. done();
  50. });
  51. registry.load(id).then(() => registry.set(id, key, value)).catch(done);
  52. });
  53. });
  54. describe('#plugins', () => {
  55. it('should return a list of registered plugins in registry', done => {
  56. const one = 'foo';
  57. const two = 'bar';
  58. expect(registry.plugins).to.be.empty();
  59. connector.schemas[one] = { type: 'object' };
  60. connector.schemas[two] = { type: 'object' };
  61. registry.load(one)
  62. .then(() => { expect(registry.plugins).to.have.length(1); })
  63. .then(() => registry.load(two))
  64. .then(() => { expect(registry.plugins).to.have.length(2); })
  65. .then(done)
  66. .catch(done);
  67. });
  68. });
  69. describe('#get()', () => {
  70. it('should get a setting item from a loaded plugin', done => {
  71. const id = 'foo';
  72. const key = 'bar';
  73. const value = 'baz';
  74. connector.schemas[id] = { type: 'object' };
  75. connector.save(id, { [key]: value })
  76. .then(() => registry.load(id))
  77. .then(() => registry.get(id, key))
  78. .then(saved => { expect(saved.user).to.be(value); })
  79. .then(done)
  80. .catch(done);
  81. });
  82. it('should get a setting item from a plugin that is not loaded', done => {
  83. const id = 'alpha';
  84. const key = 'beta';
  85. const value = 'gamma';
  86. connector.schemas[id] = { type: 'object' };
  87. connector.save(id, { [key]: value })
  88. .then(() => registry.get(id, key))
  89. .then(saved => { expect(saved.composite).to.be(value); })
  90. .then(done)
  91. .catch(done);
  92. });
  93. it('should use schema default if user data not available', done => {
  94. const id = 'alpha';
  95. const key = 'beta';
  96. const value = 'gamma';
  97. const schema = connector.schemas[id] = {
  98. type: 'object',
  99. properties: {
  100. [key]: { type: typeof value, default: value }
  101. }
  102. };
  103. registry.get(id, key)
  104. .then(saved => {
  105. expect(saved.composite).to.be(schema.properties[key].default);
  106. expect(saved.composite).to.not.be(saved.user);
  107. }).then(done)
  108. .catch(done);
  109. });
  110. it('should let user value override schema default', done => {
  111. const id = 'alpha';
  112. const key = 'beta';
  113. const value = 'gamma';
  114. const schema = connector.schemas[id] = {
  115. type: 'object',
  116. properties: {
  117. [key]: { type: typeof value, default: 'delta' }
  118. }
  119. };
  120. connector.save(id, { [key]: value })
  121. .then(() => registry.get(id, key))
  122. .then(saved => {
  123. expect(saved.composite).to.be(value);
  124. expect(saved.user).to.be(value);
  125. expect(saved.composite).to.not.be(schema.properties[key].default);
  126. expect(saved.user).to.not.be(schema.properties[key].default);
  127. }).then(done)
  128. .catch(done);
  129. });
  130. it('should reject if a plugin does not exist', done => {
  131. registry.get('foo', 'bar')
  132. .then(saved => { done('should not resolve'); })
  133. .catch(reason => { done(); });
  134. });
  135. it('should resolve `undefined` if a key does not exist', done => {
  136. const id = 'foo';
  137. const key = 'bar';
  138. connector.schemas[id] = { type: 'object' };
  139. registry.get(id, key)
  140. .then(saved => {
  141. expect(saved.composite).to.be(void 0);
  142. expect(saved.user).to.be(void 0);
  143. }).then(done)
  144. .catch(done);
  145. });
  146. });
  147. describe('#load()', () => {
  148. it(`should resolve a registered plugin's settings`, done => {
  149. const id = 'foo';
  150. expect(registry.plugins).to.be.empty();
  151. connector.schemas[id] = { type: 'object' };
  152. registry.load(id)
  153. .then(settings => { expect(settings.plugin).to.be(id); })
  154. .then(done)
  155. .catch(done);
  156. });
  157. it('should reject if a plugin does not exist', done => {
  158. registry.load('foo')
  159. .then(settings => { done('should not resolve'); })
  160. .catch(reason => { done(); });
  161. });
  162. });
  163. describe('#reload()', () => {
  164. it(`should load a registered plugin's settings`, done => {
  165. const id = 'foo';
  166. expect(registry.plugins).to.be.empty();
  167. connector.schemas[id] = { type: 'object' };
  168. registry.reload(id)
  169. .then(settings => { expect(settings.plugin).to.be(id); })
  170. .then(done)
  171. .catch(done);
  172. });
  173. it(`should replace a registered plugin's settings`, done => {
  174. const id = 'foo';
  175. const first = 'Foo';
  176. const second = 'Bar';
  177. expect(registry.plugins).to.be.empty();
  178. connector.schemas[id] = { type: 'object', title: first};
  179. registry.reload(id)
  180. .then(settings => { expect(settings.schema.title).to.be(first); })
  181. .then(() => { connector.schemas[id].title = second; })
  182. .then(() => registry.reload(id))
  183. .then(settings => { expect(settings.schema.title).to.be(second); })
  184. .then(done)
  185. .catch(done);
  186. });
  187. it('should reject if a plugin does not exist', done => {
  188. registry.reload('foo')
  189. .then(settings => { done('should not resolve'); })
  190. .catch(reason => { done(); });
  191. });
  192. });
  193. });
  194. describe('Settings', () => {
  195. const connector = new TestConnector();
  196. let registry: SettingRegistry;
  197. let settings: Settings;
  198. afterEach(() => {
  199. if (settings) {
  200. settings.dispose();
  201. settings = null;
  202. }
  203. connector.schemas = { };
  204. return connector.clear();
  205. });
  206. beforeEach(() => { registry = new SettingRegistry({ connector }); });
  207. describe('#constructor()', () => {
  208. it('should create a new settings object for a plugin', () => {
  209. const id = 'alpha';
  210. const data = { composite: { }, user: { } };
  211. const schema = { type: 'object' };
  212. const plugin = { id, data, schema };
  213. settings = new Settings({ plugin, registry });
  214. expect(settings).to.be.a(Settings);
  215. });
  216. });
  217. describe('#changed', () => {
  218. it('should emit when a plugin changes', done => {
  219. const id = 'alpha';
  220. const schema = { type: 'object' };
  221. connector.schemas[id] = schema;
  222. registry.load(id).then(s => { settings = s as Settings; })
  223. .then(() => {
  224. settings.changed.connect(() => { done(); });
  225. return settings.set('foo', 'bar');
  226. }).catch(done);
  227. });
  228. });
  229. describe('#composite', () => {
  230. it('should contain the merged user and default data', done => {
  231. const id = 'alpha';
  232. const key = 'beta';
  233. const value = 'gamma';
  234. const schema = connector.schemas[id] = {
  235. type: 'object',
  236. properties: {
  237. [key]: { type: typeof value, default: value }
  238. }
  239. };
  240. connector.schemas[id] = schema;
  241. registry.load(id).then(s => { settings = s as Settings; })
  242. .then(() => { expect(settings.composite[key]).to.equal(value); })
  243. .then(done).catch(done);
  244. });
  245. it('should privilege user data', done => {
  246. const id = 'alpha';
  247. const key = 'beta';
  248. const value = 'gamma';
  249. const schema = connector.schemas[id] = {
  250. type: 'object',
  251. properties: {
  252. [key]: { type: typeof value, default: 'delta' }
  253. }
  254. };
  255. connector.schemas[id] = schema;
  256. registry.load(id).then(s => { settings = s as Settings; })
  257. .then(() => settings.set(key, value))
  258. .then(() => { expect(settings.composite[key]).to.equal(value); })
  259. .then(done).catch(done);
  260. });
  261. });
  262. describe('#isDisposed', () => {
  263. it('should test whether the settings object is disposed', () => {
  264. const id = 'alpha';
  265. const data = { composite: { }, user: { } };
  266. const schema = { type: 'object' };
  267. const plugin = { id, data, schema };
  268. settings = new Settings({ plugin, registry });
  269. expect(settings.isDisposed).to.be(false);
  270. settings.dispose();
  271. expect(settings.isDisposed).to.be(true);
  272. });
  273. });
  274. describe('#schema', () => {
  275. it('should expose the plugin schema', () => {
  276. const id = 'alpha';
  277. const data = { composite: { }, user: { } };
  278. const schema = { type: 'object' };
  279. const plugin = { id, data, schema };
  280. settings = new Settings({ plugin, registry });
  281. expect(settings.schema).to.eql(schema);
  282. });
  283. });
  284. describe('#user', () => {
  285. it('should privilege user data', done => {
  286. const id = 'alpha';
  287. const key = 'beta';
  288. const value = 'gamma';
  289. const schema = connector.schemas[id] = {
  290. type: 'object',
  291. properties: {
  292. [key]: { type: typeof value, default: 'delta' }
  293. }
  294. };
  295. connector.schemas[id] = schema;
  296. registry.load(id).then(s => { settings = s as Settings; })
  297. .then(() => settings.set(key, value))
  298. .then(() => { expect(settings.user[key]).to.equal(value); })
  299. .then(done).catch(done);
  300. });
  301. });
  302. });
  303. });