@spinnaker/google 2026.3.2 → 2026.4.1

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 (108) hide show
  1. package/dist/autoscalingPolicy/GceAutoScalingFieldLayout.d.ts +2 -2
  2. package/dist/domain/loadBalancer.d.ts +6 -3
  3. package/dist/image/ImageSelect.d.ts +1 -1
  4. package/dist/index.js +159 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/loadBalancer/configure/common/gceLoadBalancerModels.d.ts +13 -2
  7. package/dist/loadBalancer/configure/http/GceHttpLoadBalancerEditor.d.ts +3 -0
  8. package/dist/loadBalancer/configure/http/GceHttpLoadBalancerModal.d.ts +1 -1
  9. package/dist/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerEditor.d.ts +30 -0
  10. package/dist/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerModal.d.ts +53 -0
  11. package/dist/loadBalancer/httpLoadBalancerUtils.service.d.ts +3 -1
  12. package/dist/serverGroup/configure/wizard/GceCloneServerGroupModal.d.ts +3 -0
  13. package/dist/serverGroup/configure/wizard/GceServerGroupWizard.types.d.ts +1 -0
  14. package/dist/serverGroup/configure/wizard/customInstance/CustomInstanceConfigurer.d.ts +1 -1
  15. package/dist/serverGroup/configure/wizard/customInstance/GceCustomInstanceBuilder.d.ts +2 -2
  16. package/dist/serverGroup/configure/wizard/pages/AdvancedSettings.d.ts +3 -0
  17. package/dist/serverGroup/configure/wizard/pages/GceServerGroupLoadBalancers.d.ts +1 -0
  18. package/dist/serverGroup/configure/wizard/pages/Policies.d.ts +21 -3
  19. package/dist/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.d.ts +1 -0
  20. package/dist/serverGroup/configure/wizard/pages/ServerGroupCapacity.d.ts +1 -0
  21. package/dist/serverGroup/configure/wizard/pages/ServerGroupImageSettings.d.ts +1 -0
  22. package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +1 -0
  23. package/dist/serverGroup/configure/wizard/zones/GceInstanceFlexibilityConfigurer.d.ts +2 -2
  24. package/dist/serverGroup/details/GceInstanceFlexibilityPolicyDetails.d.ts +2 -2
  25. package/dist/serverGroup/details/stateful/MarkDiskStatefulButton.d.ts +2 -2
  26. package/dist/serverGroup/details/stateful/UpdateBootImageButton.d.ts +2 -2
  27. package/package.json +5 -5
  28. package/src/autoHealingPolicy/GceAutoHealingPolicyEditor.spec.tsx +68 -52
  29. package/src/autoscalingPolicy/GceAutoscalingPolicyEditor.spec.tsx +43 -50
  30. package/src/autoscalingPolicy/GceAutoscalingPolicyEditor.tsx +1 -1
  31. package/src/autoscalingPolicy/GceAutoscalingPolicyWriter.spec.ts +1 -1
  32. package/src/domain/loadBalancer.ts +6 -3
  33. package/src/gce.module.spec.ts +132 -153
  34. package/src/instance/details/GceInstanceDetails.spec.tsx +86 -83
  35. package/src/loadBalancer/configure/choice/GceLoadBalancerChoiceModal.spec.tsx +162 -121
  36. package/src/loadBalancer/configure/choice/GceLoadBalancerChoiceModal.tsx +13 -1
  37. package/src/loadBalancer/configure/common/GceProxyLoadBalancerEditor.spec.tsx +47 -30
  38. package/src/loadBalancer/configure/common/GceProxyLoadBalancerEditor.tsx +2 -1
  39. package/src/loadBalancer/configure/common/GceProxyLoadBalancerModal.spec.tsx +85 -57
  40. package/src/loadBalancer/configure/common/gceLoadBalancerData.spec.ts +17 -16
  41. package/src/loadBalancer/configure/common/gceLoadBalancerModels.spec.ts +230 -5
  42. package/src/loadBalancer/configure/common/gceLoadBalancerModels.ts +186 -22
  43. package/src/loadBalancer/configure/common/gceLoadBalancerSubmission.spec.ts +298 -2
  44. package/src/loadBalancer/configure/common/gceLoadBalancerSubmission.ts +35 -5
  45. package/src/loadBalancer/configure/common/useGceLoadBalancerData.spec.tsx +37 -46
  46. package/src/loadBalancer/configure/http/GceHttpLoadBalancerEditor.spec.tsx +399 -58
  47. package/src/loadBalancer/configure/http/GceHttpLoadBalancerEditor.tsx +148 -41
  48. package/src/loadBalancer/configure/http/GceHttpLoadBalancerListenerEditor.spec.tsx +169 -36
  49. package/src/loadBalancer/configure/http/GceHttpLoadBalancerListenerEditor.tsx +80 -12
  50. package/src/loadBalancer/configure/http/GceHttpLoadBalancerModal.spec.tsx +224 -124
  51. package/src/loadBalancer/configure/http/GceHttpLoadBalancerModal.tsx +8 -2
  52. package/src/loadBalancer/configure/http/GceHttpLoadBalancerResourceEditors.spec.tsx +57 -22
  53. package/src/loadBalancer/configure/http/GceHttpLoadBalancerResourceEditors.tsx +21 -0
  54. package/src/loadBalancer/configure/http/GceHttpLoadBalancerRoutingEditor.spec.tsx +24 -20
  55. package/src/loadBalancer/configure/network/GceNetworkLoadBalancerEditor.spec.tsx +41 -32
  56. package/src/loadBalancer/configure/network/GceNetworkLoadBalancerModal.spec.tsx +35 -30
  57. package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerEditor.spec.tsx +443 -0
  58. package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerEditor.tsx +401 -0
  59. package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerModal.spec.tsx +330 -0
  60. package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerModal.tsx +348 -0
  61. package/src/loadBalancer/details/gceLoadBalancerDetails.spec.tsx +439 -35
  62. package/src/loadBalancer/details/gceLoadBalancerDetails.tsx +104 -21
  63. package/src/loadBalancer/httpLoadBalancerUtils.service.spec.ts +126 -0
  64. package/src/loadBalancer/httpLoadBalancerUtils.service.ts +27 -11
  65. package/src/loadBalancer/loadBalancer.setTransformer.spec.ts +165 -0
  66. package/src/loadBalancer/loadBalancer.setTransformer.ts +15 -2
  67. package/src/pipeline/stages/googleStageConfig.spec.js +22 -16
  68. package/src/securityGroup/configure/GceSecurityGroupModal.spec.tsx +177 -193
  69. package/src/securityGroup/configure/GceSecurityGroupModal.tsx +14 -2
  70. package/src/securityGroup/details/GceSecurityGroupDetails.spec.tsx +69 -78
  71. package/src/serverGroup/configure/serverGroupCommandBuilder.service.spec.js +35 -15
  72. package/src/serverGroup/configure/serverGroupConfiguration.service.js +7 -1
  73. package/src/serverGroup/configure/serverGroupConfiguration.service.spec.js +74 -4
  74. package/src/serverGroup/configure/wizard/GceCloneServerGroupModal.spec.tsx +593 -855
  75. package/src/serverGroup/configure/wizard/GceCloneServerGroupModal.tsx +120 -38
  76. package/src/serverGroup/configure/wizard/GceServerGroupWizard.types.ts +1 -0
  77. package/src/serverGroup/configure/wizard/GceServerGroupWizardPage.spec.tsx +53 -50
  78. package/src/serverGroup/configure/wizard/customInstance/GceCustomInstanceBuilder.spec.tsx +65 -49
  79. package/src/serverGroup/configure/wizard/hiddenMetadataKeys.value.js +1 -0
  80. package/src/serverGroup/configure/wizard/pages/AdvancedSettings.spec.tsx +101 -108
  81. package/src/serverGroup/configure/wizard/pages/AdvancedSettings.tsx +32 -28
  82. package/src/serverGroup/configure/wizard/pages/GceServerGroupFirewalls.spec.tsx +60 -59
  83. package/src/serverGroup/configure/wizard/pages/GceServerGroupLoadBalancers.spec.tsx +240 -114
  84. package/src/serverGroup/configure/wizard/pages/GceServerGroupLoadBalancers.tsx +152 -63
  85. package/src/serverGroup/configure/wizard/pages/Policies.spec.tsx +93 -48
  86. package/src/serverGroup/configure/wizard/pages/Policies.tsx +80 -55
  87. package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.spec.tsx +123 -94
  88. package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +24 -18
  89. package/src/serverGroup/configure/wizard/pages/ServerGroupCapacity.spec.tsx +152 -133
  90. package/src/serverGroup/configure/wizard/pages/ServerGroupCapacity.tsx +40 -37
  91. package/src/serverGroup/configure/wizard/pages/ServerGroupImageSettings.spec.tsx +66 -60
  92. package/src/serverGroup/configure/wizard/pages/ServerGroupImageSettings.tsx +8 -2
  93. package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.spec.tsx +131 -101
  94. package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +29 -28
  95. package/src/serverGroup/configure/wizard/zones/GceInstanceFlexibilityConfigurer.spec.tsx +104 -81
  96. package/src/serverGroup/details/GceInstanceFlexibilityPolicyDetails.spec.tsx +10 -11
  97. package/src/serverGroup/details/autoHealingPolicy/GceAutoHealingPolicyDetails.spec.tsx +51 -59
  98. package/src/serverGroup/details/autoHealingPolicy/modal/GceUpsertAutoHealingPolicyModal.spec.tsx +3 -3
  99. package/src/serverGroup/details/autoscalingPolicy/GceAutoscalingPolicyDetails.spec.tsx +49 -58
  100. package/src/serverGroup/details/autoscalingPolicy/modal/GceUpsertAutoscalingPolicyModal.spec.tsx +3 -3
  101. package/src/serverGroup/details/gceServerGroupDetails.spec.tsx +162 -141
  102. package/src/serverGroup/details/resize/GceResizeServerGroupModal.spec.tsx +102 -86
  103. package/src/serverGroup/details/resize/GceResizeServerGroupModal.tsx +4 -0
  104. package/src/serverGroup/details/rollback/GceRollbackServerGroupModal.spec.tsx +40 -31
  105. package/src/serverGroup/serverGroup.transformer.js +1 -0
  106. package/src/serverGroup/serverGroup.transformer.spec.js +48 -0
  107. package/src/validation/ApplicationNameValidator.spec.ts +1 -1
  108. /package/dist/{gce.logo2ae85d8704b1d6b8.png → gce.logoChI3w_A5.png} +0 -0
