@rancher/shell 3.0.8 → 3.0.9-rc.2

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 (192) hide show
  1. package/apis/intf/modal.ts +38 -0
  2. package/apis/intf/slide-in.ts +3 -1
  3. package/apis/shell/__tests__/slide-in.test.ts +36 -0
  4. package/apis/shell/slide-in.ts +5 -1
  5. package/assets/styles/base/_color.scss +1 -0
  6. package/assets/styles/base/_typography.scss +14 -5
  7. package/assets/styles/themes/_light.scss +1 -1
  8. package/assets/styles/themes/_modern.scss +1 -1
  9. package/assets/translations/en-us.yaml +94 -33
  10. package/assets/translations/zh-hans.yaml +0 -2
  11. package/components/ActionMenuShell.vue +4 -4
  12. package/components/CodeMirror.vue +4 -3
  13. package/components/DetailText.vue +54 -7
  14. package/components/Drawer/Chrome.vue +11 -4
  15. package/components/Drawer/DrawerCard.vue +19 -0
  16. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +3 -11
  17. package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +2 -2
  18. package/components/Drawer/ResourceDetailDrawer/index.vue +3 -20
  19. package/components/Drawer/types.ts +1 -0
  20. package/components/DynamicContent/DynamicContentCloseButton.vue +2 -2
  21. package/components/LocaleSelector.vue +1 -1
  22. package/components/Markdown.vue +1 -1
  23. package/components/PopoverCard.vue +3 -3
  24. package/components/Resource/Detail/Card/ExtrasCard.vue +39 -0
  25. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +142 -0
  26. package/components/Resource/Detail/Card/StateCard/composables.ts +41 -11
  27. package/components/Resource/Detail/Card/StateCard/index.vue +3 -9
  28. package/components/Resource/Detail/Card/StateCard/types.ts +6 -0
  29. package/components/Resource/Detail/Card/{PodsCard → StatusCard}/index.vue +11 -10
  30. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +24 -25
  31. package/components/Resource/Detail/Cards.vue +27 -0
  32. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +70 -0
  33. package/components/Resource/Detail/Masthead/index.vue +5 -0
  34. package/components/Resource/Detail/Metadata/KeyValueRow.vue +4 -2
  35. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -2
  36. package/components/Resource/Detail/ResourceRow.types.ts +14 -0
  37. package/components/Resource/Detail/ResourceRow.vue +23 -35
  38. package/components/Resource/Detail/StatusRow.vue +5 -2
  39. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +38 -7
  40. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +106 -2
  41. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  42. package/components/Resource/Detail/TitleBar/index.vue +41 -6
  43. package/components/ResourceDetail/Masthead/__tests__/index.test.ts +49 -1
  44. package/components/ResourceDetail/Masthead/__tests__/latest.test.ts +85 -0
  45. package/components/ResourceDetail/Masthead/index.vue +1 -0
  46. package/components/ResourceDetail/Masthead/latest.vue +8 -1
  47. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  48. package/components/Setting.vue +1 -1
  49. package/components/SortableTable/index.vue +25 -0
  50. package/components/SortableTable/selection.js +25 -12
  51. package/components/SortableTable/sorting.js +1 -1
  52. package/components/Tabbed/Tab.vue +1 -0
  53. package/components/Tabbed/index.vue +29 -6
  54. package/components/Window/ContainerShell.vue +10 -13
  55. package/components/fleet/FleetClusterTargets/TargetsList.vue +47 -29
  56. package/components/fleet/FleetClusterTargets/index.vue +82 -29
  57. package/components/fleet/FleetClusters.vue +26 -12
  58. package/components/fleet/FleetGitRepoPaths.vue +2 -2
  59. package/components/fleet/FleetResources.vue +14 -0
  60. package/components/fleet/FleetValuesFrom.vue +2 -2
  61. package/components/fleet/__tests__/FleetClusterTargets.test.ts +531 -0
  62. package/components/fleet/__tests__/FleetClusters.test.ts +576 -0
  63. package/components/fleet/dashboard/ResourceDetails.vue +96 -123
  64. package/components/form/Conditions.vue +1 -15
  65. package/components/form/HookOption.vue +5 -0
  66. package/components/form/LabeledSelect.vue +1 -1
  67. package/components/form/LifecycleHooks.vue +2 -6
  68. package/components/form/ResourceLabeledSelect.vue +12 -1
  69. package/components/form/SeccompProfile.vue +113 -0
  70. package/components/form/Security.vue +244 -133
  71. package/components/form/__tests__/LabeledSelect.test.ts +1 -1
  72. package/components/form/__tests__/SeccompProfile.test.js +124 -0
  73. package/components/form/__tests__/Security.test.ts +125 -37
  74. package/components/formatter/Autoscaler.vue +2 -2
  75. package/components/formatter/FleetSummaryGraph.vue +4 -1
  76. package/components/nav/Group.vue +5 -0
  77. package/components/nav/Header.vue +3 -3
  78. package/components/nav/HeaderPageActionMenu.vue +1 -1
  79. package/components/nav/NamespaceFilter.vue +6 -6
  80. package/components/nav/NotificationCenter/index.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +41 -16
  82. package/components/nav/TopLevelMenu.vue +45 -25
  83. package/components/nav/WorkspaceSwitcher.vue +1 -1
  84. package/components/nav/__tests__/TopLevelMenu.helper.test.ts +277 -0
  85. package/components/nav/__tests__/TopLevelMenu.test.ts +160 -4
  86. package/components/templates/default.vue +0 -3
  87. package/components/templates/home.vue +0 -3
  88. package/components/templates/plain.vue +0 -3
  89. package/composables/useClickOutside.ts +1 -1
  90. package/config/product/explorer.js +1 -2
  91. package/config/types.js +41 -8
  92. package/detail/__tests__/workload.test.ts +8 -16
  93. package/detail/catalog.cattle.io.app.vue +6 -0
  94. package/detail/fleet.cattle.io.cluster.vue +6 -0
  95. package/detail/workload/index.vue +7 -109
  96. package/edit/__tests__/projectsecret.test.ts +42 -0
  97. package/edit/auth/__tests__/oidc.test.ts +50 -0
  98. package/edit/auth/oidc.vue +68 -44
  99. package/edit/autoscaling.horizontalpodautoscaler/index.vue +140 -59
  100. package/edit/autoscaling.horizontalpodautoscaler/metrics-row.vue +41 -5
  101. package/edit/projectsecret.vue +29 -0
  102. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +89 -200
  103. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +58 -17
  104. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -0
  105. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +3 -63
  106. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +82 -14
  107. package/edit/workload/__tests__/index.test.ts +122 -85
  108. package/edit/workload/index.vue +48 -29
  109. package/edit/workload/mixins/workload.js +85 -32
  110. package/list/catalog.cattle.io.clusterrepo.vue +1 -1
  111. package/list/projectsecret.vue +2 -2
  112. package/machine-config/__tests__/vmwarevsphere.test.ts +64 -0
  113. package/machine-config/amazonec2.vue +2 -2
  114. package/machine-config/vmwarevsphere.vue +58 -4
  115. package/mixins/__tests__/brand.spec.ts +18 -13
  116. package/mixins/__tests__/chart.test.ts +63 -0
  117. package/mixins/chart.js +56 -51
  118. package/models/__tests__/catalog.cattle.io.app.test.ts +33 -0
  119. package/models/__tests__/workload.test.ts +333 -0
  120. package/models/catalog.cattle.io.app.js +8 -0
  121. package/models/pod.js +14 -0
  122. package/models/secret.js +1 -1
  123. package/models/workload.js +93 -27
  124. package/package.json +4 -4
  125. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +91 -0
  126. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  127. package/pages/c/_cluster/explorer/EventsTable.vue +2 -2
  128. package/pages/c/_cluster/fleet/index.vue +18 -12
  129. package/pages/c/_cluster/manager/hostedprovider/index.vue +1 -19
  130. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  131. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  132. package/plugins/dashboard-store/__tests__/resource-class.test.ts +234 -0
  133. package/plugins/dashboard-store/actions.js +9 -8
  134. package/plugins/dashboard-store/resource-class.js +97 -1
  135. package/plugins/steve/__tests__/revision.test.ts +84 -0
  136. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +30 -0
  137. package/plugins/steve/__tests__/subscribe.spec.ts +134 -0
  138. package/plugins/steve/mutations.js +9 -0
  139. package/plugins/steve/revision.ts +26 -0
  140. package/plugins/steve/steve-pagination-utils.ts +6 -5
  141. package/plugins/steve/subscribe.js +211 -51
  142. package/plugins/subscribe-events.ts +2 -2
  143. package/rancher-components/Form/Checkbox/Checkbox.vue +13 -0
  144. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -1
  145. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +1 -1
  146. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +3 -1
  147. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -1
  148. package/rancher-components/Pill/RcTag/RcTag.vue +1 -1
  149. package/rancher-components/Pill/index.ts +4 -0
  150. package/rancher-components/RcButton/RcButton.test.ts +53 -9
  151. package/rancher-components/RcButton/RcButton.vue +217 -25
  152. package/rancher-components/RcButton/types.ts +27 -1
  153. package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -4
  154. package/rancher-components/RcDropdown/types.ts +3 -3
  155. package/rancher-components/RcIcon/RcIcon.test.ts +42 -0
  156. package/rancher-components/RcIcon/RcIcon.vue +9 -6
  157. package/rancher-components/RcIcon/types.ts +13 -9
  158. package/rancher-components/utils/status.test.ts +10 -15
  159. package/rancher-components/utils/status.ts +5 -6
  160. package/store/aws.js +18 -12
  161. package/store/index.js +4 -8
  162. package/store/type-map.utils.ts +1 -1
  163. package/types/kube/kube-api.ts +29 -3
  164. package/types/rancher/steve.api.ts +40 -0
  165. package/types/shell/index.d.ts +99 -0
  166. package/types/store/dashboard-store.types.ts +29 -7
  167. package/types/store/pagination.types.ts +1 -0
  168. package/types/store/subscribe-events.types.ts +1 -0
  169. package/utils/__tests__/azure.test.ts +56 -0
  170. package/utils/__tests__/back-off.test.ts +364 -245
  171. package/utils/__tests__/error.test.ts +44 -0
  172. package/utils/__tests__/fleet.test.ts +8 -1
  173. package/utils/__tests__/pagination-wrapper.test.ts +167 -0
  174. package/utils/__tests__/version.test.ts +55 -1
  175. package/utils/azure.js +12 -0
  176. package/utils/back-off.ts +302 -69
  177. package/utils/cspAdaptor.ts +32 -14
  178. package/utils/dynamic-content/__tests__/index.test.ts +1 -1
  179. package/utils/dynamic-content/__tests__/new-release.test.ts +48 -7
  180. package/utils/dynamic-content/__tests__/support-notice.test.ts +1 -4
  181. package/utils/dynamic-content/index.ts +1 -6
  182. package/utils/dynamic-content/new-release.ts +5 -3
  183. package/utils/dynamic-content/types.d.ts +0 -1
  184. package/utils/error.js +9 -0
  185. package/utils/fleet.ts +2 -2
  186. package/utils/inactivity.ts +2 -3
  187. package/utils/pagination-wrapper.ts +101 -17
  188. package/utils/validators/formRules/index.ts +3 -0
  189. package/utils/version.js +38 -0
  190. package/components/auth/AzureWarning.vue +0 -77
  191. /package/components/Resource/Detail/{Card/PodsCard/Bubble.vue → Bubble.vue} +0 -0
  192. /package/components/Resource/Detail/Card/{PodsCard → StatusCard}/composable.ts +0 -0
