repluggable-core 1.302.66 → 1.302.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/__tests__/spec-setup.js +3 -0
  2. package/dist/es/{API.d.ts → src/API.d.ts} +46 -8
  3. package/dist/es/src/API.d.ts.map +1 -0
  4. package/dist/es/src/API.js +3 -0
  5. package/dist/es/src/API.js.map +1 -0
  6. package/dist/es/src/IterableWeakMap.d.ts +20 -0
  7. package/dist/es/src/IterableWeakMap.d.ts.map +1 -0
  8. package/dist/es/src/IterableWeakMap.js +92 -0
  9. package/dist/es/src/IterableWeakMap.js.map +1 -0
  10. package/dist/es/src/__internal.d.ts.map +1 -0
  11. package/dist/es/src/__internal.js +5 -0
  12. package/dist/es/src/__internal.js.map +1 -0
  13. package/dist/es/src/appHost.d.ts +7 -0
  14. package/dist/es/src/appHost.d.ts.map +1 -0
  15. package/dist/es/src/appHost.js +898 -0
  16. package/dist/es/src/appHost.js.map +1 -0
  17. package/dist/es/src/appHostServices.d.ts +12 -0
  18. package/dist/es/src/appHostServices.d.ts.map +1 -0
  19. package/dist/es/src/appHostServices.js +25 -0
  20. package/dist/es/src/appHostServices.js.map +1 -0
  21. package/dist/es/src/appHostUtils.d.ts +4 -0
  22. package/dist/es/src/appHostUtils.d.ts.map +1 -0
  23. package/dist/es/src/appHostUtils.js +8 -0
  24. package/dist/es/src/appHostUtils.js.map +1 -0
  25. package/dist/es/src/extensionSlot.d.ts +8 -0
  26. package/dist/es/src/extensionSlot.d.ts.map +1 -0
  27. package/dist/es/src/extensionSlot.js +59 -0
  28. package/dist/es/src/extensionSlot.js.map +1 -0
  29. package/dist/es/src/hot.d.ts +14 -0
  30. package/dist/es/src/hot.d.ts.map +1 -0
  31. package/dist/es/src/hot.js +52 -0
  32. package/dist/es/src/hot.js.map +1 -0
  33. package/dist/es/src/index.d.ts +11 -0
  34. package/dist/es/src/index.d.ts.map +1 -0
  35. package/dist/es/src/index.js +10 -0
  36. package/dist/es/src/index.js.map +1 -0
  37. package/dist/es/src/installedShellsState.d.ts +19 -0
  38. package/dist/es/src/installedShellsState.d.ts.map +1 -0
  39. package/dist/es/src/installedShellsState.js +30 -0
  40. package/dist/es/src/installedShellsState.js.map +1 -0
  41. package/dist/es/src/interceptAnyObject.d.ts +7 -0
  42. package/dist/es/src/interceptAnyObject.d.ts.map +1 -0
  43. package/dist/es/src/interceptAnyObject.js +21 -0
  44. package/dist/es/src/interceptAnyObject.js.map +1 -0
  45. package/dist/es/src/invokeSlotCallbacks.d.ts +3 -0
  46. package/dist/es/src/invokeSlotCallbacks.d.ts.map +1 -0
  47. package/dist/es/src/invokeSlotCallbacks.js +20 -0
  48. package/dist/es/src/invokeSlotCallbacks.js.map +1 -0
  49. package/dist/es/src/loggers.d.ts +4 -0
  50. package/dist/es/src/loggers.d.ts.map +1 -0
  51. package/dist/es/src/loggers.js +100 -0
  52. package/dist/es/src/loggers.js.map +1 -0
  53. package/dist/es/src/monitorAPI.d.ts +3 -0
  54. package/dist/es/src/monitorAPI.d.ts.map +1 -0
  55. package/dist/es/src/monitorAPI.js +53 -0
  56. package/dist/es/src/monitorAPI.js.map +1 -0
  57. package/dist/es/src/repluggableAppDebug/index.d.ts +2 -0
  58. package/dist/es/src/repluggableAppDebug/index.d.ts.map +1 -0
  59. package/dist/es/src/repluggableAppDebug/index.js +2 -0
  60. package/dist/es/src/repluggableAppDebug/index.js.map +1 -0
  61. package/dist/es/src/repluggableAppDebug/performanceDebugInfo.d.ts +15 -0
  62. package/dist/es/src/repluggableAppDebug/performanceDebugInfo.d.ts.map +1 -0
  63. package/dist/es/src/repluggableAppDebug/performanceDebugInfo.js +94 -0
  64. package/dist/es/src/repluggableAppDebug/performanceDebugInfo.js.map +1 -0
  65. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.d.ts +24 -0
  66. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.d.ts.map +1 -0
  67. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.js +132 -0
  68. package/dist/es/src/repluggableAppDebug/repluggableAppDebug.js.map +1 -0
  69. package/dist/es/src/tarjanGraph.d.ts +27 -0
  70. package/dist/es/src/tarjanGraph.d.ts.map +1 -0
  71. package/dist/es/src/tarjanGraph.js +94 -0
  72. package/dist/es/src/tarjanGraph.js.map +1 -0
  73. package/dist/es/src/throttledStore.d.ts +32 -0
  74. package/dist/es/src/throttledStore.d.ts.map +1 -0
  75. package/dist/es/src/throttledStore.js +240 -0
  76. package/dist/es/src/throttledStore.js.map +1 -0
  77. package/dist/es/test/IterableWeakMap.spec.d.ts +2 -0
  78. package/dist/es/test/IterableWeakMap.spec.d.ts.map +1 -0
  79. package/dist/es/test/IterableWeakMap.spec.js +218 -0
  80. package/dist/es/test/IterableWeakMap.spec.js.map +1 -0
  81. package/dist/es/test/appHost.mock.d.ts +4 -0
  82. package/dist/es/test/appHost.mock.d.ts.map +1 -0
  83. package/dist/es/test/appHost.mock.js +54 -0
  84. package/dist/es/test/appHost.mock.js.map +1 -0
  85. package/dist/es/test/appHost.spec.d.ts +2 -0
  86. package/dist/es/test/appHost.spec.d.ts.map +1 -0
  87. package/dist/es/test/appHost.spec.js +1451 -0
  88. package/dist/es/test/appHost.spec.js.map +1 -0
  89. package/dist/es/test/installedShellsState.spec.d.ts +2 -0
  90. package/dist/es/test/installedShellsState.spec.d.ts.map +1 -0
  91. package/dist/es/test/installedShellsState.spec.js +28 -0
  92. package/dist/es/test/installedShellsState.spec.js.map +1 -0
  93. package/dist/es/test/interceptAnyObject.spec.d.ts +2 -0
  94. package/dist/es/test/interceptAnyObject.spec.d.ts.map +1 -0
  95. package/dist/es/test/interceptAnyObject.spec.js +166 -0
  96. package/dist/es/test/interceptAnyObject.spec.js.map +1 -0
  97. package/dist/es/test/loggers.spec.d.ts +2 -0
  98. package/dist/es/test/loggers.spec.d.ts.map +1 -0
  99. package/dist/es/test/loggers.spec.js +218 -0
  100. package/dist/es/test/loggers.spec.js.map +1 -0
  101. package/dist/es/test/repluggableAppDebug.spec.d.ts +2 -0
  102. package/dist/es/test/repluggableAppDebug.spec.d.ts.map +1 -0
  103. package/dist/es/test/repluggableAppDebug.spec.js +107 -0
  104. package/dist/es/test/repluggableAppDebug.spec.js.map +1 -0
  105. package/dist/es/test/testKit.spec.d.ts +2 -0
  106. package/dist/es/test/testKit.spec.d.ts.map +1 -0
  107. package/dist/es/test/testKit.spec.js +129 -0
  108. package/dist/es/test/testKit.spec.js.map +1 -0
  109. package/dist/es/testKit/emptyLoggerOptions.d.ts +3 -0
  110. package/dist/es/testKit/emptyLoggerOptions.d.ts.map +1 -0
  111. package/dist/es/testKit/emptyLoggerOptions.js +15 -0
  112. package/dist/es/testKit/emptyLoggerOptions.js.map +1 -0
  113. package/dist/es/testKit/index.d.ts +27 -0
  114. package/dist/es/testKit/index.d.ts.map +1 -0
  115. package/dist/es/testKit/index.js +132 -0
  116. package/dist/es/testKit/index.js.map +1 -0
  117. package/dist/es/testKit/mockPackage.d.ts +23 -0
  118. package/dist/es/testKit/mockPackage.d.ts.map +1 -0
  119. package/dist/es/testKit/mockPackage.js +95 -0
  120. package/dist/es/testKit/mockPackage.js.map +1 -0
  121. package/dist/es/testKit/withConsoleErrors.d.ts +2 -0
  122. package/dist/es/testKit/withConsoleErrors.d.ts.map +1 -0
  123. package/dist/es/testKit/withConsoleErrors.js +12 -0
  124. package/dist/es/testKit/withConsoleErrors.js.map +1 -0
  125. package/dist/es/testKit/withThrowOnError.d.ts +3 -0
  126. package/dist/es/testKit/withThrowOnError.d.ts.map +1 -0
  127. package/dist/es/testKit/withThrowOnError.js +27 -0
  128. package/dist/es/testKit/withThrowOnError.js.map +1 -0
  129. package/dist/src/API.d.ts +46 -8
  130. package/dist/src/API.d.ts.map +1 -1
  131. package/dist/src/API.js +4 -0
  132. package/dist/src/API.js.map +1 -1
  133. package/dist/src/IterableWeakMap.d.ts +20 -0
  134. package/dist/src/IterableWeakMap.d.ts.map +1 -0
  135. package/dist/src/IterableWeakMap.js +96 -0
  136. package/dist/src/IterableWeakMap.js.map +1 -0
  137. package/dist/src/__internal.d.ts.map +1 -1
  138. package/dist/src/__internal.js +1 -1
  139. package/dist/src/__internal.js.map +1 -1
  140. package/dist/src/appHost.d.ts +7 -0
  141. package/dist/src/appHost.d.ts.map +1 -0
  142. package/dist/src/appHost.js +906 -0
  143. package/dist/src/appHost.js.map +1 -0
  144. package/dist/src/appHostServices.d.ts +12 -0
  145. package/dist/src/appHostServices.d.ts.map +1 -0
  146. package/dist/src/appHostServices.js +29 -0
  147. package/dist/src/appHostServices.js.map +1 -0
  148. package/dist/src/appHostUtils.d.ts +4 -0
  149. package/dist/src/appHostUtils.d.ts.map +1 -0
  150. package/dist/src/appHostUtils.js +16 -0
  151. package/dist/src/appHostUtils.js.map +1 -0
  152. package/dist/src/extensionSlot.d.ts +8 -0
  153. package/dist/src/extensionSlot.d.ts.map +1 -0
  154. package/dist/src/extensionSlot.js +67 -0
  155. package/dist/src/extensionSlot.js.map +1 -0
  156. package/dist/src/hot.d.ts +14 -0
  157. package/dist/src/hot.d.ts.map +1 -0
  158. package/dist/src/hot.js +56 -0
  159. package/dist/src/hot.js.map +1 -0
  160. package/dist/src/index.d.ts +10 -2
  161. package/dist/src/index.d.ts.map +1 -1
  162. package/dist/src/index.js +22 -15
  163. package/dist/src/index.js.map +1 -1
  164. package/dist/src/installedShellsState.d.ts +19 -0
  165. package/dist/src/installedShellsState.d.ts.map +1 -0
  166. package/dist/src/installedShellsState.js +38 -0
  167. package/dist/src/installedShellsState.js.map +1 -0
  168. package/dist/src/interceptAnyObject.d.ts +7 -0
  169. package/dist/src/interceptAnyObject.d.ts.map +1 -0
  170. package/dist/src/interceptAnyObject.js +28 -0
  171. package/dist/src/interceptAnyObject.js.map +1 -0
  172. package/dist/src/invokeSlotCallbacks.d.ts +3 -0
  173. package/dist/src/invokeSlotCallbacks.d.ts.map +1 -0
  174. package/dist/src/invokeSlotCallbacks.js +24 -0
  175. package/dist/src/invokeSlotCallbacks.js.map +1 -0
  176. package/dist/src/loggers.d.ts +4 -0
  177. package/dist/src/loggers.d.ts.map +1 -0
  178. package/dist/src/loggers.js +104 -0
  179. package/dist/src/loggers.js.map +1 -0
  180. package/dist/src/monitorAPI.d.ts +3 -0
  181. package/dist/src/monitorAPI.d.ts.map +1 -0
  182. package/dist/src/monitorAPI.js +57 -0
  183. package/dist/src/monitorAPI.js.map +1 -0
  184. package/dist/src/repluggableAppDebug/index.d.ts +2 -0
  185. package/dist/src/repluggableAppDebug/index.d.ts.map +1 -0
  186. package/dist/src/repluggableAppDebug/index.js +6 -0
  187. package/dist/src/repluggableAppDebug/index.js.map +1 -0
  188. package/dist/src/repluggableAppDebug/performanceDebugInfo.d.ts +15 -0
  189. package/dist/src/repluggableAppDebug/performanceDebugInfo.d.ts.map +1 -0
  190. package/dist/src/repluggableAppDebug/performanceDebugInfo.js +101 -0
  191. package/dist/src/repluggableAppDebug/performanceDebugInfo.js.map +1 -0
  192. package/dist/src/repluggableAppDebug/repluggableAppDebug.d.ts +24 -0
  193. package/dist/src/repluggableAppDebug/repluggableAppDebug.d.ts.map +1 -0
  194. package/dist/src/repluggableAppDebug/repluggableAppDebug.js +139 -0
  195. package/dist/src/repluggableAppDebug/repluggableAppDebug.js.map +1 -0
  196. package/dist/src/tarjanGraph.d.ts +27 -0
  197. package/dist/src/tarjanGraph.d.ts.map +1 -0
  198. package/dist/src/tarjanGraph.js +100 -0
  199. package/dist/src/tarjanGraph.js.map +1 -0
  200. package/dist/src/throttledStore.d.ts +32 -0
  201. package/dist/src/throttledStore.d.ts.map +1 -0
  202. package/dist/src/throttledStore.js +249 -0
  203. package/dist/src/throttledStore.js.map +1 -0
  204. package/dist/test/IterableWeakMap.spec.d.ts +2 -0
  205. package/dist/test/IterableWeakMap.spec.d.ts.map +1 -0
  206. package/dist/test/IterableWeakMap.spec.js +220 -0
  207. package/dist/test/IterableWeakMap.spec.js.map +1 -0
  208. package/dist/test/appHost.mock.d.ts +4 -0
  209. package/dist/test/appHost.mock.d.ts.map +1 -0
  210. package/dist/test/appHost.mock.js +59 -0
  211. package/dist/test/appHost.mock.js.map +1 -0
  212. package/dist/test/appHost.spec.d.ts +2 -0
  213. package/dist/test/appHost.spec.d.ts.map +1 -0
  214. package/dist/test/appHost.spec.js +1456 -0
  215. package/dist/test/appHost.spec.js.map +1 -0
  216. package/dist/test/installedShellsState.spec.d.ts +2 -0
  217. package/dist/test/installedShellsState.spec.d.ts.map +1 -0
  218. package/dist/test/installedShellsState.spec.js +30 -0
  219. package/dist/test/installedShellsState.spec.js.map +1 -0
  220. package/dist/test/interceptAnyObject.spec.d.ts +2 -0
  221. package/dist/test/interceptAnyObject.spec.d.ts.map +1 -0
  222. package/dist/test/interceptAnyObject.spec.js +171 -0
  223. package/dist/test/interceptAnyObject.spec.js.map +1 -0
  224. package/dist/test/loggers.spec.d.ts +2 -0
  225. package/dist/test/loggers.spec.d.ts.map +1 -0
  226. package/dist/test/loggers.spec.js +220 -0
  227. package/dist/test/loggers.spec.js.map +1 -0
  228. package/dist/test/repluggableAppDebug.spec.d.ts +2 -0
  229. package/dist/test/repluggableAppDebug.spec.d.ts.map +1 -0
  230. package/dist/test/repluggableAppDebug.spec.js +109 -0
  231. package/dist/test/repluggableAppDebug.spec.js.map +1 -0
  232. package/dist/test/testKit.spec.d.ts +2 -0
  233. package/dist/test/testKit.spec.d.ts.map +1 -0
  234. package/dist/test/testKit.spec.js +134 -0
  235. package/dist/test/testKit.spec.js.map +1 -0
  236. package/dist/testKit/emptyLoggerOptions.d.ts +3 -0
  237. package/dist/testKit/emptyLoggerOptions.d.ts.map +1 -0
  238. package/dist/testKit/emptyLoggerOptions.js +18 -0
  239. package/dist/testKit/emptyLoggerOptions.js.map +1 -0
  240. package/dist/testKit/index.d.ts +27 -0
  241. package/dist/testKit/index.d.ts.map +1 -0
  242. package/dist/testKit/index.js +160 -0
  243. package/dist/testKit/index.js.map +1 -0
  244. package/dist/testKit/mockPackage.d.ts +23 -0
  245. package/dist/testKit/mockPackage.d.ts.map +1 -0
  246. package/dist/testKit/mockPackage.js +101 -0
  247. package/dist/testKit/mockPackage.js.map +1 -0
  248. package/dist/testKit/withConsoleErrors.d.ts +2 -0
  249. package/dist/testKit/withConsoleErrors.d.ts.map +1 -0
  250. package/dist/testKit/withConsoleErrors.js +16 -0
  251. package/dist/testKit/withConsoleErrors.js.map +1 -0
  252. package/dist/testKit/withThrowOnError.d.ts +3 -0
  253. package/dist/testKit/withThrowOnError.d.ts.map +1 -0
  254. package/dist/testKit/withThrowOnError.js +31 -0
  255. package/dist/testKit/withThrowOnError.js.map +1 -0
  256. package/jest.config.js +16 -0
  257. package/package.json +21 -3
  258. package/src/API.ts +47 -9
  259. package/src/IterableWeakMap.ts +123 -0
  260. package/src/__internal.ts +1 -1
  261. package/src/appHost.ts +1222 -0
  262. package/src/appHostServices.ts +42 -0
  263. package/src/appHostUtils.ts +11 -0
  264. package/src/extensionSlot.ts +96 -0
  265. package/src/hot.ts +53 -0
  266. package/src/index.ts +42 -2
  267. package/src/installedShellsState.ts +54 -0
  268. package/src/interceptAnyObject.ts +39 -0
  269. package/src/invokeSlotCallbacks.ts +20 -0
  270. package/src/loggers.ts +112 -0
  271. package/src/monitorAPI.ts +71 -0
  272. package/src/repluggableAppDebug/debug.d.ts +37 -0
  273. package/src/repluggableAppDebug/index.ts +1 -0
  274. package/src/repluggableAppDebug/performanceDebugInfo.ts +96 -0
  275. package/src/repluggableAppDebug/repluggableAppDebug.ts +192 -0
  276. package/src/tarjanGraph.ts +96 -0
  277. package/src/throttledStore.ts +355 -0
  278. package/test/IterableWeakMap.spec.ts +280 -0
  279. package/test/appHost.mock.ts +58 -0
  280. package/test/appHost.spec.ts +1765 -0
  281. package/test/installedShellsState.spec.ts +39 -0
  282. package/test/interceptAnyObject.spec.ts +217 -0
  283. package/test/loggers.spec.ts +288 -0
  284. package/test/repluggableAppDebug.spec.ts +118 -0
  285. package/test/testKit.spec.tsx +153 -0
  286. package/testKit/emptyLoggerOptions.ts +16 -0
  287. package/testKit/index.ts +170 -0
  288. package/testKit/mockPackage.ts +110 -0
  289. package/testKit/package.json +3 -0
  290. package/testKit/withConsoleErrors.ts +12 -0
  291. package/testKit/withThrowOnError.ts +34 -0
  292. package/tsconfig.json +9 -2
  293. package/dist/es/API.d.ts.map +0 -1
  294. package/dist/es/API.js +0 -2
  295. package/dist/es/API.js.map +0 -1
  296. package/dist/es/__internal.d.ts.map +0 -1
  297. package/dist/es/__internal.js +0 -5
  298. package/dist/es/__internal.js.map +0 -1
  299. package/dist/es/index.d.ts +0 -3
  300. package/dist/es/index.d.ts.map +0 -1
  301. package/dist/es/index.js +0 -2
  302. package/dist/es/index.js.map +0 -1
  303. /package/dist/es/{__internal.d.ts → src/__internal.d.ts} +0 -0
