@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5

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 (315) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/styles/global/_layout.scss +4 -0
  3. package/assets/translations/en-us.yaml +183 -51
  4. package/assets/translations/zh-hans.yaml +1 -7
  5. package/chart/monitoring/ClusterSelector.vue +0 -21
  6. package/chart/monitoring/prometheus/index.vue +6 -3
  7. package/components/ActionDropdownShell.vue +5 -3
  8. package/components/ButtonGroup.vue +26 -1
  9. package/components/CruResource.vue +212 -16
  10. package/components/ExplorerMembers.vue +8 -4
  11. package/components/ExplorerProjectsNamespaces.vue +10 -6
  12. package/components/GrowlManager.vue +4 -0
  13. package/components/MgmtNodeList.vue +184 -0
  14. package/components/PromptRestore.vue +93 -32
  15. package/components/Questions/index.vue +1 -0
  16. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  17. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  18. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  19. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  21. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  22. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  23. package/components/ResourceDetail/index.vue +1 -1
  24. package/components/ResourceList/Masthead.vue +7 -1
  25. package/components/ResourceList/index.vue +82 -1
  26. package/components/ResourceTable.vue +1 -0
  27. package/components/RichTranslation.vue +5 -2
  28. package/components/Setting.vue +1 -0
  29. package/components/SortableTable/index.vue +4 -3
  30. package/components/SubtleLink.vue +31 -6
  31. package/components/Tabbed/Tab.vue +29 -3
  32. package/components/Tabbed/index.vue +25 -3
  33. package/components/TableOfContents/TableOfContents.vue +109 -0
  34. package/components/TableOfContents/composables.ts +258 -0
  35. package/components/Window/ContainerShell.vue +21 -11
  36. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  37. package/components/Wizard.vue +23 -5
  38. package/components/__tests__/ButtonGroup.test.ts +56 -0
  39. package/components/__tests__/PromptRestore.test.ts +169 -19
  40. package/components/fleet/AppCoChartGrid.vue +401 -0
  41. package/components/fleet/AppCoEmptyState.vue +127 -0
  42. package/components/fleet/AppCoPageHeader.vue +119 -0
  43. package/components/fleet/AppCoVersionSelect.vue +70 -0
  44. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  45. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  46. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  47. package/components/fleet/FleetIntro.vue +7 -3
  48. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  49. package/components/fleet/FleetSecretSelector.vue +5 -3
  50. package/components/fleet/FleetValuesFrom.vue +8 -2
  51. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  52. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  53. package/components/fleet/GitRepoTargetTab.vue +0 -2
  54. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  55. package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
  56. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  57. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  58. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  59. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  60. package/components/fleet/HelmOpTargetTab.vue +64 -60
  61. package/components/fleet/HelmOpValuesTab.vue +129 -105
  62. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  63. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  64. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  65. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  66. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  67. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  68. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  69. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  70. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  71. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  72. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  73. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  74. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  75. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  76. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  77. package/components/fleet/dashboard/Empty.vue +8 -4
  78. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  79. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  80. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  81. package/components/form/ArrayList.vue +61 -4
  82. package/components/form/FileSelector.vue +39 -1
  83. package/components/form/KeyValue.vue +23 -2
  84. package/components/form/LabeledSelect.vue +39 -1
  85. package/components/form/Labels.vue +22 -3
  86. package/components/form/NameNsDescription.vue +13 -5
  87. package/components/form/PrivateRegistry.constants.ts +7 -0
  88. package/components/form/PrivateRegistry.vue +253 -18
  89. package/components/form/ResourceTabs/index.vue +1 -0
  90. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  91. package/components/form/__tests__/FileSelector.test.ts +23 -0
  92. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  93. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  94. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  95. package/components/formatter/EtcdSnapshotName.vue +73 -0
  96. package/components/formatter/InternalExternalIP.vue +10 -4
  97. package/components/formatter/ServiceTargets.vue +26 -7
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  99. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  100. package/components/nav/Header.vue +12 -1
  101. package/components/nav/TopLevelMenu.vue +7 -2
  102. package/components/nav/__tests__/Header.test.ts +15 -0
  103. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  104. package/components/templates/default.vue +16 -4
  105. package/components/templates/home.vue +9 -4
  106. package/components/templates/plain.vue +9 -4
  107. package/composables/useHelmOpResources.test.ts +56 -0
  108. package/composables/useHelmOpResources.ts +32 -0
  109. package/composables/useStateColor.test.ts +325 -0
  110. package/composables/useStateColor.ts +128 -0
  111. package/config/features.js +1 -0
  112. package/config/home-links.js +1 -1
  113. package/config/labels-annotations.js +3 -0
  114. package/config/product/explorer.js +17 -4
  115. package/config/product/manager.js +8 -0
  116. package/config/router/index.js +16 -0
  117. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  118. package/config/router/navigation-guards/authentication.js +10 -4
  119. package/config/router/routes.js +20 -6
  120. package/config/secret.ts +10 -0
  121. package/config/settings.ts +6 -4
  122. package/config/table-headers.js +3 -4
  123. package/config/types.js +16 -0
  124. package/core/plugin-products-base.ts +3 -3
  125. package/core/plugin-types.ts +83 -30
  126. package/core/plugin.ts +3 -0
  127. package/core/types-provisioning.ts +34 -1
  128. package/core/types.ts +15 -2
  129. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  130. package/detail/__tests__/workload.test.ts +3 -152
  131. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  132. package/detail/provisioning.cattle.io.cluster.vue +109 -7
  133. package/detail/workload/index.vue +12 -55
  134. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  135. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  136. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  137. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  138. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
  139. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  140. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  141. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  142. package/edit/auth/__tests__/azuread.test.ts +34 -9
  143. package/edit/auth/__tests__/github.test.ts +234 -0
  144. package/edit/auth/__tests__/oidc.test.ts +26 -6
  145. package/edit/auth/__tests__/saml.test.ts +196 -0
  146. package/edit/auth/azuread.vue +128 -95
  147. package/edit/auth/github.vue +72 -13
  148. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  149. package/edit/auth/ldap/config.vue +8 -0
  150. package/edit/auth/ldap/index.vue +75 -1
  151. package/edit/auth/oidc.vue +119 -73
  152. package/edit/auth/saml.vue +76 -12
  153. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  154. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  155. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  156. package/edit/fleet.cattle.io.helmop.vue +542 -141
  157. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  158. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  159. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  160. package/edit/management.cattle.io.user.vue +5 -2
  161. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  162. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  163. package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
  164. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  165. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  166. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  167. package/list/group.principal.vue +5 -4
  168. package/list/harvesterhci.io.management.cluster.vue +8 -9
  169. package/list/management.cattle.io.user.vue +12 -9
  170. package/list/provisioning.cattle.io.cluster.vue +16 -10
  171. package/mixins/__tests__/auth-config.test.ts +90 -0
  172. package/mixins/__tests__/chart.test.ts +94 -0
  173. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  174. package/mixins/auth-config.js +7 -0
  175. package/mixins/chart.js +11 -2
  176. package/mixins/child-hook.js +12 -6
  177. package/mixins/create-edit-view/impl.js +5 -3
  178. package/mixins/resource-fetch-api-pagination.js +21 -1
  179. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  180. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  181. package/models/__tests__/fleet-application.test.ts +175 -0
  182. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  183. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  184. package/models/__tests__/management.cattle.io.node.ts +22 -0
  185. package/models/__tests__/namespace.test.ts +36 -0
  186. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
  187. package/models/__tests__/secret.test.ts +68 -1
  188. package/models/__tests__/workload.test.ts +401 -26
  189. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  190. package/models/compliance.cattle.io.clusterscan.js +39 -4
  191. package/models/fleet-application.js +4 -0
  192. package/models/fleet.cattle.io.helmop.js +20 -1
  193. package/models/management.cattle.io.cluster.js +39 -5
  194. package/models/management.cattle.io.node.js +44 -3
  195. package/models/namespace.js +1 -1
  196. package/models/pod.js +46 -3
  197. package/models/provisioning.cattle.io.cluster.js +64 -14
  198. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  199. package/models/secret.js +19 -0
  200. package/models/workload.js +120 -20
  201. package/models/workload.service.js +5 -0
  202. package/package.json +14 -13
  203. package/pages/about.vue +5 -6
  204. package/pages/auth/login.vue +0 -35
  205. package/pages/auth/setup.vue +11 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  208. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  209. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  210. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  211. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  212. package/pages/c/_cluster/apps/charts/install.vue +236 -144
  213. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  214. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  215. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  216. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  217. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  218. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  219. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  220. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  221. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  222. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  223. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  224. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  225. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  226. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  227. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  228. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  229. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  230. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  231. package/pages/c/_cluster/fleet/index.vue +2 -2
  232. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  233. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  234. package/pages/fail-whale.vue +16 -11
  235. package/pages/home.vue +16 -46
  236. package/pkg/require-asset.lib.js +25 -0
  237. package/pkg/vue.config.js +7 -0
  238. package/plugins/clean-html.d.ts +9 -0
  239. package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
  240. package/plugins/dashboard-store/getters.js +0 -1
  241. package/plugins/dashboard-store/resource-class.js +114 -19
  242. package/plugins/steve/__tests__/actions.test.ts +212 -0
  243. package/plugins/steve/actions.js +96 -0
  244. package/plugins/steve/steve-pagination-utils.ts +1 -1
  245. package/rancher-components/Accordion/Accordion.vue +53 -9
  246. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  247. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  248. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  249. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  250. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  251. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  252. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  253. package/rancher-components/RcButton/RcButton.vue +94 -15
  254. package/rancher-components/RcButton/index.ts +1 -1
  255. package/rancher-components/RcButton/types.ts +3 -0
  256. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  257. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  258. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  259. package/rancher-components/RcSection/RcSection.vue +28 -3
  260. package/scripts/extension/helm/package/Dockerfile +1 -1
  261. package/scripts/test-plugins-build.sh +2 -1
  262. package/store/__tests__/features.test.ts +131 -0
  263. package/store/__tests__/growl.test.ts +374 -0
  264. package/store/__tests__/modal.test.ts +131 -0
  265. package/store/__tests__/notifications.test.ts +434 -0
  266. package/store/__tests__/slideInPanel.test.ts +88 -0
  267. package/store/__tests__/type-map.utils.test.ts +433 -0
  268. package/store/catalog.js +57 -0
  269. package/store/features.js +4 -0
  270. package/store/plugins.js +7 -4
  271. package/types/components/buttonGroup.ts +5 -0
  272. package/types/shell/index.d.ts +166 -70
  273. package/utils/__tests__/auth.test.ts +273 -0
  274. package/utils/__tests__/computed.test.ts +193 -0
  275. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  276. package/utils/__tests__/dom.test.ts +81 -0
  277. package/utils/__tests__/duration.test.ts +37 -1
  278. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  279. package/utils/__tests__/fleet-appco.test.ts +312 -0
  280. package/utils/__tests__/monitoring.test.ts +130 -0
  281. package/utils/__tests__/object.test.ts +22 -0
  282. package/utils/__tests__/operation-cr.test.ts +34 -0
  283. package/utils/__tests__/platform.test.ts +91 -0
  284. package/utils/__tests__/position.test.ts +237 -0
  285. package/utils/__tests__/provider.test.ts +51 -1
  286. package/utils/__tests__/queue.test.ts +232 -0
  287. package/utils/__tests__/release-notes.test.ts +221 -0
  288. package/utils/__tests__/router.test.js +254 -1
  289. package/utils/__tests__/select.test.ts +208 -0
  290. package/utils/__tests__/time.test.ts +265 -1
  291. package/utils/__tests__/title.test.ts +47 -0
  292. package/utils/__tests__/width.test.ts +53 -0
  293. package/utils/__tests__/window.test.ts +158 -0
  294. package/utils/__tests__/xccdf.test.ts +126 -6
  295. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  296. package/utils/crypto/__tests__/index.test.ts +144 -0
  297. package/utils/duration.ts +104 -0
  298. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  299. package/utils/dynamic-content/info.ts +2 -1
  300. package/utils/error.js +13 -0
  301. package/utils/fleet-appco.ts +323 -0
  302. package/utils/object.js +22 -2
  303. package/utils/operation-cr.js +19 -0
  304. package/utils/provider.ts +12 -0
  305. package/utils/require-asset.ts +7 -0
  306. package/utils/validators/__tests__/container-images.test.ts +104 -0
  307. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  308. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  309. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  310. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  311. package/utils/validators/private-registry.ts +15 -4
  312. package/utils/xccdf.ts +39 -42
  313. package/vue.config.js +1 -1
  314. package/pages/support/index.vue +0 -264
  315. package/utils/duration.js +0 -43
