Registry.test.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /* eslint no-console: 0 */
  20. import mockConsole from 'jest-mock-console';
  21. import { Registry, OverwritePolicy } from '@superset-ui/core';
  22. const loader = () => 'testValue';
  23. describe('Registry', () => {
  24. it('exists', () => {
  25. expect(Registry !== undefined).toBe(true);
  26. });
  27. describe('new Registry(config)', () => {
  28. it('can create a new registry when config.name is not given', () => {
  29. const registry = new Registry();
  30. expect(registry).toBeInstanceOf(Registry);
  31. });
  32. it('can create a new registry when config.name is given', () => {
  33. const registry = new Registry({ name: 'abc' });
  34. expect(registry).toBeInstanceOf(Registry);
  35. expect(registry.name).toBe('abc');
  36. });
  37. });
  38. describe('.clear()', () => {
  39. it('clears all registered items', () => {
  40. const registry = new Registry();
  41. registry.registerValue('a', 'testValue');
  42. registry.clear();
  43. expect(Object.keys(registry.items)).toHaveLength(0);
  44. expect(Object.keys(registry.promises)).toHaveLength(0);
  45. });
  46. it('returns the registry itself', () => {
  47. const registry = new Registry();
  48. expect(registry.clear()).toBe(registry);
  49. });
  50. });
  51. describe('.has(key)', () => {
  52. it('returns true if an item with the given key exists', () => {
  53. const registry = new Registry();
  54. registry.registerValue('a', 'testValue');
  55. expect(registry.has('a')).toBe(true);
  56. registry.registerLoader('b', () => 'testValue2');
  57. expect(registry.has('b')).toBe(true);
  58. });
  59. it('returns false if an item with the given key does not exist', () => {
  60. const registry = new Registry();
  61. expect(registry.has('a')).toBe(false);
  62. });
  63. });
  64. describe('.registerValue(key, value)', () => {
  65. it('registers the given value with the given key', () => {
  66. const registry = new Registry();
  67. registry.registerValue('a', 'testValue');
  68. expect(registry.has('a')).toBe(true);
  69. expect(registry.get('a')).toBe('testValue');
  70. });
  71. it('does not overwrite if value is exactly the same', () => {
  72. const registry = new Registry();
  73. const value = { a: 1 };
  74. registry.registerValue('a', value);
  75. const promise1 = registry.getAsPromise('a');
  76. registry.registerValue('a', value);
  77. const promise2 = registry.getAsPromise('a');
  78. expect(promise1).toBe(promise2);
  79. registry.registerValue('a', { a: 1 });
  80. const promise3 = registry.getAsPromise('a');
  81. expect(promise1).not.toBe(promise3);
  82. });
  83. it('overwrites item with loader', () => {
  84. const registry = new Registry();
  85. registry.registerLoader('a', () => 'ironman');
  86. expect(registry.get('a')).toBe('ironman');
  87. registry.registerValue('a', 'hulk');
  88. expect(registry.get('a')).toBe('hulk');
  89. });
  90. it('returns the registry itself', () => {
  91. const registry = new Registry();
  92. expect(registry.registerValue('a', 'testValue')).toBe(registry);
  93. });
  94. });
  95. describe('.registerLoader(key, loader)', () => {
  96. it('registers the given loader with the given key', () => {
  97. const registry = new Registry();
  98. registry.registerLoader('a', () => 'testValue');
  99. expect(registry.has('a')).toBe(true);
  100. expect(registry.get('a')).toBe('testValue');
  101. });
  102. it('does not overwrite if loader is exactly the same', () => {
  103. const registry = new Registry();
  104. registry.registerLoader('a', loader);
  105. const promise1 = registry.getAsPromise('a');
  106. registry.registerLoader('a', loader);
  107. const promise2 = registry.getAsPromise('a');
  108. expect(promise1).toBe(promise2);
  109. registry.registerLoader('a', () => 'testValue');
  110. const promise3 = registry.getAsPromise('a');
  111. expect(promise1).not.toBe(promise3);
  112. });
  113. it('overwrites item with value', () => {
  114. const registry = new Registry();
  115. registry.registerValue('a', 'hulk');
  116. expect(registry.get('a')).toBe('hulk');
  117. registry.registerLoader('a', () => 'ironman');
  118. expect(registry.get('a')).toBe('ironman');
  119. });
  120. it('returns the registry itself', () => {
  121. const registry = new Registry();
  122. expect(registry.registerLoader('a', () => 'testValue')).toBe(registry);
  123. });
  124. });
  125. describe('.get(key)', () => {
  126. it('given the key, returns the value if the item is a value', () => {
  127. const registry = new Registry();
  128. registry.registerValue('a', 'testValue');
  129. expect(registry.get('a')).toBe('testValue');
  130. });
  131. it('given the key, returns the result of the loader function if the item is a loader', () => {
  132. const registry = new Registry();
  133. registry.registerLoader('b', () => 'testValue2');
  134. expect(registry.get('b')).toBe('testValue2');
  135. });
  136. it('returns undefined if the item with specified key does not exist', () => {
  137. const registry = new Registry();
  138. expect(registry.get('a')).toBeUndefined();
  139. });
  140. it('If the key was registered multiple times, returns the most recent item.', () => {
  141. const registry = new Registry();
  142. registry.registerValue('a', 'testValue');
  143. expect(registry.get('a')).toBe('testValue');
  144. registry.registerLoader('a', () => 'newValue');
  145. expect(registry.get('a')).toBe('newValue');
  146. });
  147. });
  148. describe('.getAsPromise(key)', () => {
  149. it('given the key, returns a promise of item value if the item is a value', () => {
  150. const registry = new Registry();
  151. registry.registerValue('a', 'testValue');
  152. return registry
  153. .getAsPromise('a')
  154. .then(value => expect(value).toBe('testValue'));
  155. });
  156. it('given the key, returns a promise of result of the loader function if the item is a loader', () => {
  157. const registry = new Registry();
  158. registry.registerLoader('a', () => 'testValue');
  159. return registry
  160. .getAsPromise('a')
  161. .then(value => expect(value).toBe('testValue'));
  162. });
  163. it('returns same promise object for the same key unless user re-registers new value with the key.', () => {
  164. const registry = new Registry();
  165. registry.registerLoader('a', () => 'testValue');
  166. const promise1 = registry.getAsPromise('a');
  167. const promise2 = registry.getAsPromise('a');
  168. expect(promise1).toBe(promise2);
  169. });
  170. it('returns a rejected promise if the item with specified key does not exist', () => {
  171. const registry = new Registry();
  172. return registry.getAsPromise('a').then(null, (err: Error) => {
  173. expect(err.toString()).toEqual(
  174. 'Error: Item with key "a" is not registered.',
  175. );
  176. });
  177. });
  178. it('If the key was registered multiple times, returns a promise of the most recent item.', async () => {
  179. const registry = new Registry();
  180. registry.registerValue('a', 'testValue');
  181. expect(await registry.getAsPromise('a')).toBe('testValue');
  182. registry.registerLoader('a', () => 'newValue');
  183. expect(await registry.getAsPromise('a')).toBe('newValue');
  184. });
  185. });
  186. describe('.getMap()', () => {
  187. it('returns key-value map as plain object', () => {
  188. const registry = new Registry();
  189. registry.registerValue('a', 'cat');
  190. registry.registerLoader('b', () => 'dog');
  191. expect(registry.getMap()).toEqual({
  192. a: 'cat',
  193. b: 'dog',
  194. });
  195. });
  196. });
  197. describe('.getMapAsPromise()', () => {
  198. it('returns a promise of key-value map', () => {
  199. const registry = new Registry();
  200. registry.registerValue('a', 'test1');
  201. registry.registerLoader('b', () => 'test2');
  202. registry.registerLoader('c', () => Promise.resolve('test3'));
  203. return registry.getMapAsPromise().then(map =>
  204. expect(map).toEqual({
  205. a: 'test1',
  206. b: 'test2',
  207. c: 'test3',
  208. }),
  209. );
  210. });
  211. });
  212. describe('.keys()', () => {
  213. it('returns an array of keys', () => {
  214. const registry = new Registry();
  215. registry.registerValue('a', 'testValue');
  216. registry.registerLoader('b', () => 'test2');
  217. expect(registry.keys()).toEqual(['a', 'b']);
  218. });
  219. });
  220. describe('.values()', () => {
  221. it('returns an array of values', () => {
  222. const registry = new Registry();
  223. registry.registerValue('a', 'test1');
  224. registry.registerLoader('b', () => 'test2');
  225. expect(registry.values()).toEqual(['test1', 'test2']);
  226. });
  227. });
  228. describe('.valuesAsPromise()', () => {
  229. it('returns a Promise of an array { key, value }', () => {
  230. const registry = new Registry();
  231. registry.registerValue('a', 'test1');
  232. registry.registerLoader('b', () => 'test2');
  233. registry.registerLoader('c', () => Promise.resolve('test3'));
  234. return registry
  235. .valuesAsPromise()
  236. .then(entries => expect(entries).toEqual(['test1', 'test2', 'test3']));
  237. });
  238. });
  239. describe('.entries()', () => {
  240. it('returns an array of { key, value }', () => {
  241. const registry = new Registry();
  242. registry.registerValue('a', 'test1');
  243. registry.registerLoader('b', () => 'test2');
  244. expect(registry.entries()).toEqual([
  245. { key: 'a', value: 'test1' },
  246. { key: 'b', value: 'test2' },
  247. ]);
  248. });
  249. });
  250. describe('.entriesAsPromise()', () => {
  251. it('returns a Promise of an array { key, value }', () => {
  252. const registry = new Registry();
  253. registry.registerValue('a', 'test1');
  254. registry.registerLoader('b', () => 'test2');
  255. registry.registerLoader('c', () => Promise.resolve('test3'));
  256. return registry.entriesAsPromise().then(entries =>
  257. expect(entries).toEqual([
  258. { key: 'a', value: 'test1' },
  259. { key: 'b', value: 'test2' },
  260. { key: 'c', value: 'test3' },
  261. ]),
  262. );
  263. });
  264. });
  265. describe('.remove(key)', () => {
  266. it('removes the item with given key', () => {
  267. const registry = new Registry();
  268. registry.registerValue('a', 'testValue');
  269. registry.remove('a');
  270. expect(registry.get('a')).toBeUndefined();
  271. });
  272. it('does not throw error if the key does not exist', () => {
  273. const registry = new Registry();
  274. expect(() => registry.remove('a')).not.toThrow();
  275. });
  276. it('returns itself', () => {
  277. const registry = new Registry();
  278. registry.registerValue('a', 'testValue');
  279. expect(registry.remove('a')).toBe(registry);
  280. });
  281. });
  282. describe('config.overwritePolicy', () => {
  283. describe('=ALLOW', () => {
  284. describe('.registerValue(key, value)', () => {
  285. it('registers normally', () => {
  286. const restoreConsole = mockConsole();
  287. const registry = new Registry();
  288. registry.registerValue('a', 'testValue');
  289. expect(() => registry.registerValue('a', 'testValue2')).not.toThrow();
  290. expect(registry.get('a')).toEqual('testValue2');
  291. expect(console.warn).not.toHaveBeenCalled();
  292. restoreConsole();
  293. });
  294. });
  295. describe('.registerLoader(key, loader)', () => {
  296. it('registers normally', () => {
  297. const restoreConsole = mockConsole();
  298. const registry = new Registry();
  299. registry.registerLoader('a', () => 'testValue');
  300. expect(() =>
  301. registry.registerLoader('a', () => 'testValue2'),
  302. ).not.toThrow();
  303. expect(registry.get('a')).toEqual('testValue2');
  304. expect(console.warn).not.toHaveBeenCalled();
  305. restoreConsole();
  306. });
  307. });
  308. });
  309. describe('=WARN', () => {
  310. describe('.registerValue(key, value)', () => {
  311. it('warns when overwrite', () => {
  312. const restoreConsole = mockConsole();
  313. const registry = new Registry({
  314. overwritePolicy: OverwritePolicy.Warn,
  315. });
  316. registry.registerValue('a', 'testValue');
  317. expect(() => registry.registerValue('a', 'testValue2')).not.toThrow();
  318. expect(registry.get('a')).toEqual('testValue2');
  319. expect(console.warn).toHaveBeenCalled();
  320. restoreConsole();
  321. });
  322. });
  323. describe('.registerLoader(key, loader)', () => {
  324. it('warns when overwrite', () => {
  325. const restoreConsole = mockConsole();
  326. const registry = new Registry({
  327. overwritePolicy: OverwritePolicy.Warn,
  328. });
  329. registry.registerLoader('a', () => 'testValue');
  330. expect(() =>
  331. registry.registerLoader('a', () => 'testValue2'),
  332. ).not.toThrow();
  333. expect(registry.get('a')).toEqual('testValue2');
  334. expect(console.warn).toHaveBeenCalled();
  335. restoreConsole();
  336. });
  337. });
  338. });
  339. describe('=PROHIBIT', () => {
  340. describe('.registerValue(key, value)', () => {
  341. it('throws error when overwrite', () => {
  342. const registry = new Registry({
  343. overwritePolicy: OverwritePolicy.Prohibit,
  344. });
  345. registry.registerValue('a', 'testValue');
  346. expect(() => registry.registerValue('a', 'testValue2')).toThrow();
  347. });
  348. });
  349. describe('.registerLoader(key, loader)', () => {
  350. it('warns when overwrite', () => {
  351. const registry = new Registry({
  352. overwritePolicy: OverwritePolicy.Prohibit,
  353. });
  354. registry.registerLoader('a', () => 'testValue');
  355. expect(() =>
  356. registry.registerLoader('a', () => 'testValue2'),
  357. ).toThrow();
  358. });
  359. });
  360. });
  361. });
  362. describe('listeners', () => {
  363. let registry = new Registry();
  364. let listener = jest.fn();
  365. beforeEach(() => {
  366. registry = new Registry();
  367. listener = jest.fn();
  368. registry.addListener(listener);
  369. });
  370. it('calls the listener when a value is registered', () => {
  371. registry.registerValue('foo', 'bar');
  372. expect(listener).toHaveBeenCalledWith(['foo']);
  373. });
  374. it('calls the listener when a loader is registered', () => {
  375. registry.registerLoader('foo', () => 'bar');
  376. expect(listener).toHaveBeenCalledWith(['foo']);
  377. });
  378. it('calls the listener when a value is overridden', () => {
  379. registry.registerValue('foo', 'bar');
  380. listener.mockClear();
  381. registry.registerValue('foo', 'baz');
  382. expect(listener).toHaveBeenCalledWith(['foo']);
  383. });
  384. it('calls the listener when a value is removed', () => {
  385. registry.registerValue('foo', 'bar');
  386. listener.mockClear();
  387. registry.remove('foo');
  388. expect(listener).toHaveBeenCalledWith(['foo']);
  389. });
  390. it('does not call the listener when a value is not actually removed', () => {
  391. registry.remove('foo');
  392. expect(listener).not.toHaveBeenCalled();
  393. });
  394. it('calls the listener when registry is cleared', () => {
  395. registry.registerValue('foo', 'bar');
  396. registry.registerLoader('fluz', () => 'baz');
  397. listener.mockClear();
  398. registry.clear();
  399. expect(listener).toHaveBeenCalledWith(['foo', 'fluz']);
  400. });
  401. it('removes listeners correctly', () => {
  402. registry.removeListener(listener);
  403. registry.registerValue('foo', 'bar');
  404. expect(listener).not.toHaveBeenCalled();
  405. });
  406. describe('with a broken listener', () => {
  407. let restoreConsole: any;
  408. beforeEach(() => {
  409. restoreConsole = mockConsole();
  410. });
  411. afterEach(() => {
  412. restoreConsole();
  413. });
  414. it('keeps working', () => {
  415. const errorListener = jest.fn().mockImplementation(() => {
  416. throw new Error('test error');
  417. });
  418. const lastListener = jest.fn();
  419. registry.addListener(errorListener);
  420. registry.addListener(lastListener);
  421. registry.registerValue('foo', 'bar');
  422. expect(listener).toHaveBeenCalledWith(['foo']);
  423. expect(errorListener).toHaveBeenCalledWith(['foo']);
  424. expect(lastListener).toHaveBeenCalledWith(['foo']);
  425. expect(console.error).toHaveBeenCalled();
  426. });
  427. });
  428. });
  429. });