@@ -25,11 +25,37 @@ export interface KubeLabelSelector {
25
25
  * https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectmeta-v1-meta
26
26
  */
27
27
  export interface KubeMetadata {
28
+ namespace?: string,
29
+ name: string
30
+
28
31
  labels?: { [key: string]: string },
29
32
  annotations?: { [key: string]: string },
30
33
  }
31
34
 
32
- export interface RancherKubeMetadata extends KubeMetadata {
33
- namespace?: string,
34
- name: string
35
+ /**
36
+ * Kube API JSON response for LIST requests
37
+ */
38
+ export interface KubeListResponse<T = any> {
39
+ kind: string,
40
+ apiVersion: string,
41
+
42
+ items: T[],
43
+
44
+ // Bucket for everything else (hopefully to remove once above populated)
45
+ [key: string]: any
46
+ }
47
+
48
+ /**
49
+ * Kube API JSON response for GET requests
50
+ */
51
+ export interface KubeGetResponse {
52
+ kind: string,
53
+ apiVersion: string,
54
+
55
+ metadata: KubeMetadata,
56
+ spec: any,
57
+ status: any,
58
+
59
+ // Bucket for everything else (hopefully to remove once above populated)
60
+ [key: string]: any
35
61
  }
@@ -0,0 +1,40 @@
1
+ import { KubeGetResponse, KubeMetadata } from '@shell/types/kube/kube-api';
2
+
3
+ /**
4
+ * Collection of string based values used by the Steve API response's `code` field
5
+ */
6
+ export const STEVE_RESPONSE_CODE = {
7
+ /**
8
+ * When the query param `revision` is sent and it's not in the vai cache then return this error
9
+ */
10
+ UNKNOWN_REVISION: 'unknown revision'
11
+ };
12
+
13
+ /**
14
+ * Steve API JSON response for LIST requests
15
+ */
16
+ export interface SteveListResponse<T = any> {
17
+ actions: any,
18
+ count: number,
19
+ data: T[],
20
+ links: any,
21
+ resourceType: string,
22
+ revision: string,
23
+ type: string,
24
+
25
+ // Bucket for everything else (hopefully to remove once above populated)
26
+ [key: string]: any
27
+ }
28
+
29
+ /**
30
+ * Steve API JSON response for GET requests
31
+ */
32
+ export interface SteveGetResponse extends KubeGetResponse {
33
+ // Rancher specific properties (there are more)
34
+ id: string,
35
+
36
+ // Bucket for everything else (hopefully to remove once above populated)
37
+ [key: string]: any
38
+ }
39
+
40
+ export type RancherKubeMetadata = KubeMetadata
@@ -2097,9 +2097,34 @@ export namespace WORKLOAD_TYPES {
2097
2097
  let REPLICA_SET: string;
2098
2098
  let REPLICATION_CONTROLLER: string;
2099
2099
  }
2100
+ export namespace WORKLOAD_KINDS {
2101
+ let DEPLOYMENT_1: string;
2102
+ export { DEPLOYMENT_1 as DEPLOYMENT };
2103
+ let CRON_JOB_1: string;
2104
+ export { CRON_JOB_1 as CRON_JOB };
2105
+ let DAEMON_SET_1: string;
2106
+ export { DAEMON_SET_1 as DAEMON_SET };
2107
+ let JOB_1: string;
2108
+ export { JOB_1 as JOB };
2109
+ let STATEFUL_SET_1: string;
2110
+ export { STATEFUL_SET_1 as STATEFUL_SET };
2111
+ let REPLICA_SET_1: string;
2112
+ export { REPLICA_SET_1 as REPLICA_SET };
2113
+ let REPLICATION_CONTROLLER_1: string;
2114
+ export { REPLICATION_CONTROLLER_1 as REPLICATION_CONTROLLER };
2115
+ }
2116
+ /**
2117
+ * Map Rancher Workload types to Kube Workload Kinds
2118
+ */
2100
2119
  export const WORKLOAD_TYPE_TO_KIND_MAPPING: {
2101
2120
  [x: string]: string;
2102
2121
  };
2122
+ /**
2123
+ * Map Kube Workload Kinds types to Rancher Workload
2124
+ */
2125
+ export const WORKLOAD_KIND_TO_TYPE_MAPPING: {
2126
+ [x: string]: string;
2127
+ };
2103
2128
  export const METRICS_SUPPORTED_KINDS: string[];
2104
2129
  export namespace SCALABLE_WORKLOAD_TYPES { }
2105
2130
  export namespace LIST_WORKLOAD_TYPES {
@@ -3434,6 +3459,46 @@ export default class Resource {
3434
3459
  id: any;
3435
3460
  };
3436
3461
  };
3462
+ /**
3463
+ * Override this getter to provide additional action buttons or a custom component
3464
+ * for the detail page title bar.
3465
+ *
3466
+ * @returns {undefined|object|Array} A Vue component definition, an array of RcButton props, or undefined
3467
+ *
3468
+ * @example
3469
+ * // Using an array of button props with the new variant/size props
3470
+ * get detailPageAdditionalActions() {
3471
+ * return [
3472
+ * { label: 'Action 1', variant: 'secondary', onClick: () => this.doAction1() },
3473
+ * { label: 'Action 2', variant: 'primary', size: 'large', onClick: () => this.doAction2() }
3474
+ * ];
3475
+ * }
3476
+ *
3477
+ * @example
3478
+ * // Using defineComponent with h() render function for custom rendering
3479
+ * import { defineComponent, h } from 'vue';
3480
+ * import RcButton from '@components/RcButton/RcButton.vue';
3481
+ *
3482
+ * get detailPageAdditionalActions() {
3483
+ * return defineComponent({
3484
+ * render() {
3485
+ * return h(RcButton, {
3486
+ * variant: 'primary',
3487
+ * onClick: () => console.log('clicked')
3488
+ * }, () => 'Click Me');
3489
+ * }
3490
+ * });
3491
+ * }
3492
+ *
3493
+ * @example
3494
+ * // Using dynamic import for a custom component
3495
+ * import { defineAsyncComponent } from 'vue';
3496
+ *
3497
+ * get detailPageAdditionalActions() {
3498
+ * return defineAsyncComponent(() => import('@shell/components/MyCustomActions.vue'));
3499
+ * }
3500
+ */
3501
+ get detailPageAdditionalActions(): any;
3437
3502
  goToDetail(): void;
3438
3503
  /**
3439
3504
  * Resource action redirects to the detail page with a query parameter 'clone'
@@ -3647,6 +3712,33 @@ export default class Resource {
3647
3712
  * Allows model to specify JSON Paths that should be folded in the YAML editor by default
3648
3713
  */
3649
3714
  get yamlFolding(): any[];
3715
+ get resourceConditions(): any;
3716
+ get resourceEvents(): any;
3717
+ get insightCardProps(): {
3718
+ title: any;
3719
+ rows: any[];
3720
+ };
3721
+ get insightCard(): {
3722
+ component: any;
3723
+ props: {
3724
+ title: any;
3725
+ rows: any[];
3726
+ };
3727
+ };
3728
+ get _cards(): {
3729
+ component: any;
3730
+ props: {
3731
+ title: any;
3732
+ rows: any[];
3733
+ };
3734
+ }[];
3735
+ get cards(): {
3736
+ component: any;
3737
+ props: {
3738
+ title: any;
3739
+ rows: any[];
3740
+ };
3741
+ }[];
3650
3742
  }
3651
3743
  }