@@ -0,0 +1,1451 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import _ from 'lodash';
11
+ import { createAppHost, mainViewSlotKey, makeLazyEntryPoint, stateSlotKey } from '../src/appHost';
12
+ import { addMockShell, asyncLoadMockPackage, dependsOnMockPackageEntryPoint, emptyLoggerOptions, MockAPI, mockPackage, mockPackageWithPublicAPI, mockPackageWithSlot, MockPublicAPI, mockShellInitialState, mockShellStateKey, MockSlot } from '../testKit';
13
+ import { AppHostAPI, AppHostServicesEntryPointName } from '../src/appHostServices';
14
+ import { ConsoleHostLogger } from '../src/loggers';
15
+ import { createCircularEntryPoints, createDirectCircularEntryPoints } from './appHost.mock';
16
+ const testHostOptions = {
17
+ monitoring: { disableMonitoring: true }
18
+ };
19
+ const createHostWithDependantPackages = (DependencyAPI) => {
20
+ const MockAPI2 = { name: 'Mock-API-2' };
21
+ const dependentPackage = [
22
+ {
23
+ name: 'DEPENDENT_MOCK_ENTRY_POINT_1',
24
+ getDependencyAPIs() {
25
+ return [DependencyAPI];
26
+ }
27
+ },
28
+ {
29
+ name: 'DEPENDENT_MOCK_ENTRY_POINT_2',
30
+ getDependencyAPIs() {
31
+ return [DependencyAPI];
32
+ },
33
+ declareAPIs() {
34
+ return [MockAPI2];
35
+ },
36
+ attach(shell) {
37
+ shell.contributeAPI(MockAPI2, () => ({}));
38
+ }
39
+ }
40
+ ];
41
+ const deeplyDependentPackage = [
42
+ {
43
+ name: 'DEPENDENT_MOCK_ENTRY_POINT_3',
44
+ getDependencyAPIs() {
45
+ return [MockAPI2];
46
+ }
47
+ }
48
+ ];
49
+ let getHelperShell = () => {
50
+ throw new Error();
51
+ };
52
+ const helperEntryPoint = {
53
+ name: 'TEST_HELPER',
54
+ declareAPIs() {
55
+ return [DependencyAPI];
56
+ },
57
+ attach(shell) {
58
+ getHelperShell = () => shell;
59
+ }
60
+ };
61
+ return {
62
+ host: createAppHost([dependentPackage, deeplyDependentPackage, helperEntryPoint], testHostOptions),
63
+ dependentPackage,
64
+ deeplyDependentPackage,
65
+ helperShell: getHelperShell()
66
+ };
67
+ };
68
+ describe('App Host', () => {
69
+ beforeEach(() => {
70
+ spyOn(ConsoleHostLogger, 'log');
71
+ });
72
+ it('should create an app host', () => {
73
+ const host = createAppHost([], testHostOptions);
74
+ expect(host).toBeInstanceOf(Object);
75
+ });
76
+ describe('AppHost Options', () => {
77
+ it('should use ConsoleHostLogger by default', () => {
78
+ const host = createAppHost([], testHostOptions);
79
+ expect(host.log).toBe(ConsoleHostLogger);
80
+ });
81
+ it('should use custom host logger if specified', () => {
82
+ const logger = {
83
+ log() { },
84
+ spanRoot() {
85
+ return {
86
+ end() { }
87
+ };
88
+ },
89
+ spanChild() {
90
+ return {
91
+ end() { }
92
+ };
93
+ }
94
+ };
95
+ const options = {
96
+ logger,
97
+ monitoring: {}
98
+ };
99
+ const host = createAppHost([], options);
100
+ expect(host.log).toBe(logger);
101
+ });
102
+ });
103
+ describe('Packages Installation', () => {
104
+ it('should NOT throw on circular dependency if check is disabled in host options', () => {
105
+ const circularPackages = createDirectCircularEntryPoints();
106
+ const hostOptionsWithDisabledCircularCheck = {
107
+ monitoring: {},
108
+ disableCheckCircularDependencies: true
109
+ };
110
+ expect(() => createAppHost(circularPackages, hostOptionsWithDisabledCircularCheck)).not.toThrow();
111
+ });
112
+ it('should throw on direct circular API dependency (private keys)', () => {
113
+ const circularPackages = createDirectCircularEntryPoints();
114
+ expect(() => createAppHost(circularPackages, testHostOptions)).toThrowError();
115
+ });
116
+ it('should throw on direct circular API dependency (public keys)', () => {
117
+ const circularPackages = createDirectCircularEntryPoints(true);
118
+ expect(() => createAppHost(circularPackages, testHostOptions)).toThrowError();
119
+ });
120
+ it('should throw on circular API dependency (private keys)', () => {
121
+ const circularPackages = createCircularEntryPoints();
122
+ expect(() => createAppHost(circularPackages, testHostOptions)).toThrowError();
123
+ });
124
+ it('should throw on circular API dependency (public keys)', () => {
125
+ const circularPackages = createCircularEntryPoints(true);
126
+ expect(() => createAppHost(circularPackages, testHostOptions)).toThrowError();
127
+ });
128
+ it('should throw when dynamically adding a shell with circular dependency', () => {
129
+ const circularPackages = createCircularEntryPoints(true);
130
+ const nonCircular = circularPackages.slice(0, 3);
131
+ const circularEP = _.last(circularPackages);
132
+ const host = createAppHost(nonCircular, testHostOptions);
133
+ expect(() => host.addShells([circularEP])).toThrow();
134
+ });
135
+ it('should install initial packages', () => {
136
+ const host = createAppHost([mockPackage], testHostOptions);
137
+ expect(host.hasShell(mockPackage.name)).toBe(true);
138
+ });
139
+ it('should install packages after initial installations', () => __awaiter(void 0, void 0, void 0, function* () {
140
+ const host = createAppHost([], testHostOptions);
141
+ expect(host.hasShell(mockPackage.name)).toBe(false);
142
+ yield host.addShells([mockPackage]);
143
+ expect(host.hasShell(mockPackage.name)).toBe(true);
144
+ }));
145
+ it('should uninstall shell', () => __awaiter(void 0, void 0, void 0, function* () {
146
+ const host = createAppHost([mockPackage], testHostOptions);
147
+ yield host.removeShells([mockPackage.name]);
148
+ expect(host.hasShell(mockPackage.name)).toBe(false);
149
+ }));
150
+ it('should not install multiple shells with the same name', () => {
151
+ expect(() => createAppHost([mockPackage, _.pick(mockPackage, 'name')], testHostOptions)).toThrow();
152
+ });
153
+ it('should install lazy shells', () => {
154
+ const lazyEntryPoint = makeLazyEntryPoint(mockPackage.name, () => __awaiter(void 0, void 0, void 0, function* () { return mockPackage; }));
155
+ const host = createAppHost([lazyEntryPoint], testHostOptions);
156
+ expect(host.hasShell(lazyEntryPoint.name)).toBe(true);
157
+ });
158
+ });
159
+ describe('EntryPoint lifecycle state', () => {
160
+ const takeEntryPointStateSnapshot = (shell) => {
161
+ return {
162
+ canUseStore: shell.canUseStore(),
163
+ canUseAPIs: shell.canUseAPIs(),
164
+ wasInitializationCompleted: shell.wasInitializationCompleted()
165
+ };
166
+ };
167
+ it('should be incomplete during appHost initialization', () => {
168
+ let stateInAttach;
169
+ let stateInExtend;
170
+ const entryPoint = {
171
+ name: 'TEST_EP',
172
+ attach(shell) {
173
+ stateInAttach = takeEntryPointStateSnapshot(shell);
174
+ },
175
+ extend(shell) {
176
+ stateInExtend = takeEntryPointStateSnapshot(shell);
177
+ }
178
+ };
179
+ createAppHost([entryPoint], testHostOptions);
180
+ expect(stateInAttach).toMatchObject({
181
+ wasInitializationCompleted: false,
182
+ canUseAPIs: false,
183
+ canUseStore: false
184
+ });
185
+ expect(stateInExtend).toMatchObject({
186
+ wasInitializationCompleted: false,
187
+ canUseAPIs: true,
188
+ canUseStore: true
189
+ });
190
+ });
191
+ it('should be complete after appHost initialization', () => {
192
+ let shell;
193
+ const entryPoint = {
194
+ name: 'TEST_EP',
195
+ attach(_shell) {
196
+ shell = _shell;
197
+ }
198
+ };
199
+ createAppHost([entryPoint], testHostOptions);
200
+ const stateAfter = shell && takeEntryPointStateSnapshot(shell);
201
+ expect(stateAfter).toMatchObject({
202
+ wasInitializationCompleted: true,
203
+ canUseAPIs: true,
204
+ canUseStore: true
205
+ });
206
+ });
207
+ it('should be incomplete during lifecycle of added entry point', () => {
208
+ let stateInAttach;
209
+ let stateInExtend;
210
+ const host = createAppHost([], testHostOptions);
211
+ addMockShell(host, {
212
+ attach(shell) {
213
+ stateInAttach = takeEntryPointStateSnapshot(shell);
214
+ },
215
+ extend(shell) {
216
+ stateInExtend = takeEntryPointStateSnapshot(shell);
217
+ }
218
+ });
219
+ expect(stateInAttach).toMatchObject({
220
+ wasInitializationCompleted: false,
221
+ canUseAPIs: false,
222
+ canUseStore: false
223
+ });
224
+ expect(stateInExtend).toMatchObject({
225
+ wasInitializationCompleted: false,
226
+ canUseAPIs: true,
227
+ canUseStore: true
228
+ });
229
+ });
230
+ it('should be complete after lifecycle of added entry point', () => {
231
+ const host = createAppHost([], testHostOptions);
232
+ const shell = addMockShell(host);
233
+ const stateAfter = takeEntryPointStateSnapshot(shell);
234
+ expect(stateAfter).toMatchObject({
235
+ wasInitializationCompleted: true,
236
+ canUseAPIs: true,
237
+ canUseStore: true
238
+ });
239
+ });
240
+ it('should be incomplete during execution of late initializer', () => {
241
+ let state;
242
+ const host = createAppHost([], testHostOptions);
243
+ const shell = addMockShell(host);
244
+ expect(shell.wasInitializationCompleted()).toBe(true);
245
+ shell.runLateInitializer(() => {
246
+ state = takeEntryPointStateSnapshot(shell);
247
+ });
248
+ expect(state).toMatchObject({
249
+ canUseStore: true,
250
+ canUseAPIs: true,
251
+ wasInitializationCompleted: false
252
+ });
253
+ });
254
+ it('should be complete after execution of late initializer', () => {
255
+ const host = createAppHost([], testHostOptions);
256
+ const shell = addMockShell(host);
257
+ expect(shell.wasInitializationCompleted()).toBe(true);
258
+ shell.runLateInitializer(() => { });
259
+ const stateAfter = takeEntryPointStateSnapshot(shell);
260
+ expect(stateAfter).toMatchObject({
261
+ canUseStore: true,
262
+ canUseAPIs: true,
263
+ wasInitializationCompleted: true
264
+ });
265
+ });
266
+ it('should be complete after execution of late initializer that throws', () => {
267
+ const host = createAppHost([], testHostOptions);
268
+ const shell = addMockShell(host);
269
+ expect(shell.wasInitializationCompleted()).toBe(true);
270
+ expect(() => {
271
+ shell.runLateInitializer(() => {
272
+ throw new Error('TEST-ERROR');
273
+ });
274
+ }).toThrow('TEST-ERROR');
275
+ const stateAfter = takeEntryPointStateSnapshot(shell);
276
+ expect(stateAfter).toMatchObject({
277
+ canUseStore: true,
278
+ canUseAPIs: true,
279
+ wasInitializationCompleted: true
280
+ });
281
+ });
282
+ });
283
+ _.forEach([
284
+ {
285
+ testCase: 'private API keys',
286
+ dependencyAPI: MockAPI,
287
+ providerPackage: mockPackage
288
+ },
289
+ {
290
+ testCase: 'public API keys',
291
+ dependencyAPI: { name: MockPublicAPI.name, public: true },
292
+ providerPackage: mockPackageWithPublicAPI
293
+ }
294
+ ], ({ testCase, dependencyAPI, providerPackage }) => {
295
+ describe(`Dependency entry point installation (${testCase})`, () => {
296
+ it('should not install dependent entry point until dependency is installed', () => __awaiter(void 0, void 0, void 0, function* () {
297
+ const { host, dependentPackage } = createHostWithDependantPackages(dependencyAPI);
298
+ expect(host.hasShell(dependentPackage[0].name)).toBe(false);
299
+ yield host.addShells([providerPackage]);
300
+ expect(host.hasShell(dependentPackage[0].name)).toBe(true);
301
+ }));
302
+ it('should install all dependent entry points chain when dependencies are installed from entry point', () => __awaiter(void 0, void 0, void 0, function* () {
303
+ const { host, dependentPackage, deeplyDependentPackage } = createHostWithDependantPackages(dependencyAPI);
304
+ expect(host.hasShell(dependentPackage[0].name)).toBe(false);
305
+ expect(host.hasShell(dependentPackage[1].name)).toBe(false);
306
+ expect(host.hasShell(deeplyDependentPackage[0].name)).toBe(false);
307
+ yield host.addShells([providerPackage]);
308
+ expect(host.hasShell(dependentPackage[0].name)).toBe(true);
309
+ expect(host.hasShell(dependentPackage[1].name)).toBe(true);
310
+ expect(host.hasShell(deeplyDependentPackage[0].name)).toBe(true);
311
+ }));
312
+ it('should install all dependent entry points chain when dependencies are installed outside of entry point', () => __awaiter(void 0, void 0, void 0, function* () {
313
+ const { host, dependentPackage, deeplyDependentPackage, helperShell } = createHostWithDependantPackages(dependencyAPI);
314
+ expect(host.hasShell(dependentPackage[0].name)).toBe(false);
315
+ expect(host.hasShell(dependentPackage[1].name)).toBe(false);
316
+ expect(host.hasShell(deeplyDependentPackage[0].name)).toBe(false);
317
+ helperShell.contributeAPI(dependencyAPI, () => ({
318
+ stubTrue: () => true,
319
+ getNewObject: () => ({})
320
+ }));
321
+ expect(host.hasShell(dependentPackage[0].name)).toBe(true);
322
+ expect(host.hasShell(dependentPackage[1].name)).toBe(true);
323
+ expect(host.hasShell(deeplyDependentPackage[0].name)).toBe(true);
324
+ }));
325
+ it('should uninstall all dependent entry points chain when dependencies are uninstalled', () => __awaiter(void 0, void 0, void 0, function* () {
326
+ const { host, dependentPackage, deeplyDependentPackage } = createHostWithDependantPackages(dependencyAPI);
327
+ yield host.addShells([providerPackage]);
328
+ yield host.removeShells([providerPackage.name]);
329
+ expect(host.hasShell(dependentPackage[0].name)).toBe(false);
330
+ expect(host.hasShell(dependentPackage[1].name)).toBe(false);
331
+ expect(host.hasShell(deeplyDependentPackage[0].name)).toBe(false);
332
+ }));
333
+ });
334
+ });
335
+ describe('Host extension slots', () => {
336
+ describe('hasSlot', () => {
337
+ it('should return false for hasSlot if the slot is not defined on the host', () => {
338
+ const host = createAppHost([], testHostOptions);
339
+ expect(host.hasSlot(MockSlot)).toBeFalsy();
340
+ });
341
+ it('should return true for hasSlot if the slot is defined on the host', () => {
342
+ const host = createAppHost([mockPackageWithSlot], testHostOptions);
343
+ expect(host.hasSlot(MockSlot)).toBeTruthy();
344
+ });
345
+ });
346
+ it('should have a state extension slot', () => {
347
+ const host = createAppHost([], testHostOptions);
348
+ expect(host.getSlot(stateSlotKey)).toBeTruthy();
349
+ });
350
+ it('should have a main view extension slot', () => {
351
+ const host = createAppHost([], testHostOptions);
352
+ expect(host.getSlot(mainViewSlotKey)).toBeTruthy();
353
+ });
354
+ it('should retrieve all slot keys', () => {
355
+ const sortSlotKeys = (slotKeys) => _.sortBy(slotKeys, 'name');
356
+ const host = createAppHost([mockPackage], testHostOptions);
357
+ const actual = sortSlotKeys(host.getAllSlotKeys());
358
+ const expected = sortSlotKeys([AppHostAPI, mainViewSlotKey, stateSlotKey, MockAPI]);
359
+ expect(actual).toEqual(expected);
360
+ });
361
+ describe('private API slot key', () => {
362
+ it('should equal itself', () => {
363
+ const host = createAppHost([mockPackage], testHostOptions);
364
+ const API = host.getAPI(MockAPI);
365
+ expect(API).toBeTruthy();
366
+ });
367
+ it('should not equal another key with same name', () => {
368
+ const host = createAppHost([mockPackage], testHostOptions);
369
+ const fakeKey = { name: MockAPI.name };
370
+ expect(() => {
371
+ host.getAPI(fakeKey);
372
+ }).toThrowError(new RegExp(MockAPI.name));
373
+ });
374
+ it('should not equal another key with same name that claims it is public', () => {
375
+ const host = createAppHost([mockPackage], testHostOptions);
376
+ const fakeKey1 = {
377
+ name: MockAPI.name,
378
+ public: true
379
+ };
380
+ const fakeKey2 = {
381
+ name: MockAPI.name,
382
+ public: false
383
+ };
384
+ const fakeKey3 = {
385
+ name: MockAPI.name,
386
+ public: 'zzz'
387
+ };
388
+ expect(() => host.getAPI(fakeKey1)).toThrowError(new RegExp(MockAPI.name));
389
+ expect(() => host.getAPI(fakeKey2)).toThrowError(new RegExp(MockAPI.name));
390
+ expect(() => host.getAPI(fakeKey3)).toThrowError(new RegExp(MockAPI.name));
391
+ });
392
+ });
393
+ describe('public API slot key', () => {
394
+ it('should equal itself', () => {
395
+ const host = createAppHost([mockPackageWithPublicAPI], testHostOptions);
396
+ const API = host.getAPI(MockPublicAPI);
397
+ expect(API).toBeTruthy();
398
+ });
399
+ it('should equal another key with same name that claims it is public', () => {
400
+ const host = createAppHost([mockPackageWithPublicAPI], testHostOptions);
401
+ const anotherKey = {
402
+ name: MockPublicAPI.name,
403
+ public: true
404
+ };
405
+ const API = host.getAPI(anotherKey);
406
+ expect(API).toBeTruthy();
407
+ });
408
+ it('should not equal another key with same name than does not claim it is public', () => {
409
+ const host = createAppHost([mockPackageWithPublicAPI], testHostOptions);
410
+ const anotherKey1 = {
411
+ name: MockPublicAPI.name
412
+ };
413
+ const anotherKey2 = {
414
+ name: MockPublicAPI.name,
415
+ public: false
416
+ };
417
+ const anotherKey3 = {
418
+ name: MockPublicAPI.name,
419
+ public: 'zzz'
420
+ };
421
+ expect(() => host.getAPI(anotherKey1)).toThrowError(new RegExp(MockPublicAPI.name));
422
+ expect(() => host.getAPI(anotherKey2)).toThrowError(new RegExp(MockPublicAPI.name));
423
+ expect(() => host.getAPI(anotherKey3)).toThrowError(new RegExp(MockPublicAPI.name));
424
+ });
425
+ });
426
+ });
427
+ describe('Shell extension slots', () => {
428
+ describe('hasSlot', () => {
429
+ it('should return false for hasSlot if the slot is not defined on the host', () => __awaiter(void 0, void 0, void 0, function* () {
430
+ const host = createAppHost([], testHostOptions);
431
+ const entryPointWithoutSlot = {
432
+ name: 'MOCK_WITHOUT_SLOT',
433
+ extend(shell) {
434
+ expect(shell.hasSlot(MockSlot)).toBeFalsy();
435
+ }
436
+ };
437
+ yield host.addShells([entryPointWithoutSlot]);
438
+ }));
439
+ it('should return true for hasSlot if the slot is defined on the host', () => __awaiter(void 0, void 0, void 0, function* () {
440
+ const host = createAppHost([], testHostOptions);
441
+ const entryPointWithSlot = {
442
+ name: 'MOCK_WITH_SLOT',
443
+ attach(shell) {
444
+ shell.declareSlot(MockSlot);
445
+ },
446
+ extend(shell) {
447
+ expect(shell.hasSlot(MockSlot)).toBeTruthy();
448
+ }
449
+ };
450
+ yield host.addShells([entryPointWithSlot]);
451
+ }));
452
+ });
453
+ it('should allow contribution', () => __awaiter(void 0, void 0, void 0, function* () {
454
+ const host = createAppHost([], testHostOptions);
455
+ const slotKey = {
456
+ name: 'MOCK_SLOT'
457
+ };
458
+ const MockAPIA = {
459
+ name: 'MOCK_API_A'
460
+ };
461
+ const contributedItemA = { value: 'A' };
462
+ const contributedItemB = { value: 'B' };
463
+ const entryPointA = {
464
+ name: 'MOCK_A',
465
+ declareAPIs() {
466
+ return [MockAPIA];
467
+ },
468
+ attach(shell) {
469
+ shell.declareSlot(slotKey);
470
+ shell.contributeAPI(MockAPIA, () => ({
471
+ contributeItem(fromShell, item) {
472
+ shell.getSlot(slotKey).contribute(fromShell, item);
473
+ }
474
+ }));
475
+ },
476
+ extend(shell) {
477
+ shell.getAPI(MockAPIA).contributeItem(shell, contributedItemA);
478
+ }
479
+ };
480
+ const entryPointB = {
481
+ name: 'MOCK_B',
482
+ getDependencyAPIs() {
483
+ return [MockAPIA];
484
+ },
485
+ extend(shell) {
486
+ shell.getAPI(MockAPIA).contributeItem(shell, contributedItemB);
487
+ }
488
+ };
489
+ yield host.addShells([entryPointA, entryPointB]);
490
+ const getItems = () => host
491
+ .getSlot(slotKey)
492
+ .getItems()
493
+ .map(({ contribution }) => contribution);
494
+ expect(getItems()).toEqual([contributedItemA, contributedItemB]);
495
+ yield host.removeShells(['MOCK_B']);
496
+ expect(getItems()).toEqual([contributedItemA]);
497
+ yield host.addShells([entryPointB]);
498
+ expect(getItems()).toEqual([contributedItemA, contributedItemB]);
499
+ }));
500
+ it('should not allow direct access to slots from other shells', () => __awaiter(void 0, void 0, void 0, function* () {
501
+ const host = createAppHost([], testHostOptions);
502
+ const slotKey = {
503
+ name: 'MOCK_SLOT'
504
+ };
505
+ const MockAPIA = {
506
+ name: 'MOCK_API_A'
507
+ };
508
+ const entryPointA = {
509
+ name: 'MOCK_A',
510
+ declareAPIs() {
511
+ return [MockAPIA];
512
+ },
513
+ attach(shell) {
514
+ shell.declareSlot(slotKey);
515
+ }
516
+ };
517
+ const entryPointB = {
518
+ name: 'MOCK_B',
519
+ extend(shell) {
520
+ const errorString = `Shell '${entryPointB.name}' is trying to get slot '${slotKey.name}' that is owned by '${entryPointA.name}'`;
521
+ expect(() => shell.getSlot(slotKey)).toThrowError(errorString);
522
+ }
523
+ };
524
+ yield host.addShells([entryPointA]);
525
+ }));
526
+ });
527
+ describe('Host State', () => {
528
+ it('should have a store with initial state', () => {
529
+ const host = createAppHost([], testHostOptions);
530
+ expect(host.getStore().getState()).toEqual({
531
+ $installedShells: {
532
+ installedShells: {
533
+ [AppHostServicesEntryPointName]: true
534
+ }
535
+ }
536
+ });
537
+ });
538
+ });
539
+ describe('Entry Point Contributions', () => {
540
+ it('should contribute API', () => {
541
+ const host = createAppHost([mockPackage], testHostOptions);
542
+ expect(host.getAPI(MockAPI)).toBeTruthy();
543
+ });
544
+ it('should contribute API after initial installations', () => __awaiter(void 0, void 0, void 0, function* () {
545
+ const host = createAppHost([], testHostOptions);
546
+ expect(() => host.getAPI(MockAPI)).toThrow();
547
+ yield host.addShells([mockPackage]);
548
+ expect(host.getAPI(MockAPI)).toBeTruthy();
549
+ }));
550
+ it('should execute detach and attach sequence according to dependencies', () => __awaiter(void 0, void 0, void 0, function* () {
551
+ const MockAPI2 = { name: 'MOCK' };
552
+ const dependantEntryPoint = {
553
+ name: 'EP1',
554
+ getDependencyAPIs: () => [MockAPI],
555
+ declareAPIs: () => [MockAPI2],
556
+ attach(shell) {
557
+ shell.contributeAPI(MockAPI2, () => shell.getAPI(MockAPI));
558
+ },
559
+ detach(shell) {
560
+ shell.getAPI(MockAPI).stubTrue();
561
+ }
562
+ };
563
+ const dependantEntryPoint2 = {
564
+ name: 'EP2',
565
+ getDependencyAPIs: () => [MockAPI, MockAPI2],
566
+ detach(shell) {
567
+ shell.getAPI(MockAPI).stubTrue();
568
+ shell.getAPI(MockAPI2).stubTrue();
569
+ }
570
+ };
571
+ const host = createAppHost([dependantEntryPoint2, mockPackage, dependantEntryPoint], testHostOptions);
572
+ expect(() => host.removeShells([mockPackage.name])).not.toThrow();
573
+ expect(host.hasShell(mockPackage.name)).toBe(false);
574
+ expect(host.hasShell(dependantEntryPoint.name)).toBe(false);
575
+ expect(host.hasShell(dependantEntryPoint2.name)).toBe(false);
576
+ yield host.addShells([mockPackage]);
577
+ expect(host.hasShell(dependantEntryPoint.name)).toBe(true);
578
+ expect(host.hasShell(dependantEntryPoint2.name)).toBe(true);
579
+ }));
580
+ it('should contribute state', () => __awaiter(void 0, void 0, void 0, function* () {
581
+ const getMockShellState = (host) => _.get(host.getStore().getState(), [mockPackage.name, mockShellStateKey], null);
582
+ const appHost = createAppHost([], testHostOptions);
583
+ expect(getMockShellState(appHost)).toBeNull();
584
+ yield appHost.addShells([mockPackage]);
585
+ expect(getMockShellState(appHost)).toEqual(mockShellInitialState);
586
+ }));
587
+ describe('memoizeForState', () => {
588
+ const memoizedAPI = {
589
+ name: 'memoizedAPI'
590
+ };
591
+ function contributeMemoizedAPI(host, shouldClear) {
592
+ const createAPI = (shell) => ({
593
+ getNewObject: shell.memoizeForState(() => ({}), _.stubTrue, shouldClear)
594
+ });
595
+ const mockShell = addMockShell(host, {
596
+ declareAPIs: () => [memoizedAPI],
597
+ attach(shell) {
598
+ shell.contributeAPI(memoizedAPI, () => createAPI(shell));
599
+ }
600
+ });
601
+ return mockShell;
602
+ }
603
+ const createMockShell = (host) => {
604
+ let observableState = {};
605
+ const mockShell = addMockShell(host, {
606
+ declareAPIs: () => [memoizedAPI],
607
+ attach(shell) {
608
+ observableState = shell.contributeObservableState(() => ({
609
+ stateValue: (state = 1, action) => {
610
+ return action.type === 'increase' ? state + 1 : state;
611
+ }
612
+ }), state => {
613
+ return {
614
+ getStateValue: () => state.stateValue
615
+ };
616
+ });
617
+ shell.contributeAPI(memoizedAPI, () => ({
618
+ getNewObject: shell.memoizeForState(() => ({
619
+ value: observableState.current(true).getStateValue()
620
+ }), _.stubTrue)
621
+ }));
622
+ }
623
+ });
624
+ return {
625
+ mockShell,
626
+ observableState
627
+ };
628
+ };
629
+ it('should memoize functions upon demand', () => {
630
+ const host = createAppHost([mockPackage], testHostOptions);
631
+ const getObj = () => host.getAPI(MockAPI).getNewObject();
632
+ expect(getObj()).not.toBe(getObj());
633
+ contributeMemoizedAPI(host);
634
+ const objForStateA = host.getAPI(memoizedAPI).getNewObject();
635
+ expect(objForStateA).toBe(host.getAPI(memoizedAPI).getNewObject());
636
+ });
637
+ it('should clear memoized functions on store dispatch', () => {
638
+ const host = createAppHost([mockPackage], testHostOptions);
639
+ contributeMemoizedAPI(host);
640
+ const objForStateA = host.getAPI(memoizedAPI).getNewObject();
641
+ expect(objForStateA).toBe(host.getAPI(memoizedAPI).getNewObject());
642
+ host.getStore().dispatch({ type: 'MOCK_ACTION' });
643
+ host.getStore().flush();
644
+ expect(objForStateA).not.toBe(host.getAPI(memoizedAPI).getNewObject());
645
+ });
646
+ it('should clear memoized functions on store dispatch when synchronous code is executing', () => {
647
+ const host = createAppHost([mockPackage], testHostOptions);
648
+ contributeMemoizedAPI(host);
649
+ const res1 = host.getAPI(memoizedAPI).getNewObject();
650
+ expect(res1).toBe(host.getAPI(memoizedAPI).getNewObject());
651
+ let res3;
652
+ host.getStore().subscribe(() => {
653
+ // cache was flushing, so call memoized API in order to create new cache
654
+ const res2 = host.getAPI(memoizedAPI).getNewObject();
655
+ expect(res1).not.toBe(res2);
656
+ expect(res2).toBe(host.getAPI(memoizedAPI).getNewObject());
657
+ // dispatch new action for sync cache flushing
658
+ host.getStore().dispatch({ type: 'MOCK_ACTION' });
659
+ res3 = host.getAPI(memoizedAPI).getNewObject();
660
+ expect(res2).not.toBe(res3);
661
+ });
662
+ host.getStore().flush();
663
+ expect(res3).toBe(host.getAPI(memoizedAPI).getNewObject());
664
+ });
665
+ it('should clear memoized functions on observable dispatch', () => {
666
+ const host = createAppHost([mockPackage], testHostOptions);
667
+ const { mockShell } = createMockShell(host);
668
+ const objForStateA = host.getAPI(memoizedAPI).getNewObject();
669
+ expect(objForStateA).toBe(host.getAPI(memoizedAPI).getNewObject());
670
+ const { dispatch, flush } = mockShell.getStore();
671
+ dispatch({ type: 'increase' });
672
+ flush();
673
+ expect(objForStateA).not.toBe(host.getAPI(memoizedAPI).getNewObject());
674
+ });
675
+ it('should clear memoized functions on observable dispatch when synchronous code is executing', () => {
676
+ const host = createAppHost([mockPackage], testHostOptions);
677
+ const { mockShell, observableState } = createMockShell(host);
678
+ const { dispatch, flush } = mockShell.getStore();
679
+ const res1 = host.getAPI(memoizedAPI).getNewObject();
680
+ expect(res1).toBe(host.getAPI(memoizedAPI).getNewObject());
681
+ let res3;
682
+ observableState.subscribe(mockShell, () => {
683
+ // cache was flushing, so call memoized API in order to create new cache
684
+ const res2 = host.getAPI(memoizedAPI).getNewObject();
685
+ expect(res1).not.toBe(res2);
686
+ expect(res2).toBe(host.getAPI(memoizedAPI).getNewObject());
687
+ // dispatch new action for sync cache flushing
688
+ dispatch({ type: 'increase' });
689
+ res3 = host.getAPI(memoizedAPI).getNewObject();
690
+ expect(res2).not.toBe(res3);
691
+ });
692
+ dispatch({ type: 'increase' });
693
+ flush();
694
+ expect(res3).toBe(host.getAPI(memoizedAPI).getNewObject());
695
+ });
696
+ it('should not clear memoized functions if not needed', () => {
697
+ const host = createAppHost([], testHostOptions);
698
+ contributeMemoizedAPI(host, _.stubFalse);
699
+ const objForStateA = host.getAPI(memoizedAPI).getNewObject();
700
+ host.getStore().dispatch({ type: 'MOCK_ACTION' });
701
+ host.getStore().flush();
702
+ expect(objForStateA).toBe(host.getAPI(memoizedAPI).getNewObject());
703
+ });
704
+ });
705
+ describe('memory cleanup:', () => {
706
+ let originalFinalizationRegistry;
707
+ let cleanupMemory = (ref) => { };
708
+ beforeEach(() => {
709
+ originalFinalizationRegistry = FinalizationRegistry;
710
+ globalThis.FinalizationRegistry = function (cleanupCb) {
711
+ const heldValueSet = new Map();
712
+ cleanupMemory = ref => {
713
+ const heldValue = heldValueSet.get(ref);
714
+ cleanupCb(heldValue);
715
+ };
716
+ return {
717
+ register(target, heldValue, unregisterToken) {
718
+ heldValueSet.set(target, heldValue);
719
+ },
720
+ unregister() { }
721
+ };
722
+ };
723
+ });
724
+ afterEach(() => {
725
+ globalThis.FinalizationRegistry = originalFinalizationRegistry;
726
+ });
727
+ it('should remove memoized function from memory when there is no ref to memoized function', () => {
728
+ const host = createAppHost([], testHostOptions);
729
+ const newAPI = { name: 'newAPI' };
730
+ let memFn1 = null;
731
+ let memFn2 = null;
732
+ const createAPI = (shell) => {
733
+ memFn1 = shell.memoizeForState(() => ({}), _.stubTrue, _.stubTrue);
734
+ memFn2 = shell.memoizeForState(() => ({}), _.stubTrue, _.stubTrue);
735
+ memFn1.cache.clear = jest.fn();
736
+ memFn2.cache.clear = jest.fn();
737
+ return {
738
+ getNewObject: memFn1,
739
+ getNewObject2: memFn2
740
+ };
741
+ };
742
+ addMockShell(host, {
743
+ declareAPIs: () => [newAPI],
744
+ attach(shell) {
745
+ shell.contributeAPI(newAPI, () => createAPI(shell));
746
+ }
747
+ });
748
+ if (memFn1 && memFn2) {
749
+ const cacheFnMock1 = memFn1.cache.clear;
750
+ const cacheFnMock2 = memFn2.cache.clear;
751
+ host.getStore().dispatch({ type: 'MOCK_ACTION' });
752
+ host.getStore().flush();
753
+ expect(cacheFnMock1).toHaveBeenCalledTimes(1);
754
+ expect(cacheFnMock2).toHaveBeenCalledTimes(1);
755
+ cleanupMemory(memFn1);
756
+ host.getStore().dispatch({ type: 'MOCK_ACTION' });
757
+ host.getStore().flush();
758
+ expect(cacheFnMock1).toHaveBeenCalledTimes(1);
759
+ expect(cacheFnMock2).toHaveBeenCalledTimes(2);
760
+ }
761
+ else {
762
+ throw Error('memFn1 or memFn2 is not defined');
763
+ }
764
+ });
765
+ });
766
+ describe('lazyEvaluator', () => {
767
+ it('should return a getter that is evaluated only once', () => {
768
+ const { helperShell } = createHostWithDependantPackages(MockAPI);
769
+ const func = jest.fn(() => 42);
770
+ const lazyEval = helperShell.lazyEvaluator(func);
771
+ expect(lazyEval.get()).toBe(42);
772
+ expect(lazyEval.get()).toBe(42);
773
+ expect(func).toHaveBeenCalledTimes(1);
774
+ });
775
+ });
776
+ });
777
+ describe('Entry Point Shell Scoping', () => {
778
+ describe('Shell hasAPI', () => {
779
+ it('should return false if the API is not defined on the host', () => __awaiter(void 0, void 0, void 0, function* () {
780
+ const host = createAppHost([], testHostOptions);
781
+ const entryPointWithoutAPI = {
782
+ name: 'ENTRY_POINT_WITHOUT_API',
783
+ getDependencyAPIs: () => [],
784
+ extend(shell) {
785
+ expect(shell.hasAPI(MockAPI)).toBeFalsy();
786
+ }
787
+ };
788
+ yield host.addShells([entryPointWithoutAPI]);
789
+ }));
790
+ it('should return false if the API is defined on the host but is not declared as dependency', () => __awaiter(void 0, void 0, void 0, function* () {
791
+ const host = createAppHost([mockPackage], testHostOptions);
792
+ const entryPointWithoutAPIDependency = {
793
+ name: 'ENTRY_POINT_WITHOUT_API_DEPENDENCY',
794
+ getDependencyAPIs: () => [],
795
+ extend(shell) {
796
+ expect(shell.hasAPI(MockAPI)).toBeFalsy();
797
+ }
798
+ };
799
+ yield host.addShells([entryPointWithoutAPIDependency]);
800
+ }));
801
+ it('should return true if the API is defined on the host and is declared as dependency', () => __awaiter(void 0, void 0, void 0, function* () {
802
+ const host = createAppHost([mockPackage], testHostOptions);
803
+ const entryPointWithAPIDependency = {
804
+ name: 'ENTRY_POINT_WITH_API_DEPENDENCY',
805
+ getDependencyAPIs: () => [MockAPI],
806
+ extend(shell) {
807
+ expect(shell.hasAPI(MockAPI)).toBeTruthy();
808
+ }
809
+ };
810
+ yield host.addShells([entryPointWithAPIDependency]);
811
+ }));
812
+ it('should return true if the API is declared on the shell', () => __awaiter(void 0, void 0, void 0, function* () {
813
+ const host = createAppHost([], testHostOptions);
814
+ const entryPointWithAPIDeclaration = {
815
+ name: 'ENTRY_POINT_WITH_API_DECLARATION',
816
+ declareAPIs: () => [MockAPI],
817
+ attach(shell) {
818
+ shell.contributeAPI(MockAPI, () => ({}));
819
+ },
820
+ extend(shell) {
821
+ expect(shell.hasAPI(MockAPI)).toBeTruthy();
822
+ }
823
+ };
824
+ yield host.addShells([entryPointWithAPIDeclaration]);
825
+ }));
826
+ });
827
+ it('should be able to call an API declared in dependencies', () => {
828
+ const entryPointThatCallsAPI = {
829
+ name: 'ENTRY_POINT_WITH_API_CALL',
830
+ getDependencyAPIs() {
831
+ return [MockAPI];
832
+ },
833
+ extend(shell) {
834
+ shell.getAPI(MockAPI).stubTrue();
835
+ }
836
+ };
837
+ const appHost = createAppHost([mockPackage], testHostOptions);
838
+ expect(() => appHost.addShells([entryPointThatCallsAPI])).not.toThrow();
839
+ });
840
+ it('should not be able to call an API not declared in dependencies', () => {
841
+ const entryPointThatCallsAPI = {
842
+ name: 'ENTRY_POINT_WITH_API_CALL',
843
+ extend(shell) {
844
+ shell.getAPI(MockAPI).stubTrue();
845
+ }
846
+ };
847
+ const appHost = createAppHost([mockPackage], testHostOptions);
848
+ expect(() => appHost.addShells([entryPointThatCallsAPI])).toThrow();
849
+ });
850
+ it('should get scoped state', done => {
851
+ const state = {};
852
+ const MOCK_STATE_KEY = 'mockStateKey';
853
+ const entryPointWithState = {
854
+ name: 'ENTRY_POINT_WITH_STATE',
855
+ attach(shell) {
856
+ shell.contributeState(() => ({
857
+ [MOCK_STATE_KEY]: () => state
858
+ }));
859
+ },
860
+ extend(shell) {
861
+ expect(_.get(shell.getStore().getState(), MOCK_STATE_KEY)).toBe(state);
862
+ done();
863
+ }
864
+ };
865
+ createAppHost([entryPointWithState], testHostOptions);
866
+ });
867
+ it('should be able to uninstall own installed packages', () => __awaiter(void 0, void 0, void 0, function* () {
868
+ // note: this test assumes that addShells and removeShells complete synchronously
869
+ const packageThatInstallsAPackage = {
870
+ name: 'ENTRY_POINT_THAT_INSTALLS_A_PACKAGE',
871
+ extend(shell) {
872
+ shell.addShells([mockPackage]);
873
+ shell.removeShells([mockPackage.name]);
874
+ }
875
+ };
876
+ createAppHost([packageThatInstallsAPackage], testHostOptions);
877
+ expect(() => createAppHost([packageThatInstallsAPackage], testHostOptions)).not.toThrow();
878
+ }));
879
+ it('should not be able to uninstall not own installed packages', () => {
880
+ const packageThatTriesToUninstallAPackage = {
881
+ name: 'ENTRY_POINT_THAT_TRYIES_TO_UNINSTALL_A_PACKAGE',
882
+ extend(shell) {
883
+ shell.removeShells([mockPackage.name]);
884
+ }
885
+ };
886
+ expect(() => createAppHost([mockPackage, packageThatTriesToUninstallAPackage], testHostOptions)).toThrow();
887
+ });
888
+ it('should not trigger other reducers by scoped dispatch', () => {
889
+ const INITIAL_STATE = { value: 0 };
890
+ const SET_VALUE = 'SET_VALUE';
891
+ const setValue = (value) => ({ type: SET_VALUE, value });
892
+ const host = createAppHost([], {
893
+ shouldScopeReducers: true,
894
+ monitoring: {}
895
+ });
896
+ const shell1 = addMockShell(host, {
897
+ attach(shell) {
898
+ shell.contributeState(() => ({
899
+ state1: (state = INITIAL_STATE, action) => {
900
+ switch (action.type) {
901
+ case SET_VALUE:
902
+ return { value: action.value };
903
+ }
904
+ return state;
905
+ }
906
+ }));
907
+ }
908
+ });
909
+ // Initial state
910
+ const getState = () => shell1.getStore().getState().state1.value;
911
+ expect(getState()).toBe(0);
912
+ // Scoped dispatch
913
+ shell1.getStore().dispatch(setValue(73));
914
+ expect(getState()).toBe(73);
915
+ // Unscoped dispatch
916
+ host.getStore().dispatch(setValue(1337));
917
+ expect(getState()).toBe(1337);
918
+ addMockShell(host, {
919
+ extend(shell) {
920
+ // Scoped dispatch in a different scope
921
+ shell.getStore().dispatch(setValue(42));
922
+ }
923
+ });
924
+ expect(getState()).toBe(1337);
925
+ });
926
+ });
927
+ describe('Entry Point HMR support', () => {
928
+ const LowLevelSlotKey = { name: 'LOW-LEVEL-SLOT' };
929
+ const HighLevelSlotKey = { name: 'HIGH-LEVEL-SLOT' };
930
+ const ConsumerSlotKey = { name: 'CONSUMER-SLOT' };
931
+ const LowLevelAPI = {
932
+ name: 'LOW-LEVEL-API'
933
+ };
934
+ const HighLevelAPI = {
935
+ name: 'HIGH-LEVEL-API'
936
+ };
937
+ const hmrTestPackage = [
938
+ {
939
+ name: 'LOW_LEVEL_API_ENTRY_POINT',
940
+ declareAPIs() {
941
+ return [LowLevelAPI];
942
+ },
943
+ attach(shell) {
944
+ shell.declareSlot(LowLevelSlotKey);
945
+ shell.contributeAPI(LowLevelAPI, () => ({
946
+ lowLevelFunc: jest.fn()
947
+ }));
948
+ }
949
+ },
950
+ {
951
+ name: 'HIGH_LEVEL_API_ENTRY_POINT',
952
+ getDependencyAPIs() {
953
+ return [LowLevelAPI];
954
+ },
955
+ declareAPIs() {
956
+ return [HighLevelAPI];
957
+ },
958
+ attach(shell) {
959
+ shell.declareSlot(HighLevelSlotKey);
960
+ shell.contributeAPI(HighLevelAPI, () => ({
961
+ highLevelFunc: jest.fn
962
+ }));
963
+ },
964
+ extend(shell) {
965
+ shell.getAPI(LowLevelAPI).lowLevelFunc('HIGH');
966
+ }
967
+ },
968
+ {
969
+ name: 'CONSUMER_ENTRY_POINT',
970
+ getDependencyAPIs() {
971
+ return [HighLevelAPI];
972
+ },
973
+ extend(shell) {
974
+ shell.declareSlot(ConsumerSlotKey);
975
+ shell.getAPI(HighLevelAPI).highLevelFunc('CONSUMER');
976
+ }
977
+ }
978
+ ];
979
+ it('should be able to reload entry points', () => __awaiter(void 0, void 0, void 0, function* () {
980
+ const appHost = createAppHost(hmrTestPackage, testHostOptions);
981
+ yield appHost.removeShells(['LOW_LEVEL_API_ENTRY_POINT']);
982
+ yield appHost.addShells([hmrTestPackage[0]]);
983
+ expect(appHost.getAPI(HighLevelAPI)).toBeDefined();
984
+ }));
985
+ });
986
+ describe('API layer', () => {
987
+ it('should allow dependency from high to lower level API', () => __awaiter(void 0, void 0, void 0, function* () {
988
+ const MockAPI1 = { name: 'Mock-API', layer: 'INFRA' };
989
+ const layers = [
990
+ {
991
+ level: 0,
992
+ name: 'INFRA'
993
+ },
994
+ {
995
+ level: 1,
996
+ name: 'PRODUCT'
997
+ }
998
+ ];
999
+ const host = createAppHost([], Object.assign(Object.assign({}, emptyLoggerOptions), { layers }));
1000
+ const EntryPoint1 = {
1001
+ name: 'MOCK_ENTRY_POINT_1',
1002
+ layer: 'PRODUCT',
1003
+ getDependencyAPIs: () => [MockAPI1]
1004
+ };
1005
+ const EntryPoint2 = {
1006
+ name: 'MOCK_ENTRY_POINT_2',
1007
+ layer: 'INFRA',
1008
+ declareAPIs: () => [MockAPI1],
1009
+ attach(shell) {
1010
+ shell.contributeAPI(MockAPI1, () => ({}));
1011
+ }
1012
+ };
1013
+ yield host.addShells([EntryPoint2]);
1014
+ expect(() => host.addShells([EntryPoint1])).not.toThrow();
1015
+ }));
1016
+ it('should not allow dependency from low to higher level API', () => __awaiter(void 0, void 0, void 0, function* () {
1017
+ const MockAPI1 = { name: 'Mock-API', layer: 'PRODUCT' };
1018
+ const layers = [
1019
+ {
1020
+ level: 0,
1021
+ name: 'INFRA'
1022
+ },
1023
+ {
1024
+ level: 1,
1025
+ name: 'PRODUCT'
1026
+ }
1027
+ ];
1028
+ const host = createAppHost([], Object.assign(Object.assign({}, emptyLoggerOptions), { layers }));
1029
+ const EntryPoint1 = {
1030
+ name: 'MOCK_ENTRY_POINT_1',
1031
+ layer: 'INFRA',
1032
+ getDependencyAPIs: () => [MockAPI1]
1033
+ };
1034
+ const EntryPoint2 = {
1035
+ name: 'MOCK_ENTRY_POINT_2',
1036
+ layer: 'PRODUCT',
1037
+ declareAPIs: () => [MockAPI1],
1038
+ attach(shell) {
1039
+ shell.contributeAPI(MockAPI1, () => ({}));
1040
+ }
1041
+ };
1042
+ yield host.addShells([EntryPoint2]);
1043
+ expect(() => host.addShells([EntryPoint1])).toThrowError(`Entry point ${EntryPoint1.name} of layer ${layers[0].name} cannot depend on API ${MockAPI1.name} of layer ${EntryPoint2.layer}`);
1044
+ }));
1045
+ it('should not allow adding shell of unknown layer', () => {
1046
+ const MockAPI1 = {
1047
+ name: 'Mock-API',
1048
+ layer: 'NON_EXIXTING_layer'
1049
+ };
1050
+ const layers = [
1051
+ {
1052
+ level: 0,
1053
+ name: 'INFRA'
1054
+ },
1055
+ {
1056
+ level: 1,
1057
+ name: 'PRODUCT'
1058
+ }
1059
+ ];
1060
+ const host = createAppHost([], Object.assign(Object.assign({}, emptyLoggerOptions), { layers }));
1061
+ const EntryPoint1 = {
1062
+ name: 'MOCK_ENTRY_POINT_1',
1063
+ layer: 'NON_EXIXTING_layer',
1064
+ getDependencyAPIs: () => [MockAPI1]
1065
+ };
1066
+ expect(() => host.addShells([EntryPoint1])).toThrowError(`Cannot find layer ${EntryPoint1.layer}`);
1067
+ });
1068
+ it('should not allow contribution of API with non-matching entry point layer', () => {
1069
+ const MockAPI1 = { name: 'Mock-API', layer: 'PRODUCT' };
1070
+ const layers = [
1071
+ {
1072
+ level: 0,
1073
+ name: 'INFRA'
1074
+ },
1075
+ {
1076
+ level: 1,
1077
+ name: 'PRODUCT'
1078
+ }
1079
+ ];
1080
+ const host = createAppHost([], Object.assign(Object.assign({}, emptyLoggerOptions), { layers }));
1081
+ const EntryPoint1 = {
1082
+ name: 'MOCK_ENTRY_POINT_1',
1083
+ layer: 'INFRA',
1084
+ declareAPIs: () => [MockAPI1],
1085
+ attach(shell) {
1086
+ shell.contributeAPI(MockAPI1, () => ({}));
1087
+ }
1088
+ };
1089
+ expect(() => host.addShells([EntryPoint1])).toThrowError(`Cannot contribute API ${MockAPI1.name} of layer ${MockAPI1.layer} from entry point ${EntryPoint1.name} of layer ${EntryPoint1.layer}`);
1090
+ });
1091
+ it('should support multi dimensional layers definition', () => {
1092
+ const MockAPI1 = {
1093
+ name: 'Mock-API',
1094
+ layer: ['COMMON', 'INFRA']
1095
+ };
1096
+ const layersDimension1 = [
1097
+ {
1098
+ level: 0,
1099
+ name: 'INFRA'
1100
+ },
1101
+ {
1102
+ level: 1,
1103
+ name: 'PRODUCT'
1104
+ }
1105
+ ];
1106
+ const layersDimension2 = [
1107
+ {
1108
+ level: 0,
1109
+ name: 'COMMON'
1110
+ },
1111
+ {
1112
+ level: 1,
1113
+ name: 'SPECIFIC'
1114
+ }
1115
+ ];
1116
+ const host = createAppHost([], Object.assign(Object.assign({}, emptyLoggerOptions), { layers: [layersDimension1, layersDimension2] }));
1117
+ const EntryPoint1 = {
1118
+ name: 'MOCK_ENTRY_POINT_1',
1119
+ layer: ['INFRA', 'COMMON'],
1120
+ declareAPIs: () => [MockAPI1],
1121
+ attach(shell) {
1122
+ shell.contributeAPI(MockAPI1, () => ({}));
1123
+ }
1124
+ };
1125
+ const EntryPoint2 = {
1126
+ name: 'MOCK_ENTRY_POINT_2',
1127
+ layer: ['PRODUCT', 'SPECIFIC'],
1128
+ getDependencyAPIs: () => [MockAPI1]
1129
+ };
1130
+ expect(() => host.addShells([EntryPoint1, EntryPoint2])).not.toThrow();
1131
+ });
1132
+ it('should throw for multi dimensional layers violation', () => {
1133
+ const MockAPI1 = {
1134
+ name: 'Mock-API',
1135
+ layer: ['INFRA', 'SPECIFIC']
1136
+ };
1137
+ const layersDimension1 = [
1138
+ {
1139
+ level: 0,
1140
+ name: 'INFRA'
1141
+ },
1142
+ {
1143
+ level: 1,
1144
+ name: 'PRODUCT'
1145
+ }
1146
+ ];
1147
+ const layersDimension2 = [
1148
+ {
1149
+ level: 0,
1150
+ name: 'COMMON'
1151
+ },
1152
+ {
1153
+ level: 1,
1154
+ name: 'SPECIFIC'
1155
+ }
1156
+ ];
1157
+ const host = createAppHost([], Object.assign(Object.assign({}, emptyLoggerOptions), { layers: [layersDimension1, layersDimension2] }));
1158
+ const EntryPoint1 = {
1159
+ name: 'MOCK_ENTRY_POINT_1',
1160
+ layer: ['INFRA', 'SPECIFIC'],
1161
+ declareAPIs: () => [MockAPI1],
1162
+ attach(shell) {
1163
+ shell.contributeAPI(MockAPI1, () => ({}));
1164
+ }
1165
+ };
1166
+ const EntryPoint2 = {
1167
+ name: 'MOCK_ENTRY_POINT_2',
1168
+ layer: ['COMMON', 'PRODUCT'],
1169
+ getDependencyAPIs: () => [MockAPI1]
1170
+ };
1171
+ expect(() => host.addShells([EntryPoint1, EntryPoint2])).toThrowError(`Entry point ${EntryPoint2.name} of layer COMMON cannot depend on API ${MockAPI1.name} of layer SPECIFIC`);
1172
+ });
1173
+ it('should enforce cross-multi-dimensional-layers name uniqueness', () => {
1174
+ const layersDimension1 = [
1175
+ {
1176
+ level: 0,
1177
+ name: 'INFRA'
1178
+ },
1179
+ {
1180
+ level: 1,
1181
+ name: 'NOT_UNIQUE'
1182
+ }
1183
+ ];
1184
+ const layersDimension2 = [
1185
+ {
1186
+ level: 0,
1187
+ name: 'COMMON'
1188
+ },
1189
+ {
1190
+ level: 1,
1191
+ name: 'NOT_UNIQUE'
1192
+ }
1193
+ ];
1194
+ expect(() => createAppHost([], Object.assign(Object.assign({}, emptyLoggerOptions), { layers: [layersDimension1, layersDimension2] }))).toThrowError(`Cannot initialize host with non unique layers: NOT_UNIQUE`);
1195
+ });
1196
+ it('should allow single layered API for multi dimensional layers host', () => {
1197
+ const MockAPI1 = { name: 'Mock-API', layer: 'INFRA' };
1198
+ const layersDimension1 = [
1199
+ {
1200
+ level: 0,
1201
+ name: 'INFRA'
1202
+ },
1203
+ {
1204
+ level: 1,
1205
+ name: 'PRODUCT'
1206
+ }
1207
+ ];
1208
+ const layersDimension2 = [
1209
+ {
1210
+ level: 0,
1211
+ name: 'COMMON'
1212
+ },
1213
+ {
1214
+ level: 1,
1215
+ name: 'SPECIFIC'
1216
+ }
1217
+ ];
1218
+ const EntryPoint1 = {
1219
+ name: 'MOCK_ENTRY_POINT_1',
1220
+ layer: 'PRODUCT',
1221
+ getDependencyAPIs: () => [MockAPI1]
1222
+ };
1223
+ const EntryPoint2 = {
1224
+ name: 'MOCK_ENTRY_POINT_2',
1225
+ layer: 'INFRA',
1226
+ declareAPIs: () => [MockAPI1],
1227
+ attach(shell) {
1228
+ shell.contributeAPI(MockAPI1, () => ({}));
1229
+ }
1230
+ };
1231
+ expect(() => createAppHost([EntryPoint1, EntryPoint2], Object.assign(Object.assign({}, emptyLoggerOptions), { layers: [layersDimension1, layersDimension2] }))).not.toThrow();
1232
+ });
1233
+ });
1234
+ describe('API version', () => {
1235
+ it('should provide API of matching version', () => __awaiter(void 0, void 0, void 0, function* () {
1236
+ const MockAPIv0 = { name: 'Mock-API' };
1237
+ const MockAPIv2 = {
1238
+ name: 'Mock-API',
1239
+ version: 2
1240
+ };
1241
+ const host = createAppHost([]);
1242
+ const entryPoint = {
1243
+ name: 'MOCK_ENTRY_POINT',
1244
+ declareAPIs: () => [MockAPIv0, MockAPIv2],
1245
+ attach(shell) {
1246
+ shell.contributeAPI(MockAPIv0, () => ({ f1() { } }));
1247
+ shell.contributeAPI(MockAPIv2, () => ({ f2() { } }));
1248
+ }
1249
+ };
1250
+ yield host.addShells([entryPoint]);
1251
+ expect(host.getAPI(MockAPIv0).f1).toBeDefined();
1252
+ expect(host.getAPI(MockAPIv0).f2).not.toBeDefined();
1253
+ expect(host.getAPI(MockAPIv2).f2).toBeDefined();
1254
+ expect(host.getAPI(MockAPIv2).f1).not.toBeDefined();
1255
+ const SecondMockAPIv2 = {
1256
+ name: 'Mock-API',
1257
+ version: 2
1258
+ };
1259
+ expect(() => {
1260
+ addMockShell(host, {
1261
+ declareAPIs: () => [SecondMockAPIv2],
1262
+ attach(shell) {
1263
+ shell.contributeAPI(SecondMockAPIv2, () => ({ f2() { } }));
1264
+ }
1265
+ });
1266
+ }).toThrowError(new RegExp(`Extension slot with key '${SecondMockAPIv2.name}\\\(v${SecondMockAPIv2.version}\\\)' already exists`));
1267
+ }));
1268
+ });
1269
+ describe('Host API', () => {
1270
+ describe('hasAPI', () => {
1271
+ it('should return false if the API is not defined on the host', () => {
1272
+ const host = createAppHost([], testHostOptions);
1273
+ expect(host.hasAPI(MockAPI)).toBeFalsy();
1274
+ });
1275
+ it('should return true if the API is defined on the host', () => {
1276
+ const host = createAppHost([mockPackage], testHostOptions);
1277
+ expect(host.hasAPI(MockAPI)).toBeTruthy();
1278
+ });
1279
+ });
1280
+ it('should get all entry points', () => __awaiter(void 0, void 0, void 0, function* () {
1281
+ const host = createAppHost([mockPackage], testHostOptions);
1282
+ yield host.addShells([mockPackageWithPublicAPI]);
1283
+ const allEntryPoints = host.getAPI(AppHostAPI).getAllEntryPoints();
1284
+ expect(_.sortBy(allEntryPoints, 'name')).toEqual(_.sortBy([mockPackage, mockPackageWithPublicAPI, host.getAppHostServicesShell().entryPoint], 'name'));
1285
+ }));
1286
+ it('should get host options', () => {
1287
+ const host = createAppHost([mockPackage], testHostOptions);
1288
+ expect(host.getAPI(AppHostAPI).getAppHostOptions()).toEqual(testHostOptions);
1289
+ });
1290
+ });
1291
+ describe('Cyclic Mode', () => {
1292
+ it('should load cyclic dependencies groups if all other dependencies are ready', () => {
1293
+ const API1 = { name: 'API1' };
1294
+ const API2 = { name: 'API2' };
1295
+ const API3 = { name: 'API3' };
1296
+ const entryPoints = [
1297
+ {
1298
+ name: 'Package1',
1299
+ getDependencyAPIs: () => [API2],
1300
+ declareAPIs: () => [API1],
1301
+ attach(shell) {
1302
+ shell.contributeAPI(API1, () => ({}));
1303
+ }
1304
+ },
1305
+ {
1306
+ name: 'Package2',
1307
+ getDependencyAPIs: () => [API3],
1308
+ declareAPIs: () => [API2],
1309
+ attach(shell) {
1310
+ shell.contributeAPI(API2, () => ({}));
1311
+ }
1312
+ },
1313
+ {
1314
+ name: 'Package3',
1315
+ getDependencyAPIs: () => [API1],
1316
+ declareAPIs: () => [API3],
1317
+ attach(shell) {
1318
+ shell.contributeAPI(API3, () => ({}));
1319
+ }
1320
+ }
1321
+ ];
1322
+ const host = createAppHost(entryPoints, Object.assign(Object.assign({}, testHostOptions), { experimentalCyclicMode: true }));
1323
+ expect(host.hasShell(entryPoints[0].name)).toBe(true);
1324
+ expect(host.hasShell(entryPoints[1].name)).toBe(true);
1325
+ expect(host.hasShell(entryPoints[2].name)).toBe(true);
1326
+ });
1327
+ it('should not load cyclic dependencies groups if some other dependencies are not ready', () => {
1328
+ const API1 = { name: 'API1' };
1329
+ const API2 = { name: 'API2' };
1330
+ const API3 = { name: 'API3' };
1331
+ const API4 = { name: 'API4' };
1332
+ const entryPoints = [
1333
+ {
1334
+ name: 'Package1',
1335
+ getDependencyAPIs: () => [API2],
1336
+ declareAPIs: () => [API1],
1337
+ attach(shell) {
1338
+ shell.contributeAPI(API1, () => ({}));
1339
+ }
1340
+ },
1341
+ {
1342
+ name: 'Package2',
1343
+ getDependencyAPIs: () => [API3],
1344
+ declareAPIs: () => [API2],
1345
+ attach(shell) {
1346
+ shell.contributeAPI(API2, () => ({}));
1347
+ }
1348
+ },
1349
+ {
1350
+ name: 'Package3',
1351
+ getDependencyAPIs: () => [API1, API4],
1352
+ declareAPIs: () => [API3],
1353
+ attach(shell) {
1354
+ shell.contributeAPI(API3, () => ({}));
1355
+ }
1356
+ }
1357
+ ];
1358
+ const host = createAppHost(entryPoints, Object.assign(Object.assign({}, testHostOptions), { experimentalCyclicMode: true }));
1359
+ expect(host.hasShell(entryPoints[0].name)).toBe(false);
1360
+ expect(host.hasShell(entryPoints[1].name)).toBe(false);
1361
+ expect(host.hasShell(entryPoints[2].name)).toBe(false);
1362
+ });
1363
+ });
1364
+ describe('Host.executeWhenFree', () => {
1365
+ it('should be invoked immediately', () => {
1366
+ const host = createAppHost([], testHostOptions);
1367
+ const spy = jest.fn();
1368
+ host.executeWhenFree('1', spy);
1369
+ expect(spy).toBeCalledTimes(1);
1370
+ });
1371
+ it('when invoked during entryPoints installation, should run once installation resumes ', () => __awaiter(void 0, void 0, void 0, function* () {
1372
+ const host = createAppHost([], testHostOptions);
1373
+ const spy = jest.fn();
1374
+ const API1 = { name: 'API1' };
1375
+ const API2 = { name: 'API2' };
1376
+ const API3 = { name: 'API3' };
1377
+ const entryPoints = [
1378
+ {
1379
+ name: 'Package1',
1380
+ declareAPIs: () => [API1],
1381
+ attach(shell) {
1382
+ host.executeWhenFree('1', spy);
1383
+ shell.contributeAPI(API1, () => ({}));
1384
+ },
1385
+ extend(shell) {
1386
+ expect(spy).toBeCalledTimes(0);
1387
+ }
1388
+ },
1389
+ {
1390
+ name: 'Package2',
1391
+ getDependencyAPIs: () => [API1],
1392
+ declareAPIs: () => [API2],
1393
+ attach(shell) {
1394
+ shell.contributeAPI(API2, () => ({}));
1395
+ },
1396
+ extend(shell) {
1397
+ expect(spy).toBeCalledTimes(0);
1398
+ }
1399
+ },
1400
+ {
1401
+ name: 'Package3',
1402
+ getDependencyAPIs: () => [API2],
1403
+ declareAPIs: () => [API3],
1404
+ attach(shell) {
1405
+ shell.contributeAPI(API3, () => ({}));
1406
+ },
1407
+ extend(shell) {
1408
+ expect(spy).toBeCalledTimes(0);
1409
+ }
1410
+ }
1411
+ ];
1412
+ yield host.addShells(entryPoints);
1413
+ expect(spy).toBeCalledTimes(1);
1414
+ }));
1415
+ });
1416
+ describe('Host.onDeclarationsChanged', () => {
1417
+ it('should be called once for an entry point attach phase', () => {
1418
+ const host = createAppHost([], testHostOptions);
1419
+ const spy = jest.fn();
1420
+ host.onDeclarationsChanged(spy);
1421
+ host.addShells([mockPackage, dependsOnMockPackageEntryPoint, mockPackageWithSlot]);
1422
+ expect(spy).toBeCalledTimes(1);
1423
+ });
1424
+ it('should be called once for an entry point detach phase', () => {
1425
+ const host = createAppHost([], testHostOptions);
1426
+ const spy = jest.fn();
1427
+ host.onDeclarationsChanged(spy);
1428
+ host.addShells([mockPackage, dependsOnMockPackageEntryPoint, mockPackageWithSlot]);
1429
+ expect(spy).toBeCalledTimes(1);
1430
+ host.removeShells([mockPackage.name]);
1431
+ expect(spy).toBeCalledTimes(2);
1432
+ });
1433
+ it('should be called once for an async api contribution in the extend phase', () => {
1434
+ const host = createAppHost([], testHostOptions);
1435
+ const spy = jest.fn();
1436
+ host.onDeclarationsChanged(spy);
1437
+ host.addShells([asyncLoadMockPackage]);
1438
+ expect(spy).toBeCalledTimes(1);
1439
+ });
1440
+ it('should be called for a slot declaration outside of an entry point', () => {
1441
+ const host = createAppHost([], testHostOptions);
1442
+ const spy = jest.fn();
1443
+ host.onDeclarationsChanged(spy);
1444
+ const shell = addMockShell(host);
1445
+ expect(spy).toBeCalledTimes(1);
1446
+ shell.declareSlot(MockSlot);
1447
+ expect(spy).toBeCalledTimes(2);
1448
+ });
1449
+ });
1450
+ });
1451
+ //# sourceMappingURL=appHost.spec.js.map