@@ -0,0 +1,374 @@
1
+ import { state, getters, mutations, actions } from '@shell/store/growl';
2
+ import { NotificationLevel } from '@shell/types/notifications';
3
+
4
+ describe('growl store', () => {
5
+ describe('state', () => {
6
+ it('returns initial state with nextId=1 and empty stack', () => {
7
+ const result = state();
8
+
9
+ expect(result.nextId).toStrictEqual(1);
10
+ expect(result.stack).toStrictEqual([]);
11
+ });
12
+ });
13
+
14
+ describe('getters', () => {
15
+ const item1 = {
16
+ id: 1, color: 'success', title: 'item 1'
17
+ };
18
+ const item2 = {
19
+ id: 2, color: 'error', title: 'item 2'
20
+ };
21
+ let mockState: ReturnType<typeof state>;
22
+
23
+ beforeEach(() => {
24
+ mockState = state();
25
+ mockState.stack = [item1, item2];
26
+ });
27
+
28
+ describe('find', () => {
29
+ it('finds an item by key and value', () => {
30
+ const result = getters.find(mockState)({ key: 'color', val: 'error' });
31
+
32
+ expect(result).toStrictEqual(item2);
33
+ });
34
+
35
+ it('returns undefined when no item matches', () => {
36
+ const result = getters.find(mockState)({ key: 'color', val: 'warning' });
37
+
38
+ expect(result).toBeUndefined();
39
+ });
40
+ });
41
+
42
+ describe('byId', () => {
43
+ it('finds an item by id', () => {
44
+ const result = getters.byId(mockState)(2);
45
+
46
+ expect(result).toStrictEqual(item2);
47
+ });
48
+
49
+ it('returns undefined when id not found', () => {
50
+ const result = getters.byId(mockState)(99);
51
+
52
+ expect(result).toBeUndefined();
53
+ });
54
+ });
55
+ });
56
+
57
+ describe('mutations', () => {
58
+ describe('add', () => {
59
+ it('adds an item to the front of the stack', () => {
60
+ const s = state();
61
+
62
+ mutations.add(s, { title: 'hello' });
63
+
64
+ expect(s.stack).toHaveLength(1);
65
+ expect(s.stack[0]).toMatchObject({ title: 'hello' });
66
+ });
67
+
68
+ it('assigns auto-incremented ids and updates nextId', () => {
69
+ const s = state();
70
+
71
+ mutations.add(s, { title: 'first' });
72
+ mutations.add(s, { title: 'second' });
73
+
74
+ expect(s.stack[1].id).toStrictEqual(1);
75
+ expect(s.stack[0].id).toStrictEqual(2);
76
+ expect(s.nextId).toStrictEqual(3);
77
+ });
78
+
79
+ it('sets the started timestamp on the new item', () => {
80
+ const s = state();
81
+ const before = Date.now();
82
+
83
+ mutations.add(s, { title: 'timed' });
84
+
85
+ const after = Date.now();
86
+
87
+ expect(s.stack[0].started).toBeGreaterThanOrEqual(before);
88
+ expect(s.stack[0].started).toBeLessThanOrEqual(after);
89
+ });
90
+
91
+ it('prepends new items so the most recent is first', () => {
92
+ const s = state();
93
+
94
+ mutations.add(s, { title: 'first' });
95
+ mutations.add(s, { title: 'second' });
96
+
97
+ expect(s.stack[0].title).toStrictEqual('second');
98
+ expect(s.stack[1].title).toStrictEqual('first');
99
+ });
100
+
101
+ it('removes the oldest item when the stack reaches MAX_GROWLS (5)', () => {
102
+ const s = state();
103
+
104
+ for (let i = 0; i < 5; i++) {
105
+ mutations.add(s, { title: `item-${ i }` });
106
+ }
107
+
108
+ mutations.add(s, { title: 'overflow' });
109
+
110
+ expect(s.stack).toHaveLength(5);
111
+ expect(s.stack[0].title).toStrictEqual('overflow');
112
+ });
113
+ });
114
+
115
+ describe('remove', () => {
116
+ it('removes an item from the stack by id', () => {
117
+ const s = state();
118
+
119
+ mutations.add(s, { title: 'to remove' });
120
+ const { id } = s.stack[0];
121
+
122
+ mutations.remove(s, id);
123
+
124
+ expect(s.stack).toHaveLength(0);
125
+ });
126
+
127
+ it('is a no-op when the id does not exist', () => {
128
+ const s = state();
129
+
130
+ mutations.add(s, { title: 'keep' });
131
+ mutations.remove(s, 999);
132
+
133
+ expect(s.stack).toHaveLength(1);
134
+ });
135
+ });
136
+
137
+ describe('clear', () => {
138
+ it('empties the stack', () => {
139
+ const s = state();
140
+
141
+ mutations.add(s, { title: 'a' });
142
+ mutations.add(s, { title: 'b' });
143
+ mutations.clear(s);
144
+
145
+ expect(s.stack).toHaveLength(0);
146
+ });
147
+ });
148
+ });
149
+
150
+ describe('actions', () => {
151
+ let commit: jest.Mock;
152
+ let dispatch: jest.Mock;
153
+
154
+ beforeEach(() => {
155
+ commit = jest.fn();
156
+ dispatch = jest.fn();
157
+ });
158
+
159
+ describe('clear', () => {
160
+ it('commits the clear mutation', async() => {
161
+ await actions.clear({ commit } as any);
162
+
163
+ expect(commit).toHaveBeenCalledWith('clear');
164
+ });
165
+ });
166
+
167
+ describe('remove', () => {
168
+ it('commits the remove mutation with the given id', async() => {
169
+ await actions.remove({ commit } as any, 42);
170
+
171
+ expect(commit).toHaveBeenCalledWith('remove', 42);
172
+ });
173
+ });
174
+
175
+ describe('close', () => {
176
+ it('removes the growl and dispatches notifications/markRead when the growl has a notification', async() => {
177
+ const notifId = 'notif-123';
178
+ const mockGetters = { byId: jest.fn().mockReturnValue({ notification: notifId }) };
179
+
180
+ dispatch.mockResolvedValue(undefined);
181
+
182
+ await actions.close({
183
+ commit, dispatch, getters: mockGetters
184
+ } as any, 7);
185
+
186
+ expect(commit).toHaveBeenCalledWith('remove', 7);
187
+ expect(dispatch).toHaveBeenCalledWith('notifications/markRead', notifId, { root: true });
188
+ });
189
+
190
+ it('removes the growl without dispatching when the growl has no notification', async() => {
191
+ const mockGetters = { byId: jest.fn().mockReturnValue({ notification: undefined }) };
192
+
193
+ await actions.close({
194
+ commit, dispatch, getters: mockGetters
195
+ } as any, 3);
196
+
197
+ expect(commit).toHaveBeenCalledWith('remove', 3);
198
+ expect(dispatch).not.toHaveBeenCalled();
199
+ });
200
+ });
201
+
202
+ describe('success', () => {
203
+ it('dispatches notifications/fromGrowl with Success level and commits add with success styling', async() => {
204
+ const mockNotification = 'notif-success';
205
+
206
+ dispatch.mockResolvedValue(mockNotification);
207
+
208
+ await actions.success({ commit, dispatch } as any, { title: 'Done', message: 'ok' });
209
+
210
+ expect(dispatch).toHaveBeenCalledWith('notifications/fromGrowl', {
211
+ title: 'Done',
212
+ message: 'ok',
213
+ level: NotificationLevel.Success,
214
+ }, { root: true });
215
+ expect(commit).toHaveBeenCalledWith('add', {
216
+ color: 'success',
217
+ icon: 'checkmark',
218
+ timeout: 5000,
219
+ notification: mockNotification,
220
+ title: 'Done',
221
+ message: 'ok',
222
+ });
223
+ });
224
+ });
225
+
226
+ describe('info', () => {
227
+ it('commits add with info styling without dispatching a notification', async() => {
228
+ await actions.info({ commit } as any, { title: 'FYI', message: 'just info' });
229
+
230
+ expect(dispatch).not.toHaveBeenCalled();
231
+ expect(commit).toHaveBeenCalledWith('add', {
232
+ color: 'info',
233
+ icon: 'info',
234
+ timeout: 5000,
235
+ title: 'FYI',
236
+ message: 'just info',
237
+ });
238
+ });
239
+ });
240
+
241
+ describe('warning', () => {
242
+ it('dispatches notifications/fromGrowl with Warning level and commits add with warning styling', async() => {
243
+ const mockNotification = 'notif-warning';
244
+
245
+ dispatch.mockResolvedValue(mockNotification);
246
+
247
+ await actions.warning({ commit, dispatch } as any, { title: 'Careful', message: 'watch out' });
248
+
249
+ expect(dispatch).toHaveBeenCalledWith('notifications/fromGrowl', {
250
+ title: 'Careful',
251
+ message: 'watch out',
252
+ level: NotificationLevel.Warning,
253
+ }, { root: true });
254
+ expect(commit).toHaveBeenCalledWith('add', {
255
+ color: 'warning',
256
+ icon: 'warning',
257
+ timeout: 5000,
258
+ notification: mockNotification,
259
+ title: 'Careful',
260
+ message: 'watch out',
261
+ });
262
+ });
263
+ });
264
+
265
+ describe('error', () => {
266
+ it('dispatches notifications/fromGrowl with Error level and commits add with error styling', async() => {
267
+ const mockNotification = 'notif-error';
268
+
269
+ dispatch.mockResolvedValue(mockNotification);
270
+
271
+ await actions.error({ commit, dispatch } as any, { title: 'Oops', message: 'failed' });
272
+
273
+ expect(dispatch).toHaveBeenCalledWith('notifications/fromGrowl', {
274
+ title: 'Oops',
275
+ message: 'failed',
276
+ level: NotificationLevel.Error,
277
+ }, { root: true });
278
+ expect(commit).toHaveBeenCalledWith('add', {
279
+ color: 'error',
280
+ icon: 'error',
281
+ timeout: 5000,
282
+ notification: mockNotification,
283
+ title: 'Oops',
284
+ message: 'failed',
285
+ });
286
+ });
287
+ });
288
+
289
+ describe('fromError', () => {
290
+ it('dispatches fromGrowl with the stringified error message and commits add with error styling', async() => {
291
+ const mockNotification = 'notif-from-error';
292
+
293
+ dispatch.mockResolvedValue(mockNotification);
294
+
295
+ const err = new Error('something broke');
296
+
297
+ await actions.fromError({ commit, dispatch } as any, { title: 'Error title', err });
298
+
299
+ expect(dispatch).toHaveBeenCalledWith('notifications/fromGrowl', {
300
+ title: 'Error title',
301
+ message: 'something broke',
302
+ level: NotificationLevel.Error,
303
+ }, { root: true });
304
+ expect(commit).toHaveBeenCalledWith('add', {
305
+ color: 'error',
306
+ icon: 'error',
307
+ timeout: 5000,
308
+ notification: mockNotification,
309
+ title: 'Error title',
310
+ message: 'something broke',
311
+ });
312
+ });
313
+ });
314
+
315
+ describe('notification', () => {
316
+ it.each([
317
+ {
318
+ desc: 'Success level',
319
+ level: NotificationLevel.Success,
320
+ color: 'success',
321
+ icon: 'checkmark',
322
+ },
323
+ {
324
+ desc: 'Warning level',
325
+ level: NotificationLevel.Warning,
326
+ color: 'warning',
327
+ icon: 'warning',
328
+ },
329
+ {
330
+ desc: 'Error level',
331
+ level: NotificationLevel.Error,
332
+ color: 'error',
333
+ icon: 'error',
334
+ },
335
+ ])('commits add for $desc', ({ level, color, icon }) => {
336
+ const notif = {
337
+ id: 'n1',
338
+ title: 'test',
339
+ message: 'msg',
340
+ level,
341
+ };
342
+
343
+ actions.notification({ commit } as any, notif);
344
+
345
+ expect(commit).toHaveBeenCalledWith('add', {
346
+ title: 'test',
347
+ message: 'msg',
348
+ notification: 'n1',
349
+ timeout: 5000,
350
+ color,
351
+ icon,
352
+ });
353
+ });
354
+
355
+ it.each([
356
+ { desc: 'Info level', level: NotificationLevel.Info },
357
+ { desc: 'Announcement level', level: NotificationLevel.Announcement },
358
+ { desc: 'Task level', level: NotificationLevel.Task },
359
+ { desc: 'Hidden level', level: NotificationLevel.Hidden },
360
+ ])('skips commit for $desc', ({ level }) => {
361
+ const notif = {
362
+ id: 'n1',
363
+ title: 'test',
364
+ message: 'msg',
365
+ level,
366
+ };
367
+
368
+ actions.notification({ commit } as any, notif);
369
+
370
+ expect(commit).not.toHaveBeenCalled();
371
+ });
372
+ });
373
+ });
374
+ });
@@ -0,0 +1,131 @@
1
+ import modalStore from '../modal';
2
+
3
+ describe('modal store', () => {
4
+ let s: ReturnType<typeof modalStore.state>;
5
+ const fakeComponent = { name: 'FakeComponent' } as any;
6
+
7
+ beforeEach(() => {
8
+ s = modalStore.state();
9
+ });
10
+
11
+ describe('state', () => {
12
+ it('returns initial default state', () => {
13
+ expect(s.isOpen).toBe(false);
14
+ expect(s.component).toBeNull();
15
+ expect(s.componentProps).toStrictEqual({});
16
+ expect(s.resources).toStrictEqual([]);
17
+ expect(s.closeOnClickOutside).toBe(false);
18
+ expect(s.modalWidth).toStrictEqual('600px');
19
+ expect(s.modalSticky).toBe(false);
20
+ });
21
+ });
22
+
23
+ describe('mutations', () => {
24
+ describe('openModal', () => {
25
+ it('sets isOpen to true', () => {
26
+ modalStore.mutations.openModal(s, { component: fakeComponent });
27
+
28
+ expect(s.isOpen).toBe(true);
29
+ });
30
+
31
+ it('stores the component reference', () => {
32
+ modalStore.mutations.openModal(s, { component: fakeComponent });
33
+
34
+ expect(s.component).toBe(fakeComponent);
35
+ });
36
+
37
+ it('sets componentProps from payload', () => {
38
+ modalStore.mutations.openModal(s, { component: fakeComponent, componentProps: { foo: 'bar' } });
39
+
40
+ expect(s.componentProps).toStrictEqual({ foo: 'bar' });
41
+ });
42
+
43
+ it('defaults componentProps to empty object when not provided', () => {
44
+ modalStore.mutations.openModal(s, { component: fakeComponent });
45
+
46
+ expect(s.componentProps).toStrictEqual({});
47
+ });
48
+
49
+ it('preserves resources when payload resources is an array', () => {
50
+ const res = [{ id: 1 }, { id: 2 }];
51
+
52
+ modalStore.mutations.openModal(s, { component: fakeComponent, resources: res });
53
+
54
+ expect(s.resources).toStrictEqual(res);
55
+ });
56
+
57
+ it('wraps a single non-array resource in an array', () => {
58
+ const res = { id: 1 };
59
+
60
+ modalStore.mutations.openModal(s, { component: fakeComponent, resources: res as any });
61
+
62
+ expect(s.resources).toStrictEqual([res]);
63
+ });
64
+
65
+ it('sets resources to empty array when not provided', () => {
66
+ modalStore.mutations.openModal(s, { component: fakeComponent });
67
+
68
+ expect(s.resources).toStrictEqual([]);
69
+ });
70
+
71
+ it('sets closeOnClickOutside to true when specified', () => {
72
+ modalStore.mutations.openModal(s, { component: fakeComponent, closeOnClickOutside: true });
73
+
74
+ expect(s.closeOnClickOutside).toBe(true);
75
+ });
76
+
77
+ it('defaults closeOnClickOutside to false when not provided', () => {
78
+ modalStore.mutations.openModal(s, { component: fakeComponent });
79
+
80
+ expect(s.closeOnClickOutside).toBe(false);
81
+ });
82
+
83
+ it('sets custom modalWidth from payload', () => {
84
+ modalStore.mutations.openModal(s, { component: fakeComponent, modalWidth: '800px' });
85
+
86
+ expect(s.modalWidth).toStrictEqual('800px');
87
+ });
88
+
89
+ it('defaults modalWidth to 600px when not provided', () => {
90
+ modalStore.mutations.openModal(s, { component: fakeComponent });
91
+
92
+ expect(s.modalWidth).toStrictEqual('600px');
93
+ });
94
+
95
+ it('sets modalSticky to true when specified', () => {
96
+ modalStore.mutations.openModal(s, { component: fakeComponent, modalSticky: true });
97
+
98
+ expect(s.modalSticky).toBe(true);
99
+ });
100
+
101
+ it('defaults modalSticky to false when not provided', () => {
102
+ modalStore.mutations.openModal(s, { component: fakeComponent });
103
+
104
+ expect(s.modalSticky).toBe(false);
105
+ });
106
+ });
107
+
108
+ describe('closeModal', () => {
109
+ it('resets all state to defaults', () => {
110
+ modalStore.mutations.openModal(s, {
111
+ component: fakeComponent,
112
+ componentProps: { foo: 'bar' },
113
+ resources: [{ id: 1 }],
114
+ closeOnClickOutside: true,
115
+ modalWidth: '800px',
116
+ modalSticky: true,
117
+ });
118
+
119
+ modalStore.mutations.closeModal(s);
120
+
121
+ expect(s.isOpen).toBe(false);
122
+ expect(s.component).toBeNull();
123
+ expect(s.componentProps).toStrictEqual({});
124
+ expect(s.resources).toStrictEqual([]);
125
+ expect(s.closeOnClickOutside).toBe(false);
126
+ expect(s.modalWidth).toStrictEqual('600px');
127
+ expect(s.modalSticky).toBe(false);
128
+ });
129
+ });
130
+ });
131
+ });