3652
3744
 
@@ -4461,6 +4553,7 @@ export class ApiError extends Error {
4461
4553
  };
4462
4554
  }
4463
4555
  export function normalizeError(err: any): any;
4556
+ export function formatAWSError(err: any): any;
4464
4557
  }
4465
4558
 
4466
4559
  // @shell/utils/favicon
@@ -5318,6 +5411,12 @@ export function seenReleaseNotes(store: any): boolean;
5318
5411
  export function markSeenReleaseNotes(store: any): Promise<void>;
5319
5412
  export function readReleaseNotes(store: any): boolean;
5320
5413
  export function markReadReleaseNotes(store: any): Promise<void>;
5414
+ /**
5415
+ * Get the release notes URL for a given version
5416
+ *
5417
+ * @param {Get} version s
5418
+ */
5419
+ export function getReleaseNotesURL(isPrime: any, version: Get): string;
5321
5420
  }
5322
5421
 
5323
5422
  // @shell/utils/width
@@ -48,11 +48,6 @@ export interface ActionFindAllArgs extends ActionCoreFindArgs {
48
48
  depaginate?: boolean,
49
49
  }
50
50
 
51
- export interface ActionFindPageTransientResult<T> {
52
- pagination: StorePagination,
53
- data: T[],
54
- }
55
-
56
51
  /**
57
52
  * Args used for findPage action
58
53
  */