@@ -1,19 +1,16 @@
1
- import { mount as enzymeMount, shallow } from 'enzyme';
1
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
2
2
  import React from 'react';
3
3
 
4
- import { CloudProviderRegistry, CollapsibleSection, DeckRuntimeContext, ManagedMenuItem } from '@spinnaker/core';
4
+ import { CloudProviderRegistry, ConfirmationModalService, DeckRuntimeContext } from '@spinnaker/core';
5
5
 
6
6
  import { GceAutoscalingPolicyWriter } from '../../autoscalingPolicy';
7
- import { registerGoogleProvider } from '../../gce.module';
8
7
  import { GceCloneServerGroupModal } from '../configure/wizard/GceCloneServerGroupModal';
9
- import { GceAutoHealingPolicyDetails } from './autoHealingPolicy';
10
- import { GceAutoscalingPolicyDetails } from './autoscalingPolicy';
8
+ import { registerGoogleProvider } from '../../gce.module';
11
9
  import {
12
10
  GceServerGroupActions,
13
- GceServerGroupLaunchConfigSection,
14
11
  gceServerGroupDetailsSections,
12
+ GceServerGroupLaunchConfigSection,
15
13
  } from './gceServerGroupDetails';
16
- import { GceInstanceFlexibilityPolicyDetails } from './GceInstanceFlexibilityPolicyDetails';
17
14
  import { GceResizeServerGroupModal } from './resize/GceResizeServerGroupModal';
