@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
@@ -170,6 +170,81 @@ describe('component: NameNsDescription', () => {
170
170
  expect(nameInput.element.value).toBe('Default');
171
171
  });
172
172
 
173
+ it('should set namespace to a plain string when forceNamespace prop is provided', () => {
174
+ const forcedNs = 'cert-manager';
175
+ const store = createStore({
176
+ getters: {
177
+ allowedNamespaces: () => () => ({}),
178
+ currentStore: () => () => 'cluster',
179
+ 'cluster/schemaFor': () => jest.fn()
180
+ }
181
+ });
182
+
183
+ const wrapper = mount(NameNsDescription, {
184
+ props: {
185
+ value: {
186
+ setAnnotation: jest.fn(),
187
+ metadata: { namespace: '' }
188
+ },
189
+ mode: 'create',
190
+ forceNamespace: forcedNs,
191
+ },
192
+ global: {
193
+ provide: { store },
194
+ mocks: {
195
+ $store: {
196
+ dispatch: jest.fn(),
197
+ getters: {
198
+ namespaces: jest.fn(),
199
+ 'i18n/t': jest.fn(),
200
+ },
201
+ },
202
+ },
203
+ },
204
+ });
205
+
206
+ expect(typeof (wrapper.vm as any).namespace).toBe('string');
207
+ expect((wrapper.vm as any).namespace).toBe(forcedNs);
208
+ });
209
+
210
+ it('should set metadata.namespace to a plain string when falling back to defaultNamespace', () => {
211
+ const defaultNs = 'default';
212
+ const metadata: Record<string, unknown> = {};
213
+ const store = createStore({
214
+ getters: {
215
+ allowedNamespaces: () => () => ({}),
216
+ currentStore: () => () => 'cluster',
217
+ 'cluster/schemaFor': () => jest.fn(),
218
+ defaultNamespace: () => defaultNs,
219
+ }
220
+ });
221
+
222
+ mount(NameNsDescription, {
223
+ props: {
224
+ value: {
225
+ setAnnotation: jest.fn(),
226
+ metadata,
227
+ },
228
+ mode: 'create',
229
+ },
230
+ global: {
231
+ provide: { store },
232
+ mocks: {
233
+ $store: {
234
+ dispatch: jest.fn(),
235
+ getters: {
236
+ namespaces: jest.fn(),
237
+ 'i18n/t': jest.fn(),
238
+ },
239
+ },
240
+ },
241
+ },
242
+ });
243
+
244
+ expect(typeof metadata.namespace).toBe('string');
245
+ expect(metadata.namespace).toBe(defaultNs);
246
+ });
247
+
173
248
  it('sets the name using the nameKey prop', () => {
174
249
  const namespaceName = 'test';
175
250
  const store = createStore({
@@ -1,133 +1,523 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
+ import { createStore, Store } from 'vuex';
2
3
  import PrivateRegistry from '@shell/components/form/PrivateRegistry.vue';
4
+ import { PRIVATE_REGISTRY_CONTEXT } from '@shell/components/form/PrivateRegistry.constants';
3
5
  import { Checkbox } from '@components/Form/Checkbox';
4
6
  import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
7
+ import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret.vue';
8
+ import Banner from '@components/Banner/Banner.vue';
9
+ import { SETTING } from '@shell/config/settings';
5
10
 
6
- const defaultMocks = {
7
- $store: {
11
+ const buildStore = (settings: Record<string, any> = {}): Store<any> => {
12
+ return createStore({
8
13
  getters: {
9
- 'i18n/t': (text: string) => text,
10
- t: (text: string) => text,
11
- }
12
- }
14
+ 'i18n/t': () => (text: string) => text,
15
+ t: () => (text: string) => text,
16
+ 'management/byId': () => (_type: string, id: string) => {
17
+ if (id === SETTING.SYSTEM_DEFAULT_REGISTRY && settings.registry) {
18
+ return { value: settings.registry };
19
+ }
20
+ if (id === SETTING.SYSTEM_DEFAULT_REGISTRY_PULL_SECRETS && settings.pullSecrets) {
21
+ return { value: settings.pullSecrets };
22
+ }
23
+
24
+ return null;
25
+ },
26
+ },
27
+ });
13
28
  };
14
29
 
15
- const mountPrivateRegistry = (props = {}) => {
30
+ const mountPrivateRegistry = (props = {}, storeSettings: Record<string, any> = {}) => {
31
+ const store = buildStore(storeSettings);
32
+
16
33
  return shallowMount(PrivateRegistry, {
17
34
  props: {
18
35
  mode: 'edit',
19
36
  ...props
20
37
  },
21
- global: { mocks: defaultMocks }
38
+ global: {
39
+ plugins: [store],
40
+ mocks: { $store: store }
41
+ }
22
42
  });
23
43
  };
24
44
 
25
45
  describe('privateRegistry', () => {
26
- it('should render the info banner', () => {
27
- const wrapper = mountPrivateRegistry();
28
- const banner = wrapper.find('[color="info"]');
46
+ describe('basic rendering', () => {
47
+ it('should render the info banner', () => {
48
+ const wrapper = mountPrivateRegistry();
49
+ const banners = wrapper.findAllComponents(Banner);
29
50
 
30
- expect(banner.exists()).toBe(true);
31
- });
51
+ expect(banners.some((b) => b.props('color') === 'info')).toBe(true);
52
+ });
32
53
 
33
- it('should render the enable checkbox', () => {
34
- const wrapper = mountPrivateRegistry();
35
- const checkbox = wrapper.findComponent(Checkbox);
54
+ it('should render the enable checkbox', () => {
55
+ const wrapper = mountPrivateRegistry();
56
+ const checkbox = wrapper.findComponent(Checkbox);
36
57
 
37
- expect(checkbox.exists()).toBe(true);
38
- });
58
+ expect(checkbox.exists()).toBe(true);
59
+ });
39
60
 
40
- it('should not show the URL input when no value is provided', () => {
41
- const wrapper = mountPrivateRegistry();
61
+ it('should not show the URL input when no value is provided', () => {
62
+ const wrapper = mountPrivateRegistry();
42
63
 
43
- expect(wrapper.findComponent(LabeledInput).exists()).toBe(false);
44
- });
64
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(false);
65
+ });
45
66
 
46
- it('should show the URL input when a value is provided', () => {
47
- const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
67
+ it('should show the URL input when a value is provided', () => {
68
+ const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
48
69
 
49
- expect(wrapper.findComponent(LabeledInput).exists()).toBe(true);
70
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(true);
71
+ });
50
72
  });
51
73
 
52
- it('should show the URL input when checkbox is checked', async() => {
53
- const wrapper = mountPrivateRegistry();
74
+ describe('enable/disable toggle', () => {
75
+ it('should show the URL input when checkbox is checked', async() => {
76
+ const wrapper = mountPrivateRegistry();
77
+
78
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(false);
54
79
 
55
- expect(wrapper.findComponent(LabeledInput).exists()).toBe(false);
80
+ const checkbox = wrapper.findComponent(Checkbox);
56
81
 
57
- const checkbox = wrapper.findComponent(Checkbox);
82
+ await checkbox.vm.$emit('update:value', true);
83
+ await wrapper.vm.$nextTick();
58
84
 
59
- await checkbox.vm.$emit('update:value', true);
60
- await wrapper.vm.$nextTick();
85
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(true);
86
+ });
87
+
88
+ it('should emit update:value with undefined when checkbox is unchecked', async() => {
89
+ const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
90
+ const checkbox = wrapper.findComponent(Checkbox);
61
91
 
62
- expect(wrapper.findComponent(LabeledInput).exists()).toBe(true);
92
+ await checkbox.vm.$emit('update:value', false);
93
+ await wrapper.vm.$nextTick();
94
+
95
+ expect(wrapper.emitted('update:value')).toHaveLength(1);
96
+ expect(wrapper.emitted('update:value')![0]).toStrictEqual([undefined]);
97
+ });
98
+
99
+ it('should emit update:pullSecret with undefined when checkbox is unchecked', async() => {
100
+ const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
101
+ const checkbox = wrapper.findComponent(Checkbox);
102
+
103
+ await checkbox.vm.$emit('update:value', false);
104
+ await wrapper.vm.$nextTick();
105
+
106
+ expect(wrapper.emitted('update:pullSecret')).toHaveLength(1);
107
+ expect(wrapper.emitted('update:pullSecret')![0]).toStrictEqual([undefined]);
108
+ });
109
+
110
+ it('should auto-enable the checkbox when value changes from null to a string', async() => {
111
+ const wrapper = mountPrivateRegistry();
112
+
113
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(false);
114
+
115
+ await wrapper.setProps({ value: 'registry.example.com' });
116
+
117
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(true);
118
+ });
119
+
120
+ it('should sync showInput when enabled prop changes', async() => {
121
+ const wrapper = mountPrivateRegistry({ enabled: false });
122
+
123
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(false);
124
+
125
+ await wrapper.setProps({ enabled: true });
126
+
127
+ expect(wrapper.findComponent(LabeledInput).exists()).toBe(true);
128
+ });
63
129
  });
64
130
 
65
- it('should emit update:value with null when checkbox is unchecked', async() => {
66
- const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
131
+ describe('URL input', () => {
132
+ it('should emit update:value when the URL input changes', async() => {
133
+ const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
134
+ const input = wrapper.findComponent(LabeledInput);
67
135
 
68
- const checkbox = wrapper.findComponent(Checkbox);
136
+ await input.vm.$emit('update:value', 'new-registry.example.com');
137
+
138
+ expect(wrapper.emitted('update:value')).toHaveLength(1);
139
+ expect(wrapper.emitted('update:value')![0]).toStrictEqual(['new-registry.example.com']);
140
+ });
69
141
 
70
- await checkbox.vm.$emit('update:value', false);
71
- await wrapper.vm.$nextTick();
142
+ it('should pass rules to the URL input', () => {
143
+ const mockRule = jest.fn();
144
+ const wrapper = mountPrivateRegistry({
145
+ value: 'registry.example.com',
146
+ rules: [mockRule]
147
+ });
148
+ const input = wrapper.findComponent(LabeledInput);
72
149
 
73
- expect(wrapper.emitted('update:value')).toHaveLength(1);
74
- expect(wrapper.emitted('update:value')![0]).toStrictEqual([null]);
150
+ expect(input.props('rules')).toBeDefined();
151
+ });
152
+
153
+ it('should use globalRegistry from store as fallback value', async() => {
154
+ const wrapper = mountPrivateRegistry(
155
+ {},
156
+ { registry: 'global.registry.io' }
157
+ );
158
+
159
+ // Toggle checkbox to show input without setting value
160
+ const checkbox = wrapper.findComponent(Checkbox);
161
+
162
+ await checkbox.vm.$emit('update:value', true);
163
+ await wrapper.vm.$nextTick();
164
+
165
+ const input = wrapper.findComponent(LabeledInput);
166
+
167
+ expect(input.props('value')).toBe('global.registry.io');
168
+ });
169
+
170
+ it('should use defaultRegistry prop over store setting', async() => {
171
+ const wrapper = mountPrivateRegistry(
172
+ { defaultRegistry: 'prop.registry.io' },
173
+ { registry: 'store.registry.io' }
174
+ );
175
+
176
+ const checkbox = wrapper.findComponent(Checkbox);
177
+
178
+ await checkbox.vm.$emit('update:value', true);
179
+ await wrapper.vm.$nextTick();
180
+
181
+ const input = wrapper.findComponent(LabeledInput);
182
+
183
+ expect(input.props('value')).toBe('prop.registry.io');
184
+ });
75
185
  });
76
186
 
77
- it('should emit update:value when the URL input changes', async() => {
78
- const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
79
- const input = wrapper.findComponent(LabeledInput);
187
+ describe('data-testid', () => {
188
+ it('should apply custom data-testid to checkbox when provided', () => {
189
+ const wrapper = mountPrivateRegistry({ checkboxTestId: 'my-checkbox' });
190
+ const checkbox = wrapper.findComponent(Checkbox);
191
+
192
+ expect(checkbox.attributes('data-testid')).toBe('my-checkbox');
193
+ });
194
+
195
+ it('should apply custom data-testid to input when provided', () => {
196
+ const wrapper = mountPrivateRegistry({
197
+ value: 'registry.example.com',
198
+ inputTestId: 'my-input'
199
+ });
200
+ const input = wrapper.findComponent(LabeledInput);
80
201
 
81
- await input.vm.$emit('update:value', 'new-registry.example.com');
202
+ expect(input.attributes('data-testid')).toBe('my-input');
203
+ });
204
+
205
+ it('should not set data-testid when not provided', () => {
206
+ const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
207
+ const checkbox = wrapper.findComponent(Checkbox);
208
+ const input = wrapper.findComponent(LabeledInput);
82
209
 
83
- expect(wrapper.emitted('update:value')).toHaveLength(1);
84
- expect(wrapper.emitted('update:value')![0]).toStrictEqual(['new-registry.example.com']);
210
+ expect(checkbox.attributes('data-testid')).toBeUndefined();
211
+ expect(input.attributes('data-testid')).toBeUndefined();
212
+ });
85
213
  });
86
214
 
87
- it('should auto-enable the checkbox when value changes from null to a string', async() => {
88
- const wrapper = mountPrivateRegistry();
215
+ describe('pull secrets section', () => {
216
+ it('should show SelectOrCreateAuthSecret when showPullSecrets is true and value is provided', () => {
217
+ const wrapper = mountPrivateRegistry({
218
+ value: 'registry.example.com',
219
+ showPullSecrets: true
220
+ });
221
+
222
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(true);
223
+ });
224
+
225
+ it('should not show SelectOrCreateAuthSecret when showPullSecrets is false', () => {
226
+ const wrapper = mountPrivateRegistry({
227
+ value: 'registry.example.com',
228
+ showPullSecrets: false
229
+ });
230
+
231
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(false);
232
+ });
233
+
234
+ it('should not show SelectOrCreateAuthSecret when no value is provided', () => {
235
+ const wrapper = mountPrivateRegistry({ showPullSecrets: true });
236
+
237
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(false);
238
+ });
239
+
240
+ it('should pass noneLabel to SelectOrCreateAuthSecret when provided', () => {
241
+ const wrapper = mountPrivateRegistry({
242
+ value: 'registry.example.com',
243
+ noneLabel: 'Custom none label'
244
+ });
245
+ const selector = wrapper.findComponent(SelectOrCreateAuthSecret);
89
246
 
90
- expect(wrapper.findComponent(LabeledInput).exists()).toBe(false);
247
+ expect(selector.props('noneLabel')).toBe('Custom none label');
248
+ });
249
+
250
+ it('should emit update:pullSecret when SelectOrCreateAuthSecret emits update:value', async() => {
251
+ const wrapper = mountPrivateRegistry({
252
+ value: 'registry.example.com',
253
+ showPullSecrets: true
254
+ });
255
+ const selector = wrapper.findComponent(SelectOrCreateAuthSecret);
91
256
 
92
- await wrapper.setProps({ value: 'registry.example.com' });
257
+ await selector.vm.$emit('update:value', 'my-secret');
93
258
 
94
- expect(wrapper.findComponent(LabeledInput).exists()).toBe(true);
259
+ expect(wrapper.emitted('update:pullSecret')).toHaveLength(1);
260
+ expect(wrapper.emitted('update:pullSecret')![0]).toStrictEqual(['my-secret']);
261
+ });
95
262
  });
96
263
 
97
- it('should pass rules to the URL input', () => {
98
- const mockRule = jest.fn();
99
- const wrapper = mountPrivateRegistry({
100
- value: 'registry.example.com',
101
- rules: [mockRule]
264
+ describe('default pull secrets', () => {
265
+ it('should load default pull secrets from repoDefaultPullSecrets prop', async() => {
266
+ const wrapper = mountPrivateRegistry({
267
+ value: 'registry.example.com',
268
+ repoDefaultPullSecrets: ['repo-secret-1']
269
+ });
270
+
271
+ await wrapper.vm.$nextTick();
272
+ const selector = wrapper.findComponent(SelectOrCreateAuthSecret);
273
+
274
+ expect(selector.props('noneLabel')).toContain('catalog.chart.registry.pullSecret.defaultLabel');
275
+ });
276
+
277
+ it('should load default pull secrets from store setting when repoDefaultPullSecrets is empty', async() => {
278
+ const wrapper = mountPrivateRegistry(
279
+ {
280
+ value: 'registry.example.com',
281
+ repoDefaultPullSecrets: []
282
+ },
283
+ { pullSecrets: 'store-secret-1' }
284
+ );
285
+
286
+ await wrapper.vm.$nextTick();
287
+ const selector = wrapper.findComponent(SelectOrCreateAuthSecret);
288
+
289
+ expect(selector.props('noneLabel')).toContain('catalog.chart.registry.pullSecret.defaultLabel');
290
+ });
291
+
292
+ it('should prefer repoDefaultPullSecrets over store setting', async() => {
293
+ const wrapper = mountPrivateRegistry(
294
+ {
295
+ value: 'registry.example.com',
296
+ repoDefaultPullSecrets: ['repo-secret']
297
+ },
298
+ { pullSecrets: 'store-secret' }
299
+ );
300
+
301
+ await wrapper.vm.$nextTick();
302
+ const selector = wrapper.findComponent(SelectOrCreateAuthSecret);
303
+
304
+ // The label should reference the repo default, not the store one
305
+ expect(selector.props('noneLabel')).toContain('repo-secret');
306
+ });
307
+
308
+ it('should use generic default label when multiple default pull secrets exist', async() => {
309
+ const wrapper = mountPrivateRegistry({
310
+ value: 'registry.example.com',
311
+ repoDefaultPullSecrets: ['secret-1', 'secret-2']
312
+ });
313
+
314
+ await wrapper.vm.$nextTick();
315
+ const selector = wrapper.findComponent(SelectOrCreateAuthSecret);
316
+
317
+ expect(selector.props('noneLabel')).toBe('catalog.chart.registry.pullSecret.defaultLabelGeneric');
318
+ });
319
+
320
+ it('should show defaults banner when multiple default pull secrets exist', async() => {
321
+ const wrapper = mountPrivateRegistry({
322
+ value: 'registry.example.com',
323
+ repoDefaultPullSecrets: ['secret-1', 'secret-2']
324
+ });
325
+
326
+ await wrapper.vm.$nextTick();
327
+ const banners = wrapper.findAllComponents(Banner).filter((b) => b.props('color') === 'info');
328
+
329
+ // Should have at least the description banner + the defaults banner
330
+ expect(banners.length).toBeGreaterThanOrEqual(2);
102
331
  });
103
- const input = wrapper.findComponent(LabeledInput);
104
332
 
105
- expect(input.attributes('rules')).toBeDefined();
333
+ it('should not show defaults banner when only one default pull secret exists', () => {
334
+ const wrapper = mountPrivateRegistry({
335
+ value: 'registry.example.com',
336
+ repoDefaultPullSecrets: ['single-secret']
337
+ });
338
+ const banners = wrapper.findAllComponents(Banner).filter((b) => b.props('color') === 'info');
339
+
340
+ // Only the description banner
341
+ expect(banners).toHaveLength(1);
342
+ });
106
343
  });
107
344
 
108
- it('should apply custom data-testid to checkbox when provided', () => {
109
- const wrapper = mountPrivateRegistry({ checkboxTestId: 'my-checkbox' });
110
- const checkbox = wrapper.findComponent(Checkbox);
345
+ describe('existing values pull secrets', () => {
346
+ it('should show existing values banner when multiple existing pull secrets are provided', () => {
347
+ const wrapper = mountPrivateRegistry({
348
+ value: 'registry.example.com',
349
+ existingValuesPullSecrets: ['existing-1', 'existing-2']
350
+ });
351
+ const banners = wrapper.findAllComponents(Banner).filter((b) => b.props('color') === 'info');
352
+
353
+ // Description banner + existing values banner
354
+ expect(banners.length).toBeGreaterThanOrEqual(2);
355
+ });
356
+
357
+ it('should hide SelectOrCreateAuthSecret when multiple existing pull secrets are provided', () => {
358
+ const wrapper = mountPrivateRegistry({
359
+ value: 'registry.example.com',
360
+ existingValuesPullSecrets: ['existing-1', 'existing-2']
361
+ });
362
+
363
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(false);
364
+ });
365
+
366
+ it('should show SelectOrCreateAuthSecret when only one existing pull secret is provided', () => {
367
+ const wrapper = mountPrivateRegistry({
368
+ value: 'registry.example.com',
369
+ existingValuesPullSecrets: ['single-existing']
370
+ });
111
371
 
112
- expect(checkbox.attributes('data-testid')).toBe('my-checkbox');
372
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(true);
373
+ });
374
+
375
+ it('should show existing values banner instead of defaults banner when both have multiple', () => {
376
+ const wrapper = mountPrivateRegistry({
377
+ value: 'registry.example.com',
378
+ existingValuesPullSecrets: ['existing-1', 'existing-2'],
379
+ repoDefaultPullSecrets: ['default-1', 'default-2']
380
+ });
381
+ // Existing values banner takes priority, defaults banner hidden
382
+ const banners = wrapper.findAllComponents(Banner).filter((b) => b.props('color') === 'info');
383
+
384
+ // Description banner + existing values banner (not defaults banner)
385
+ expect(banners).toHaveLength(2);
386
+ });
113
387
  });
114
388
 
115
- it('should apply custom data-testid to input when provided', () => {
116
- const wrapper = mountPrivateRegistry({
117
- value: 'registry.example.com',
118
- inputTestId: 'my-input'
389
+ describe('skip pull secrets', () => {
390
+ it('should show skip checkbox only for charts context', () => {
391
+ const wrapper = mountPrivateRegistry({
392
+ value: 'registry.example.com',
393
+ context: PRIVATE_REGISTRY_CONTEXT.CHARTS
394
+ });
395
+ const skipCheckbox = wrapper.find('[data-testid="registry-skip-pull-secrets-checkbox"]');
396
+
397
+ expect(skipCheckbox.exists()).toBe(true);
119
398
  });
120
- const input = wrapper.findComponent(LabeledInput);
121
399
 
122
- expect(input.attributes('data-testid')).toBe('my-input');
400
+ it('should not show skip checkbox for provisioning context', () => {
401
+ const wrapper = mountPrivateRegistry({
402
+ value: 'registry.example.com',
403
+ context: PRIVATE_REGISTRY_CONTEXT.PROVISIONING
404
+ });
405
+ const skipCheckbox = wrapper.find('[data-testid="registry-skip-pull-secrets-checkbox"]');
406
+
407
+ expect(skipCheckbox.exists()).toBe(false);
408
+ });
409
+
410
+ it('should hide SelectOrCreateAuthSecret when skip is enabled', async() => {
411
+ const wrapper = mountPrivateRegistry({
412
+ value: 'registry.example.com',
413
+ context: PRIVATE_REGISTRY_CONTEXT.CHARTS
414
+ });
415
+ const skipCheckbox = wrapper.findComponent('[data-testid="registry-skip-pull-secrets-checkbox"]');
416
+
417
+ await skipCheckbox.vm.$emit('update:value', true);
418
+ await wrapper.vm.$nextTick();
419
+
420
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(false);
421
+ });
422
+
423
+ it('should emit update:skipPullSecrets when skip checkbox changes', async() => {
424
+ const wrapper = mountPrivateRegistry({
425
+ value: 'registry.example.com',
426
+ context: PRIVATE_REGISTRY_CONTEXT.CHARTS
427
+ });
428
+ const skipCheckbox = wrapper.findComponent('[data-testid="registry-skip-pull-secrets-checkbox"]');
429
+
430
+ await skipCheckbox.vm.$emit('update:value', true);
431
+ await wrapper.vm.$nextTick();
432
+
433
+ expect(wrapper.emitted('update:skipPullSecrets')).toHaveLength(1);
434
+ expect(wrapper.emitted('update:skipPullSecrets')![0]).toStrictEqual([true]);
435
+ });
436
+
437
+ it('should emit update:pullSecret with undefined when skip is enabled', async() => {
438
+ const wrapper = mountPrivateRegistry({
439
+ value: 'registry.example.com',
440
+ context: PRIVATE_REGISTRY_CONTEXT.CHARTS
441
+ });
442
+ const skipCheckbox = wrapper.findComponent('[data-testid="registry-skip-pull-secrets-checkbox"]');
443
+
444
+ await skipCheckbox.vm.$emit('update:value', true);
445
+ await wrapper.vm.$nextTick();
446
+
447
+ expect(wrapper.emitted('update:pullSecret')).toHaveLength(1);
448
+ expect(wrapper.emitted('update:pullSecret')![0]).toStrictEqual([undefined]);
449
+ });
450
+
451
+ it('should sync localSkipPullSecrets when skipPullSecrets prop changes', async() => {
452
+ const wrapper = mountPrivateRegistry({
453
+ value: 'registry.example.com',
454
+ context: PRIVATE_REGISTRY_CONTEXT.CHARTS,
455
+ skipPullSecrets: false
456
+ });
457
+
458
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(true);
459
+
460
+ await wrapper.setProps({ skipPullSecrets: true });
461
+
462
+ expect(wrapper.findComponent(SelectOrCreateAuthSecret).exists()).toBe(false);
463
+ });
464
+
465
+ it('should hide banners when skip is enabled', async() => {
466
+ const wrapper = mountPrivateRegistry({
467
+ value: 'registry.example.com',
468
+ context: PRIVATE_REGISTRY_CONTEXT.CHARTS,
469
+ repoDefaultPullSecrets: ['secret-1', 'secret-2']
470
+ });
471
+
472
+ await wrapper.vm.$nextTick();
473
+
474
+ const bannersBefore = wrapper.findAllComponents(Banner).filter((b) => b.props('color') === 'info');
475
+
476
+ expect(bannersBefore.length).toBeGreaterThanOrEqual(2);
477
+
478
+ const skipCheckbox = wrapper.findComponent('[data-testid="registry-skip-pull-secrets-checkbox"]');
479
+
480
+ await skipCheckbox.vm.$emit('update:value', true);
481
+ await wrapper.vm.$nextTick();
482
+
483
+ const bannersAfter = wrapper.findAllComponents(Banner).filter((b) => b.props('color') === 'info');
484
+
485
+ // Only the description banner should remain
486
+ expect(bannersAfter).toHaveLength(1);
487
+ });
123
488
  });
124
489
 
125
- it('should not set data-testid when not provided', () => {
126
- const wrapper = mountPrivateRegistry({ value: 'registry.example.com' });
127
- const checkbox = wrapper.findComponent(Checkbox);
128
- const input = wrapper.findComponent(LabeledInput);
490
+ describe('pullSecret watcher', () => {
491
+ it('should emit update:value with globalRegistry when pullSecret is set and no value exists', async() => {
492
+ const wrapper = mountPrivateRegistry(
493
+ { showPullSecrets: true },
494
+ { registry: 'global.registry.io' }
495
+ );
129
496
 
130
- expect(checkbox.attributes('data-testid')).toBeUndefined();
131
- expect(input.attributes('data-testid')).toBeUndefined();
497
+ await wrapper.setProps({ pullSecret: 'my-secret' });
498
+
499
+ const emitted = wrapper.emitted('update:value');
500
+
501
+ expect(emitted).toBeTruthy();
502
+ expect(emitted![emitted!.length - 1]).toStrictEqual(['global.registry.io']);
503
+ });
504
+
505
+ it('should emit update:value with undefined when pullSecret is cleared and value equals globalRegistry', async() => {
506
+ const wrapper = mountPrivateRegistry(
507
+ {
508
+ value: 'global.registry.io',
509
+ pullSecret: 'my-secret',
510
+ showPullSecrets: true
511
+ },
512
+ { registry: 'global.registry.io' }
513
+ );
514
+
515
+ await wrapper.setProps({ pullSecret: undefined });
516
+
517
+ const emitted = wrapper.emitted('update:value');
518
+
519
+ expect(emitted).toBeTruthy();
520
+ expect(emitted![emitted!.length - 1]).toStrictEqual([undefined]);
521
+ });
132
522
  });
133
523
  });