@@ -81,19 +76,46 @@ export interface ActionFindPageArgs extends ActionCoreFindArgs {
81
76
  * If true don't persist the http response to the store, just pass it back
82
77
  */
83
78
  transient?: boolean,
79
+ /**
80
+ * The target minimum revision for the resource.
81
+ *
82
+ * If this is higher than the latest revision known to rancher then an error will be returned
83
+ */
84
+ revision?: string
84
85
  }
85
86
 
86
- export type ActionFindPageResponse<T = any> = {
87
+ /**
88
+ * Response to a transient (not stored in cache) findPage action
89
+ */
90
+ export type ActionFindPageTransientResponse<T = any> = {
87
91
  data: T[],
88
92
  pagination?: StorePagination
89
- } | T[];
93
+ };
94
+
95
+ /**
96
+ * Response to the findPage action
97
+ *
98
+ * If the request was transient (not stored in cache) this will be an object contain all the details of the request
99
+ *
100
+ * If the request was not transient this will just be the array of resources
101
+ */
102
+ export type ActionFindPageResponse<T = any> = ActionFindPageTransientResponse | T[];
90
103
 
104
+ /**
105
+ * Args used for findPage action
106
+ */
91
107
  export interface ActionFindMatchingArgs extends ActionCoreFindArgs {
92
108
  labelSelector: KubeLabelSelector,
93
109
  namespaced?: string,
94
110
  depaginate?: boolean
95
111
  }