18
15
  import { GceRollbackServerGroupModal } from './rollback/GceRollbackServerGroupModal';
19
16
 
@@ -22,7 +19,7 @@ describe('GCE server group details integration', () => {
22
19
  const RuntimeWrapper = ({ children }: React.PropsWithChildren<{}>) => (
23
20
  <DeckRuntimeContext.Provider value={{ services: runtimeServices } as any}>{children}</DeckRuntimeContext.Provider>
24
21
  );
25
- const mount = (component: React.ReactElement) => enzymeMount(component, { wrappingComponent: RuntimeWrapper });
22
+ const renderWithRuntime = (component: React.ReactElement) => render(<RuntimeWrapper>{component}</RuntimeWrapper>);
26
23
 
27
24
  beforeEach(() => {
28
25
  runtimeServices = {
@@ -57,82 +54,78 @@ describe('GCE server group details integration', () => {
57
54
  { ...eligibleRollbackCandidate, app: 'other', name: 'other-main-v003' },
58
55
  { ...eligibleRollbackCandidate, isDisabled: false, name: 'fnord-main-v002' },
59
56
  ],
60
- refresh: jasmine.createSpy('refresh'),
57
+ refresh: vi.fn(),
61
58
  },
62
59
  } as any;
63
60
 
64
- const managedAction = (wrapper: any, label: string) =>
65
- wrapper.find(ManagedMenuItem).filterWhere((item: any) => item.prop('children') === label);
66
-
67
- const linkAction = (wrapper: any, label: string) =>
68
- wrapper.find('a').filterWhere((link: any) => link.text() === label);
69
-
70
- it('registers autoscaling and auto-healing sections backed by the completed details components', () => {
61
+ it('registered policy sections derive enabled mutation controls from the provider state', () => {
71
62
  registerGoogleProvider();
72
-
73
- expect(CloudProviderRegistry.getValue('gce', 'serverGroup.detailsSections')).toEqual(gceServerGroupDetailsSections);
74
-
75
- const renderedSections = gceServerGroupDetailsSections.map((Section) =>
76
- shallow(<Section app={app} serverGroup={serverGroup} />),
77
- );
78
- const autoscaling = renderedSections.find(
79
- (section) => section.find(CollapsibleSection).prop('heading') === 'Autoscaling',
80
- );
81
- const autoHealing = renderedSections.find(
82
- (section) => section.find(CollapsibleSection).prop('heading') === 'Auto-healing',
63
+ vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(false);
64
+
65
+ const sections = CloudProviderRegistry.getValue('gce', 'serverGroup.detailsSections');
66
+ expect(sections).toEqual(gceServerGroupDetailsSections);
67
+ const policySections = sections.slice(-2);
68
+ render(
69
+ <>
70
+ {policySections.map((Section: React.ComponentType<any>, index: number) => (
71
+ <Section key={index} app={app} serverGroup={serverGroup} />
72
+ ))}
73
+ </>,
83
74
  );
84
75
 
85
- expect(autoscaling.find(GceAutoscalingPolicyDetails).props()).toEqual(
86
- jasmine.objectContaining({ application: app, policy: serverGroup.autoscalingPolicy, serverGroup }),
87
- );
88
- expect(autoHealing.find(GceAutoHealingPolicyDetails).props()).toEqual(
89
- jasmine.objectContaining({ application: app, policy: serverGroup.autoHealingPolicy, serverGroup }),
90
- );
76
+ expandSection('Autoscaling');
77
+ expandSection('Auto-healing');
78
+ expect(screen.getByText('Min # VMs')).toBeInTheDocument();
79
+ expect(screen.getByText('fnord-health-check')).toBeInTheDocument();
80
+ expect(screen.getByTestId('edit-autoscaling-policy')).toBeInTheDocument();
81
+ expect(screen.getByTestId('delete-autoscaling-policy')).toBeInTheDocument();
82
+ expect(screen.getByTestId('edit-auto-healing-policy')).toBeInTheDocument();
83
+ expect(screen.getByTestId('delete-auto-healing-policy')).toBeInTheDocument();
91
84
  });
92
85
 
93
- it('keeps policy summaries read-only when GCE ad-hoc infrastructure writes are disabled', () => {
94
- spyOn(CloudProviderRegistry, 'isDisabled').and.returnValue(true);
95
- const renderedSections = gceServerGroupDetailsSections.map((Section) =>
96
- shallow(<Section app={app} serverGroup={serverGroup} />),
97
- );
98
- const autoscalingSection = renderedSections.find(
99
- (section) => section.find(CollapsibleSection).prop('heading') === 'Autoscaling',
100
- );
101
- const autoHealingSection = renderedSections.find(
102
- (section) => section.find(CollapsibleSection).prop('heading') === 'Auto-healing',
86
+ it('registered policy sections derive read-only summaries from disabled provider state', () => {
87
+ registerGoogleProvider();
88
+ vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(true);
89
+ const policySections = CloudProviderRegistry.getValue('gce', 'serverGroup.detailsSections').slice(-2);
90
+ render(
91
+ <>
92
+ {policySections.map((Section: React.ComponentType<any>, index: number) => (
93
+ <Section key={index} app={app} serverGroup={serverGroup} />
94
+ ))}
95
+ </>,
103
96
  );
104
- const autoscaling = shallow(autoscalingSection.find(GceAutoscalingPolicyDetails).getElement());
105
- const autoHealing = shallow(autoHealingSection.find(GceAutoHealingPolicyDetails).getElement());
106
-
107
- expect(autoscaling.text()).toContain('Min # VMs');
108
- expect(autoscaling.find('[data-testid="edit-autoscaling-policy"]').length).toBe(0);
109
- expect(autoscaling.find('[data-testid="delete-autoscaling-policy"]').length).toBe(0);
110
- expect(autoHealing.text()).toContain('fnord-health-check');
111
- expect(autoHealing.find('[data-testid="edit-auto-healing-policy"]').length).toBe(0);
112
- expect(autoHealing.find('[data-testid="delete-auto-healing-policy"]').length).toBe(0);
97
+ expandSection('Autoscaling');
98
+ expandSection('Auto-healing');
99
+
100
+ expect(screen.getByText('Min # VMs')).toBeInTheDocument();
101
+ expect(screen.getByText('fnord-health-check')).toBeInTheDocument();
102
+ expect(screen.queryByTestId('edit-autoscaling-policy')).not.toBeInTheDocument();
103
+ expect(screen.queryByTestId('delete-autoscaling-policy')).not.toBeInTheDocument();
104
+ expect(screen.queryByTestId('edit-auto-healing-policy')).not.toBeInTheDocument();
105
+ expect(screen.queryByTestId('delete-auto-healing-policy')).not.toBeInTheDocument();
113
106
  });
114
107
 
115
- it('does not offer policy creation when GCE ad-hoc infrastructure writes are disabled', () => {
116
- spyOn(CloudProviderRegistry, 'isDisabled').and.returnValue(true);
108
+ it('registered policy sections do not offer policy creation when provider mutations are disabled', () => {
109
+ registerGoogleProvider();
110
+ vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(true);
117
111
  const serverGroupWithoutPolicies = {
118
112
  ...serverGroup,
119
113
  autoscalingPolicy: undefined,
120
114
  autoHealingPolicy: undefined,
121
115
  };
122
- const renderedSections = gceServerGroupDetailsSections.map((Section) =>
123
- shallow(<Section app={app} serverGroup={serverGroupWithoutPolicies} />),
124
- );
125
- const autoscalingSection = renderedSections.find(
126
- (section) => section.find(CollapsibleSection).prop('heading') === 'Autoscaling',
127
- );
128
- const autoHealingSection = renderedSections.find(
129
- (section) => section.find(CollapsibleSection).prop('heading') === 'Auto-healing',
116
+ const policySections = CloudProviderRegistry.getValue('gce', 'serverGroup.detailsSections').slice(-2);
117
+ render(
118
+ <>
119
+ {policySections.map((Section: React.ComponentType<any>, index: number) => (
120
+ <Section key={index} app={app} serverGroup={serverGroupWithoutPolicies} />
121
+ ))}
122
+ </>,
130
123
  );
131
- const autoscaling = shallow(autoscalingSection.find(GceAutoscalingPolicyDetails).getElement());
132
- const autoHealing = shallow(autoHealingSection.find(GceAutoHealingPolicyDetails).getElement());
133
124
 
134
- expect(autoscaling.find('[data-testid="add-autoscaling-policy"]').length).toBe(0);
135
- expect(autoHealing.find('[data-testid="add-auto-healing-policy"]').length).toBe(0);
125
+ expandSection('Autoscaling');
126
+ expandSection('Auto-healing');
127
+ expect(screen.queryByTestId('add-autoscaling-policy')).not.toBeInTheDocument();
128
+ expect(screen.queryByTestId('add-auto-healing-policy')).not.toBeInTheDocument();
136
129
  });
137
130
 
138
131
  it('renders target shape and instance flexibility in the launch configuration without nesting definition lists', () => {
@@ -141,7 +134,7 @@ describe('GCE server group details integration', () => {
141
134
  preferred: { rank: 1, machineTypes: ['n2-standard-8'] },
142
135
  },
143
136
  };
144
- const wrapper = shallow(
137
+ const { container } = render(
145
138
  <GceServerGroupLaunchConfigSection
146
139
  app={app}
147
140
  serverGroup={{
@@ -152,16 +145,17 @@ describe('GCE server group details integration', () => {
152
145
  />,
153
146
  );
154
147
 
155
- expect(wrapper.find('dt').filterWhere((term) => term.text() === 'Target shape').length).toBe(1);
156
- expect(wrapper.find('dd').filterWhere((definition) => definition.text() === 'BALANCED').length).toBe(1);
157
- expect(wrapper.find(GceInstanceFlexibilityPolicyDetails).prop('instanceFlexibilityPolicy')).toBe(
158
- instanceFlexibilityPolicy,
159
- );
160
- expect(wrapper.find('dl dl').length).toBe(0);
148
+ expect(screen.getAllByText('Target shape', { selector: 'dt' })).toHaveLength(1);
149
+ expect(screen.getAllByText('BALANCED', { selector: 'dd' })).toHaveLength(1);
150
+ expect(definition('Target shape')).toBe('BALANCED');
151
+ expect(definition('Selection')).toBe('preferred');
152
+ expect(definition('Rank')).toBe('1');
153
+ expect(definition('Machine types')).toBe('n2-standard-8');
154
+ expect(container.querySelectorAll('dl dl')).toHaveLength(0);
161
155
  });
162
156
 
163
157
  it('renders shielded settings with stable, legacy, then top-level field precedence', () => {
164
- const wrapper = shallow(
158
+ render(
165
159
  <GceServerGroupLaunchConfigSection
166
160
  app={app}
167
161
  serverGroup={{
@@ -173,118 +167,145 @@ describe('GCE server group details integration', () => {
173
167
  }}
174
168
  />,
175
169
  );
176
- const definitions = (label: string) => {
177
- const index = wrapper
178
- .find('dt')
179
- .map((candidate) => candidate.text())
180
- .indexOf(label);
181
- return index < 0 ? undefined : wrapper.find('dd').at(index).text();
182
- };
183
170
 
184
- expect(definitions('Secure Boot')).toBe('false');
185
- expect(definitions('vTPM')).toBe('false');
186
- expect(definitions('Integrity Monitoring')).toBe('false');
171
+ expect(definition('Secure Boot')).toBe('false');
172
+ expect(definition('vTPM')).toBe('false');
173
+ expect(definition('Integrity Monitoring')).toBe('false');
187
174
  });
188
175
 
189
176
  it('adds rollback and resize without changing existing enabled action visibility', () => {
190
- const wrapper = mount(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
191
-
192
- expect(managedAction(wrapper, 'Rollback').length).toBe(1);
193
- expect(managedAction(wrapper, 'Resize').length).toBe(1);
194
- expect(linkAction(wrapper, 'Clone').length).toBe(1);
195
- expect(linkAction(wrapper, 'Disable').length).toBe(1);
196
- expect(linkAction(wrapper, 'Enable').length).toBe(0);
197
- expect(linkAction(wrapper, 'Destroy').length).toBe(1);
177
+ registerGoogleProvider();
178
+ const RegisteredActions = CloudProviderRegistry.getValue('gce', 'serverGroup.detailsActions');
179
+ renderWithRuntime(<RegisteredActions app={app} serverGroup={serverGroup} />);
180
+
181
+ expect(screen.getByText('Rollback')).toBeInTheDocument();
182
+ expect(screen.getByText('Resize')).toBeInTheDocument();
183
+ expect(screen.getByText('Clone')).toBeInTheDocument();
184
+ expect(screen.getByText('Disable')).toBeInTheDocument();
185
+ expect(screen.queryByText('Enable')).not.toBeInTheDocument();
186
+ expect(screen.getByText('Destroy')).toBeInTheDocument();
198
187
  });
199
188
 
200
189
  it('opens clone with the command builder and runtime services from Deck context', async () => {
201
190
  const command = { application: serverGroup.app, stack: 'main' };
202
191
  runtimeServices.serverGroupCommandBuilder = {
203
- buildServerGroupCommandFromExisting: jasmine
204
- .createSpy('buildServerGroupCommandFromExisting')
205
- .and.resolveTo(command),
192
+ buildServerGroupCommandFromExisting: vi.fn().mockResolvedValue(command),
206
193
  };
207
- const show = spyOn(GceCloneServerGroupModal, 'show').and.resolveTo({} as any);
208
- const wrapper = mount(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
194
+ const show = vi.spyOn(GceCloneServerGroupModal, 'show').mockResolvedValue({} as any);
195
+ renderWithRuntime(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
209
196
 
210
- linkAction(wrapper, 'Clone').simulate('click');
211
- await Promise.resolve();
197
+ fireEvent.click(screen.getByText('Clone'));
212
198
 
213
199
  expect(runtimeServices.serverGroupCommandBuilder.buildServerGroupCommandFromExisting).toHaveBeenCalledWith(
214
200
  app,
215
201
  serverGroup,
216
202
  );
217
- expect(show).toHaveBeenCalledWith(
218
- { application: app, command, title: `Clone ${serverGroup.name}` },
219
- runtimeServices,
203
+ await waitFor(() =>
204
+ expect(show).toHaveBeenCalledWith(
205
+ { application: app, command, title: `Clone ${serverGroup.name}` },
206
+ runtimeServices,
207
+ ),
220
208
  );
221
209
  });
222
210
 
223
211
  it('keeps rollback hidden for a disabled server group without changing existing disabled action visibility', () => {
224
- const wrapper = mount(<GceServerGroupActions app={app} serverGroup={{ ...serverGroup, isDisabled: true }} />);
212
+ renderWithRuntime(<GceServerGroupActions app={app} serverGroup={{ ...serverGroup, isDisabled: true }} />);
225
213
 
226
- expect(managedAction(wrapper, 'Rollback').length).toBe(0);
227
- expect(managedAction(wrapper, 'Resize').length).toBe(1);
228
- expect(linkAction(wrapper, 'Disable').length).toBe(0);
229
- expect(linkAction(wrapper, 'Enable').length).toBe(1);
214
+ expect(screen.queryByText('Rollback')).not.toBeInTheDocument();
215
+ expect(screen.getByText('Resize')).toBeInTheDocument();
216
+ expect(screen.queryByText('Disable')).not.toBeInTheDocument();
217
+ expect(screen.getByText('Enable')).toBeInTheDocument();
230
218
  });
231
219
 
232
- it('opens rollback with filtered candidates and the existing server group writer', () => {
233
- const show = spyOn(GceRollbackServerGroupModal, 'show').and.returnValue(Promise.resolve({} as any));
234
- const wrapper = mount(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
220
+ it('opens rollback with filtered candidates and the existing server group writer', async () => {
221
+ const show = vi.spyOn(GceRollbackServerGroupModal, 'show').mockReturnValue(Promise.resolve({} as any));
222
+ renderWithRuntime(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
235
223
 
236
- managedAction(wrapper, 'Rollback').prop('onClick')();
224
+ fireEvent.click(screen.getByText('Rollback'));
237
225
 
238
- expect(show).toHaveBeenCalledOnceWith({
239
- application: app,
240
- serverGroup,
241
- serverGroups: [eligibleRollbackCandidate],
242
- serverGroupWriter: runtimeServices.serverGroupWriter,
243
- });
226
+ await waitFor(() =>
227
+ expect(show).toHaveBeenCalledExactlyOnceWith({
228
+ application: app,
229
+ serverGroup,
230
+ serverGroups: [eligibleRollbackCandidate],
231
+ serverGroupWriter: runtimeServices.serverGroupWriter,
232
+ }),
233
+ );
244
234
  });
245
235
 
246
- it('keeps rollback available when there are no candidates and delegates empty handling to the modal', () => {
236
+ it('keeps rollback available when there are no candidates and delegates empty handling to the modal', async () => {
247
237
  const appWithoutCandidates = { ...app, serverGroups: { ...app.serverGroups, data: [] } };
248
- const show = spyOn(GceRollbackServerGroupModal, 'show').and.returnValue(Promise.resolve({} as any));
249
- const wrapper = mount(<GceServerGroupActions app={appWithoutCandidates} serverGroup={serverGroup} />);
238
+ const show = vi.spyOn(GceRollbackServerGroupModal, 'show').mockReturnValue(Promise.resolve({} as any));
239
+ renderWithRuntime(<GceServerGroupActions app={appWithoutCandidates} serverGroup={serverGroup} />);
250
240
 
251
- expect(managedAction(wrapper, 'Rollback').length).toBe(1);
252
- managedAction(wrapper, 'Rollback').prop('onClick')();
241
+ fireEvent.click(screen.getByText('Rollback'));
253
242
 
254
- expect(show).toHaveBeenCalledOnceWith(
255
- jasmine.objectContaining({ application: appWithoutCandidates, serverGroup, serverGroups: [] }),
243
+ await waitFor(() =>
244
+ expect(show).toHaveBeenCalledExactlyOnceWith(
245
+ expect.objectContaining({ application: appWithoutCandidates, serverGroup, serverGroups: [] }),
246
+ ),
256
247
  );
257
248
  });
258
249
 
259
- it('opens resize with the completed writers', () => {
260
- const show = spyOn(GceResizeServerGroupModal, 'show').and.returnValue(Promise.resolve());
261
- const wrapper = mount(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
250
+ it('opens resize with the completed writers', async () => {
251
+ const show = vi.spyOn(GceResizeServerGroupModal, 'show').mockReturnValue(Promise.resolve());
252
+ renderWithRuntime(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
262
253
 
263
- managedAction(wrapper, 'Resize').prop('onClick')();
254
+ fireEvent.click(screen.getByText('Resize'));
264
255
 
265
- expect(show).toHaveBeenCalledOnceWith({
266
- application: app,
267
- autoscalingPolicyWriter: GceAutoscalingPolicyWriter,
268
- serverGroup,
269
- serverGroupWriter: runtimeServices.serverGroupWriter,
270
- });
256
+ await waitFor(() =>
257
+ expect(show).toHaveBeenCalledExactlyOnceWith({
258
+ application: app,
259
+ autoscalingPolicyWriter: GceAutoscalingPolicyWriter,
260
+ serverGroup,
261
+ serverGroupWriter: runtimeServices.serverGroupWriter,
262
+ }),
263
+ );
271
264
  });
272
265
 
273
266
  it('protects rollback and resize with the managed-resource interstitial', () => {
274
- const wrapper = mount(<GceServerGroupActions app={app} serverGroup={{ ...serverGroup, isManaged: true }} />);
267
+ const confirm = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(new Promise(() => undefined) as any);
268
+ renderWithRuntime(
269
+ <GceServerGroupActions
270
+ app={app}
271
+ serverGroup={{
272
+ ...serverGroup,
273
+ isManaged: true,
274
+ managedResourceSummary: {
275
+ id: 'managed-resource-id',
276
+ isPaused: false,
277
+ locations: { account: 'prod', regions: [] },
278
+ },
279
+ }}
280
+ />,
281
+ );
275
282
 
276
283
  ['Rollback', 'Resize'].forEach((label) => {
277
- expect(managedAction(wrapper, label).props()).toEqual(
278
- jasmine.objectContaining({ application: app, resource: jasmine.objectContaining({ isManaged: true }) }),
279
- );
284
+ fireEvent.click(screen.getByText(label));
280
285
  });
286
+ expect(confirm).toHaveBeenCalledTimes(2);
287
+ expect(confirm.mock.calls.every(([params]) => params.header === 'Pause Management?')).toBe(true);
281
288
  });
282
289
 
283
290
  it('hides server group actions when GCE ad-hoc infrastructure writes are disabled', () => {
284
- spyOn(CloudProviderRegistry, 'isDisabled').and.returnValue(true);
291
+ registerGoogleProvider();
292
+ vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(true);
293
+ const RegisteredActions = CloudProviderRegistry.getValue('gce', 'serverGroup.detailsActions');
285
294
 
286
- const wrapper = mount(<GceServerGroupActions app={app} serverGroup={serverGroup} />);
295
+ const { container } = renderWithRuntime(<RegisteredActions app={app} serverGroup={serverGroup} />);
287
296
 
288
- expect(wrapper.isEmptyRender()).toBe(true);
297
+ expect(container).toBeEmptyDOMElement();
289
298
  });
290
299
  });
300
+
301
+ function expandSection(name: string): void {
302
+ const heading = screen.getByRole('heading', { name });
303
+ const section = heading.closest('.collapsible-section');
304
+ if (!section?.querySelector('.content-body')) {
305
+ fireEvent.click(heading.parentElement as HTMLElement);
306
+ }
307
+ }
308
+
309
+ function definition(term: string): string | undefined {
310
+ return screen.getByText(term, { selector: 'dt' }).nextElementSibling?.textContent ?? undefined;
311
+ }