96
112
 
113
+ /**
114
+ * Response to the findMatching action
115
+ */
97
116
  export type ActionFindMatchingResponse<T = any> = ActionFindPageResponse<T>
98
117
 
118
+ /**
119
+ * Args used for findLabelSelector action
120
+ */
99
121
  export type ActionFindLabelSelectorArgs = ActionFindPageArgs | ActionFindMatchingArgs;
@@ -599,6 +599,7 @@ export interface StorePaginationResult {
599
599
  * The last time the resource was updated. Used to assist list watching for changes
600
600
  */
601
601
  timestamp: number,
602
+ revision: string,
602
603
  }
603
604
 
604
605
  export interface StorePaginationRequest {
@@ -17,6 +17,7 @@ export interface STEVE_WATCH_EVENT_PARAMS_COMMON {
17
17
  */
18
18
  export type STEVE_WATCH_EVENT_LISTENER_CALLBACK_PARAMS = {
19
19
  forceWatch?: boolean,
20
+ revision?: string,
20
21
  }
21
22
 
22
23
  /**
@@ -0,0 +1,56 @@
1
+ import { parseAzureError } from '@shell/utils/azure';
2
+
3
+ describe('azure.js', () => {
4
+ describe('parseAzureError', () => {
5
+ it('should extract error from Message="..." format', () => {
6
+ const err = 'Something went wrong Message="Failure"';
7
+
8
+ expect(parseAzureError(err)).toBe('Failure');
9
+ });
10
+
11
+ it('should extract error_description from single-line Response body JSON', () => {
12
+ const err = 'Error X Response body: {"error_description": "Failure"}';
13
+
14
+ expect(parseAzureError(err)).toBe('Failure');
15
+ });
16
+
17
+ it('should extract error.message from multiline JSON containing "error":', () => {
18
+ const err = `
19
+ { "error": { "code": "NoRegisteredProviderFound", "message": "No registered resource provider found for location 'X'" } }
20
+ `;
21
+
22
+ expect(parseAzureError(err)).toBe("No registered resource provider found for location 'X'");
23
+ });
24
+
25
+ it('should return undefined if single-line JSON exists but lacks error_description', () => {
26
+ // Note: Current implementation returns undefined immediately if JSON parses but key is missing
27
+ const err = 'Response body: {"other_field": "value"}';
28
+
29
+ expect(parseAzureError(err)).toBeUndefined();
30
+ });
31
+
32
+ it('should return false if multiline JSON exists but lacks error.message', () => {
33
+ const err = `
34
+ {
35
+ "error": {
36
+ "code": "NoMessage"
37
+ }
38
+ }
39
+ `;
40
+
41
+ expect(parseAzureError(err)).toBe(false);
42
+ });
43
+
44
+ it('should return false if JSON parsing fails', () => {
45
+ const err1 = 'Response body: { invalid json }';
46
+ const err2 = '{ "error": { invalid } }';
47
+
48
+ expect(parseAzureError(err1)).toBe(false);
49
+ expect(parseAzureError(err2)).toBe(false);
50
+ });
51
+
52
+ it('should return false for unmatched strings', () => {
53
+ expect(parseAzureError('error string')).toBe(false);
54
+ });
55
+ });
56
+ });