@spinnaker/google 2026.3.2 → 2026.4.0

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,20 +1,21 @@
1
+ import { fireEvent, render } from '@testing-library/react';
2
+ import type { RenderResult } from '@testing-library/react';
1
3
  import type { FormikProps } from 'formik';
2
- import { shallow } from 'enzyme';
3
4
  import React from 'react';
5
+ import type { Mock, Mocked } from 'vitest';
4
6
 
5
7
  import type { IGceServerGroupCommand, IGceServerGroupWizardAdapter } from '../GceServerGroupWizard.types';
6
- import { GceInstanceFlexibilityConfigurer } from '../zones/GceInstanceFlexibilityConfigurer';
7
- import { ServerGroupCapacity } from './ServerGroupCapacity';
8
+ import { ServerGroupCapacity, validateGceServerGroupCapacity } from './ServerGroupCapacity';
8
9
 
9
10
  describe('GCE server group Capacity page', () => {
10
11
  it('restores desired capacity and keeps simple capacity min, max, and desired linked', () => {
11
12
  const values = command({ autoscalingPolicy: null, capacity: { min: 0, max: 0, desired: 3 } });
12
13
  const { formik } = testProps(values);
13
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
14
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
14
15
 
15
- expect(wrapper.find('input[aria-label="Desired capacity"]').prop('value')).toBe(3);
16
+ expect(input(wrapper, 'input[aria-label="Desired capacity"]').valueAsNumber).toBe(3);
16
17
 
17
- wrapper.find('input[aria-label="Desired capacity"]').simulate('change', { target: { value: '5' } });
18
+ fireEvent.change(input(wrapper, 'input[aria-label="Desired capacity"]'), { target: { value: '5' } });
18
19
 
19
20
  expect(formik.setFieldValue).toHaveBeenCalledWith('capacity', { min: 5, max: 5, desired: 5 });
20
21
  });
@@ -26,13 +27,12 @@ describe('GCE server group Capacity page', () => {
26
27
  viewState: { mode: 'create' },
27
28
  });
28
29
  const { formik } = testProps(values);
29
- const page = new ServerGroupCapacity({ app: {} as any, formik } as any);
30
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
30
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
31
31
 
32
- wrapper.find('input[aria-label="Desired capacity"]').simulate('change', { target: { value: '4' } });
32
+ fireEvent.change(input(wrapper, 'input[aria-label="Desired capacity"]'), { target: { value: '4' } });
33
33
 
34
34
  expect(formik.setFieldValue).toHaveBeenCalledWith('capacity', { min: 4, max: 4, desired: 4 });
35
- expect(page.validate(values)).toEqual({});
35
+ expect(validateGceServerGroupCapacity(values)).toEqual({});
36
36
  });
37
37
 
38
38
  it('derives capacity mode and validation from canonical policy presence instead of stale view state', () => {
@@ -42,13 +42,11 @@ describe('GCE server group Capacity page', () => {
42
42
  viewState: { mode: 'create', useSimpleCapacity: true },
43
43
  });
44
44
  const autoscalingProps = testProps(autoscaling);
45
- const autoscalingWrapper = shallow(<ServerGroupCapacity app={{} as any} formik={autoscalingProps.formik} />);
45
+ const autoscalingWrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={autoscalingProps.formik} />);
46
46
 
47
- expect(autoscalingWrapper.find('input[aria-label="Autoscaling capacity"]').prop('checked')).toBe(true);
48
- expect(autoscalingWrapper.find('input[aria-label="Minimum capacity"]').exists()).toBe(true);
49
- expect(
50
- new ServerGroupCapacity({ app: {} as any, formik: autoscalingProps.formik } as any).validate(autoscaling),
51
- ).toEqual({});
47
+ expect(input(autoscalingWrapper, 'input[aria-label="Autoscaling capacity"]')).toBeChecked();
48
+ expect(autoscalingWrapper.queryByLabelText('Minimum capacity')).toBeInTheDocument();
49
+ expect(validateGceServerGroupCapacity(autoscaling)).toEqual({});
52
50
 
53
51
  const fixed = command({
54
52
  capacity: { min: 3, max: 3, desired: 3 },
@@ -56,11 +54,11 @@ describe('GCE server group Capacity page', () => {
56
54
  viewState: { mode: 'create', useSimpleCapacity: false },
57
55
  });
58
56
  const fixedProps = testProps(fixed);
59
- const fixedWrapper = shallow(<ServerGroupCapacity app={{} as any} formik={fixedProps.formik} />);
57
+ const fixedWrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={fixedProps.formik} />);
60
58
 
61
- expect(fixedWrapper.find('input[aria-label="Simple capacity"]').prop('checked')).toBe(true);
62
- expect(fixedWrapper.find('input[aria-label="Minimum capacity"]').exists()).toBe(false);
63
- expect(new ServerGroupCapacity({ app: {} as any, formik: fixedProps.formik } as any).validate(fixed)).toEqual({});
59
+ expect(input(fixedWrapper, 'input[aria-label="Simple capacity"]')).toBeChecked();
60
+ expect(fixedWrapper.container.querySelector('input[aria-label="Minimum capacity"]')).not.toBeInTheDocument();
61
+ expect(validateGceServerGroupCapacity(fixed)).toEqual({});
64
62
  });
65
63
 
66
64
  it('links autoscaling minimum and maximum to the policy and capacity command fields', () => {
@@ -70,16 +68,16 @@ describe('GCE server group Capacity page', () => {
70
68
  viewState: { mode: 'clone', useSimpleCapacity: false },
71
69
  });
72
70
  const { formik } = testProps(values);
73
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
71
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
74
72
 
75
- expect(wrapper.find('input[aria-label="Minimum capacity"]').prop('value')).toBe(2);
76
- expect(wrapper.find('input[aria-label="Maximum capacity"]').prop('value')).toBe(6);
77
- expect(wrapper.find('input[aria-label="Desired capacity"]').prop('value')).toBe(4);
73
+ expect(input(wrapper, 'input[aria-label="Minimum capacity"]').valueAsNumber).toBe(2);
74
+ expect(input(wrapper, 'input[aria-label="Maximum capacity"]').valueAsNumber).toBe(6);
75
+ expect(input(wrapper, 'input[aria-label="Desired capacity"]').valueAsNumber).toBe(4);
78
76
 
79
- wrapper.find('input[aria-label="Minimum capacity"]').simulate('change', { target: { value: '3' } });
80
- wrapper.find('input[aria-label="Maximum capacity"]').simulate('change', { target: { value: '8' } });
77
+ fireEvent.change(input(wrapper, 'input[aria-label="Minimum capacity"]'), { target: { value: '3' } });
78
+ fireEvent.change(input(wrapper, 'input[aria-label="Maximum capacity"]'), { target: { value: '8' } });
81
79
 
82
- expect(formik.setFieldValue.calls.allArgs()).toEqual([
80
+ expect(formik.setFieldValue.mock.calls).toEqual([
83
81
  ['autoscalingPolicy', { minNumReplicas: 3, maxNumReplicas: 6, unknownPolicyField: 'keep' }],
84
82
  ['capacity', { min: 3, max: 6, desired: 4 }],
85
83
  ['autoscalingPolicy', { minNumReplicas: 3, maxNumReplicas: 8, unknownPolicyField: 'keep' }],
@@ -96,9 +94,9 @@ describe('GCE server group Capacity page', () => {
96
94
  viewState: { mode: 'clone', useSimpleCapacity: false, unrelated: 'keep' },
97
95
  });
98
96
  const { formik } = testProps(values);
99
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
97
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
100
98
 
101
- wrapper.find('input[aria-label="Simple capacity"]').simulate('change', { target: { checked: true } });
99
+ fireEvent.click(input(wrapper, 'input[aria-label="Simple capacity"]'));
102
100
 
103
101
  expect(values.viewState).toEqual({ mode: 'clone', useSimpleCapacity: true, unrelated: 'keep' });
104
102
  expect(values.autoscalingPolicy).toBeNull();
@@ -120,26 +118,31 @@ describe('GCE server group Capacity page', () => {
120
118
  viewState: { mode, useSimpleCapacity: false },
121
119
  });
122
120
  const { formik } = testProps(values);
123
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
121
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
124
122
 
125
- wrapper.find('input[aria-label="Simple capacity"]').simulate('change', { target: { checked: true } });
123
+ fireEvent.click(input(wrapper, 'input[aria-label="Simple capacity"]'));
126
124
 
127
125
  expect(values.overwriteAncestorAutoscalingPolicy).toBe(false);
128
126
  });
129
127
  });
130
128
 
131
- it('does not overwrite ancestor autoscaling for a clone without an autoscaling policy', () => {
129
+ it('does not mark ancestor autoscaling for overwrite when a clone enables autoscaling without an inherited policy', () => {
132
130
  const values = command({
133
131
  autoscalingPolicy: null,
134
132
  overwriteAncestorAutoscalingPolicy: true,
135
133
  viewState: { mode: 'clone', useSimpleCapacity: true },
136
134
  });
137
135
  const { formik } = testProps(values);
138
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
136
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
137
+
138
+ expect(wrapper.getByLabelText('Simple capacity')).toBeChecked();
139
139
 
140
- wrapper.find('input[aria-label="Simple capacity"]').simulate('change', { target: { checked: true } });
140
+ fireEvent.click(wrapper.getByLabelText('Autoscaling capacity'));
141
+ wrapper.rerender(<ServerGroupCapacity app={{} as any} formik={formik} />);
141
142
 
142
143
  expect(values.overwriteAncestorAutoscalingPolicy).toBe(false);
144
+ expect(wrapper.getByLabelText('Autoscaling capacity')).toBeChecked();
145
+ expect(wrapper.getByLabelText('Minimum capacity')).toBeVisible();
143
146
  });
144
147
 
145
148
  it('round trips fixed capacity through autoscaling with a complete synchronized policy', () => {
@@ -151,9 +154,9 @@ describe('GCE server group Capacity page', () => {
151
154
  viewState: { mode: 'clone', useSimpleCapacity: true, unrelated: 'keep' },
152
155
  });
153
156
  const { formik } = testProps(values);
154
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
157
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
155
158
 
156
- wrapper.find('input[aria-label="Autoscaling capacity"]').simulate('change', { target: { checked: true } });
159
+ fireEvent.click(input(wrapper, 'input[aria-label="Autoscaling capacity"]'));
157
160
 
158
161
  expect(values.autoscalingPolicy).toEqual({
159
162
  minNumReplicas: 3,
@@ -167,8 +170,8 @@ describe('GCE server group Capacity page', () => {
167
170
  expect(values.source.useSourceCapacity).toBe(false);
168
171
  expect(values.viewState).toEqual({ mode: 'clone', useSimpleCapacity: false, unrelated: 'keep' });
169
172
 
170
- wrapper.setProps({ formik });
171
- wrapper.find('input[aria-label="Simple capacity"]').simulate('change', { target: { checked: true } });
173
+ wrapper.rerender(<ServerGroupCapacity app={{} as any} formik={formik} />);
174
+ fireEvent.click(input(wrapper, 'input[aria-label="Simple capacity"]'));
172
175
 
173
176
  expect(values.autoscalingPolicy).toBeNull();
174
177
  expect(values.capacity).toEqual({ min: 3, max: 3, desired: 3 });
@@ -177,8 +180,8 @@ describe('GCE server group Capacity page', () => {
177
180
  expect(values.source.useSourceCapacity).toBe(false);
178
181
  expect(values.viewState).toEqual({ mode: 'clone', useSimpleCapacity: true, unrelated: 'keep' });
179
182
 
180
- wrapper.setProps({ formik });
181
- wrapper.find('input[aria-label="Autoscaling capacity"]').simulate('change', { target: { checked: true } });
183
+ wrapper.rerender(<ServerGroupCapacity app={{} as any} formik={formik} />);
184
+ fireEvent.click(input(wrapper, 'input[aria-label="Autoscaling capacity"]'));
182
185
 
183
186
  expect(values.overwriteAncestorAutoscalingPolicy).toBe(false);
184
187
  });
@@ -189,12 +192,12 @@ describe('GCE server group Capacity page', () => {
189
192
  viewState: { mode: 'create', useSimpleCapacity: false },
190
193
  });
191
194
  const { formik } = testProps(values);
192
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
195
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
193
196
 
194
197
  ['-1', '1.5', 'Infinity', 'NaN', ''].forEach((value) => {
195
- wrapper.find('input[aria-label="Minimum capacity"]').simulate('change', { target: { value } });
196
- wrapper.find('input[aria-label="Maximum capacity"]').simulate('change', { target: { value } });
197
- wrapper.find('input[aria-label="Desired capacity"]').simulate('change', { target: { value } });
198
+ fireEvent.change(input(wrapper, 'input[aria-label="Minimum capacity"]'), { target: { value } });
199
+ fireEvent.change(input(wrapper, 'input[aria-label="Maximum capacity"]'), { target: { value } });
200
+ fireEvent.change(input(wrapper, 'input[aria-label="Desired capacity"]'), { target: { value } });
198
201
  });
199
202
 
200
203
  expect(formik.setFieldValue).not.toHaveBeenCalled();
@@ -210,25 +213,24 @@ describe('GCE server group Capacity page', () => {
210
213
  viewState: { mode: 'editPipeline', useSimpleCapacity: false, templatingEnabled: true },
211
214
  });
212
215
  const { formik } = testProps(values);
213
- const page = new ServerGroupCapacity({ app: {} as any, formik } as any);
214
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
216
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
215
217
 
216
- expect(page.validate(values)).toEqual({});
217
- expect(wrapper.find('input[aria-label="Minimum capacity"]').prop('type')).toBe('text');
218
- expect(wrapper.find('input[aria-label="Maximum capacity"]').prop('type')).toBe('text');
219
- expect(wrapper.find('input[aria-label="Desired capacity"]').prop('type')).toBe('text');
218
+ expect(validateGceServerGroupCapacity(values)).toEqual({});
219
+ expect(input(wrapper, 'input[aria-label="Minimum capacity"]')).toHaveAttribute('type', 'text');
220
+ expect(input(wrapper, 'input[aria-label="Maximum capacity"]')).toHaveAttribute('type', 'text');
221
+ expect(input(wrapper, 'input[aria-label="Desired capacity"]')).toHaveAttribute('type', 'text');
220
222
 
221
- wrapper.find('input[aria-label="Minimum capacity"]').simulate('change', {
223
+ fireEvent.change(input(wrapper, 'input[aria-label="Minimum capacity"]'), {
222
224
  target: { value: '${ parameters.newMin }' },
223
225
  });
224
- wrapper.find('input[aria-label="Maximum capacity"]').simulate('change', {
226
+ fireEvent.change(input(wrapper, 'input[aria-label="Maximum capacity"]'), {
225
227
  target: { value: '${ parameters.newMax }' },
226
228
  });
227
- wrapper.find('input[aria-label="Desired capacity"]').simulate('change', {
229
+ fireEvent.change(input(wrapper, 'input[aria-label="Desired capacity"]'), {
228
230
  target: { value: '${ parameters.newDesired }' },
229
231
  });
230
232
 
231
- expect(formik.setFieldValue.calls.allArgs()).toEqual([
233
+ expect(formik.setFieldValue.mock.calls).toEqual([
232
234
  ['autoscalingPolicy', { minNumReplicas: '${ parameters.newMin }', maxNumReplicas: '${ parameters.max }' }],
233
235
  ['capacity', { min: '${ parameters.newMin }', max: '${ parameters.max }', desired: '${ parameters.desired }' }],
234
236
  ['autoscalingPolicy', { minNumReplicas: '${ parameters.newMin }', maxNumReplicas: '${ parameters.newMax }' }],
@@ -243,7 +245,7 @@ describe('GCE server group Capacity page', () => {
243
245
  ]);
244
246
 
245
247
  expect(
246
- page.validate(
248
+ validateGceServerGroupCapacity(
247
249
  command({
248
250
  capacity: { desired: '${ parameters.desired }' } as any,
249
251
  autoscalingPolicy: {
@@ -264,10 +266,8 @@ describe('GCE server group Capacity page', () => {
264
266
 
265
267
  it('validates integer capacities and enforces min <= desired <= max', () => {
266
268
  const { formik } = testProps();
267
- const page = new ServerGroupCapacity({ app: {} as any, formik } as any);
268
-
269
269
  expect(
270
- page.validate(
270
+ validateGceServerGroupCapacity(
271
271
  command({
272
272
  capacity: { desired: Number.POSITIVE_INFINITY },
273
273
  autoscalingPolicy: { minNumReplicas: -1, maxNumReplicas: 2.5 },
@@ -283,7 +283,7 @@ describe('GCE server group Capacity page', () => {
283
283
  });
284
284
 
285
285
  expect(
286
- page.validate(
286
+ validateGceServerGroupCapacity(
287
287
  command({
288
288
  capacity: { desired: 2 },
289
289
  autoscalingPolicy: { minNumReplicas: 3, maxNumReplicas: 4 },
@@ -293,7 +293,7 @@ describe('GCE server group Capacity page', () => {
293
293
  ).toEqual({ capacity: { desired: 'Desired capacity must be at least minimum capacity.' } });
294
294
 
295
295
  expect(
296
- page.validate(
296
+ validateGceServerGroupCapacity(
297
297
  command({
298
298
  capacity: { desired: 5 },
299
299
  autoscalingPolicy: { minNumReplicas: 1, maxNumReplicas: 4 },
@@ -306,7 +306,7 @@ describe('GCE server group Capacity page', () => {
306
306
  it('requires a zone for zonal commands and invokes zoneChanged with the selected zone', async () => {
307
307
  const values = command({ zone: 'persisted-zone' });
308
308
  const { adapter, formik, reconciled } = testProps(values);
309
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} adapter={adapter} />);
309
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} adapter={adapter} />);
310
310
 
311
311
  expect(selectOptions(wrapper, 'Zone')).toEqual([
312
312
  ['', 'Select...'],
@@ -314,14 +314,14 @@ describe('GCE server group Capacity page', () => {
314
314
  ['known-zone-b', 'known-zone-b'],
315
315
  ['persisted-zone', 'persisted-zone (unavailable)'],
316
316
  ]);
317
- expect(new ServerGroupCapacity({ app: {} as any, formik } as any).validate(command({ zone: null }))).toEqual({
317
+ expect(validateGceServerGroupCapacity(command({ zone: null }))).toEqual({
318
318
  zone: 'Zone required.',
319
319
  });
320
320
 
321
- wrapper.find('select[aria-label="Zone"]').simulate('change', { target: { value: 'known-zone-b' } });
321
+ fireEvent.change(wrapper.getByLabelText('Zone'), { target: { value: 'known-zone-b' } });
322
322
  await flush();
323
323
 
324
- const changedCommand = adapter.applyCommandHandler.calls.first().args[0];
324
+ const changedCommand = adapter.applyCommandHandler.mock.calls[0][0];
325
325
  expect(changedCommand.zone).toBe('known-zone-b');
326
326
  expect(adapter.applyCommandHandler).toHaveBeenCalledWith(changedCommand, 'zoneChanged');
327
327
  expect(handlerNames(adapter)).toEqual(['zoneChanged', 'selectZonesChanged']);
@@ -336,19 +336,19 @@ describe('GCE server group Capacity page', () => {
336
336
  distributionPolicy: { zones: ['known-zone-a', 'persisted-zone'], targetShape: 'EVEN' },
337
337
  });
338
338
  const { adapter, formik } = testProps(values);
339
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} adapter={adapter} />);
339
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} adapter={adapter} />);
340
340
 
341
- expect(wrapper.find('input[aria-label="Explicit zone distribution"]').prop('checked')).toBe(true);
342
- expect(wrapper.find('input[aria-label="Preferred zone distribution"]').prop('checked')).toBe(false);
343
- expect(wrapper.find('input[aria-label="Zone known-zone-a"]').prop('checked')).toBe(true);
344
- expect(wrapper.find('input[aria-label="Zone persisted-zone"]').prop('checked')).toBe(true);
345
- expect(wrapper.find('label[htmlFor="gce-capacity-zone-persisted-zone"]').text()).toContain('(unavailable)');
346
- expect(wrapper.find('select[aria-label="Target shape"]').prop('value')).toBe('EVEN');
341
+ expect(wrapper.getByLabelText('Explicit zone distribution')).toBeChecked();
342
+ expect(wrapper.getByLabelText('Preferred zone distribution')).not.toBeChecked();
343
+ expect(wrapper.getByLabelText('Zone known-zone-a')).toBeChecked();
344
+ expect(wrapper.getByLabelText('Zone persisted-zone')).toBeChecked();
345
+ expect(wrapper.getByText(/persisted-zone \(unavailable\)/)).toBeInTheDocument();
346
+ expect(wrapper.getByLabelText('Target shape')).toHaveValue('EVEN');
347
347
 
348
- wrapper.find('input[aria-label="Preferred zone distribution"]').simulate('change', { target: { checked: true } });
348
+ fireEvent.click(wrapper.getByLabelText('Preferred zone distribution'));
349
349
  await flush();
350
350
 
351
- const changedCommand = adapter.applyCommandHandler.calls.first().args[0];
351
+ const changedCommand = adapter.applyCommandHandler.mock.calls[0][0];
352
352
  expect(changedCommand.selectZones).toBe(false);
353
353
  expect(changedCommand.distributionPolicy.zones).toEqual(['known-zone-a', 'persisted-zone']);
354
354
  expect(adapter.applyCommandHandler).toHaveBeenCalledWith(changedCommand, 'selectZonesChanged');
@@ -359,28 +359,28 @@ describe('GCE server group Capacity page', () => {
359
359
  const transitions = [deferredCommand(), deferredCommand(), deferredCommand(), deferredCommand()];
360
360
  const values = command();
361
361
  const { adapter, formik } = transitionProps(values, transitions);
362
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} adapter={adapter} />);
362
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} adapter={adapter} />);
363
363
 
364
- wrapper.find('input[aria-label="Regional server group"]').simulate('change', { target: { checked: true } });
364
+ fireEvent.click(wrapper.getByLabelText('Regional server group'));
365
365
  expect(handlerNames(adapter)).toEqual(['regionalChanged']);
366
366
 
367
367
  const afterRegional = command({ regional: true, zone: null, transition: 'regional' });
368
368
  transitions[0].resolve(update(afterRegional));
369
369
  await flush();
370
370
  expect(handlerNames(adapter)).toEqual(['regionalChanged', 'regionChanged']);
371
- expect(adapter.applyCommandHandler.calls.argsFor(1)[0]).toBe(afterRegional);
371
+ expect(adapter.applyCommandHandler.mock.calls[1][0]).toBe(afterRegional);
372
372
 
373
373
  const afterRegion = command({ regional: true, zone: null, transition: 'region' });
374
374
  transitions[1].resolve(update(afterRegion));
375
375
  await flush();
376
376
  expect(handlerNames(adapter)).toEqual(['regionalChanged', 'regionChanged', 'zoneChanged']);
377
- expect(adapter.applyCommandHandler.calls.argsFor(2)[0]).toBe(afterRegion);
377
+ expect(adapter.applyCommandHandler.mock.calls[2][0]).toBe(afterRegion);
378
378
 
379
379
  const afterZone = command({ regional: true, zone: null, transition: 'zone' });
380
380
  transitions[2].resolve(update(afterZone));
381
381
  await flush();
382
382
  expect(handlerNames(adapter)).toEqual(['regionalChanged', 'regionChanged', 'zoneChanged', 'selectZonesChanged']);
383
- expect(adapter.applyCommandHandler.calls.argsFor(3)[0]).toBe(afterZone);
383
+ expect(adapter.applyCommandHandler.mock.calls[3][0]).toBe(afterZone);
384
384
  expect(formik.setValues).not.toHaveBeenCalled();
385
385
 
386
386
  const reconciled = command({ regional: true, zone: null, transition: 'selectZones' });
@@ -392,15 +392,17 @@ describe('GCE server group Capacity page', () => {
392
392
  it('serializes zonal and explicit-zone reconciliation handlers', async () => {
393
393
  const zonalTransitions = [deferredCommand(), deferredCommand()];
394
394
  const zonal = transitionProps(command(), zonalTransitions);
395
- const zonalWrapper = shallow(<ServerGroupCapacity app={{} as any} formik={zonal.formik} adapter={zonal.adapter} />);
395
+ const zonalWrapper = renderPage(
396
+ <ServerGroupCapacity app={{} as any} formik={zonal.formik} adapter={zonal.adapter} />,
397
+ );
396
398
 
397
- zonalWrapper.find('select[aria-label="Zone"]').simulate('change', { target: { value: 'known-zone-b' } });
399
+ fireEvent.change(zonalWrapper.getByLabelText('Zone'), { target: { value: 'known-zone-b' } });
398
400
  expect(handlerNames(zonal.adapter)).toEqual(['zoneChanged']);
399
401
  const afterZone = command({ zone: 'known-zone-b', transition: 'zone' });
400
402
  zonalTransitions[0].resolve(update(afterZone));
401
403
  await flush();
402
404
  expect(handlerNames(zonal.adapter)).toEqual(['zoneChanged', 'selectZonesChanged']);
403
- expect(zonal.adapter.applyCommandHandler.calls.argsFor(1)[0]).toBe(afterZone);
405
+ expect(zonal.adapter.applyCommandHandler.mock.calls[1][0]).toBe(afterZone);
404
406
  const zonalReconciled = command({ zone: 'known-zone-b', transition: 'selectZones' });
405
407
  zonalTransitions[1].resolve(update(zonalReconciled));
406
408
  await flush();
@@ -411,11 +413,11 @@ describe('GCE server group Capacity page', () => {
411
413
  command({ regional: true, zone: null, selectZones: true, distributionPolicy: { zones: ['known-zone-a'] } }),
412
414
  regionalTransitions,
413
415
  );
414
- const regionalWrapper = shallow(
416
+ const regionalWrapper = renderPage(
415
417
  <ServerGroupCapacity app={{} as any} formik={regional.formik} adapter={regional.adapter} />,
416
418
  );
417
419
 
418
- regionalWrapper.find('input[aria-label="Zone known-zone-b"]').simulate('change', { target: { checked: true } });
420
+ fireEvent.click(regionalWrapper.getByLabelText('Zone known-zone-b'));
419
421
  expect(handlerNames(regional.adapter)).toEqual(['selectZonesChanged']);
420
422
  const afterSelectZones = command({
421
423
  regional: true,
@@ -427,7 +429,7 @@ describe('GCE server group Capacity page', () => {
427
429
  regionalTransitions[0].resolve(update(afterSelectZones));
428
430
  await flush();
429
431
  expect(handlerNames(regional.adapter)).toEqual(['selectZonesChanged', 'zoneChanged']);
430
- expect(regional.adapter.applyCommandHandler.calls.argsFor(1)[0]).toBe(afterSelectZones);
432
+ expect(regional.adapter.applyCommandHandler.mock.calls[1][0]).toBe(afterSelectZones);
431
433
  const regionalReconciled = { ...afterSelectZones, transition: 'zone' };
432
434
  regionalTransitions[1].resolve(update(regionalReconciled));
433
435
  await flush();
@@ -441,15 +443,15 @@ describe('GCE server group Capacity page', () => {
441
443
  distributionPolicy: { zones: [], targetShape: 'EVEN' },
442
444
  });
443
445
  const { formik } = testProps(values);
444
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
446
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
445
447
 
446
- wrapper.find('select[aria-label="Target shape"]').simulate('change', { target: { value: 'ANY' } });
448
+ fireEvent.change(wrapper.getByLabelText('Target shape'), { target: { value: 'ANY' } });
447
449
 
448
450
  expect(formik.setFieldValue).toHaveBeenCalledWith('distributionPolicy', { zones: [], targetShape: 'ANY' });
449
- expect(new ServerGroupCapacity({ app: {} as any, formik } as any).validate(values)).toEqual({});
450
- expect(
451
- new ServerGroupCapacity({ app: {} as any, formik } as any).validate({ ...values, selectZones: true }),
452
- ).toEqual({ distributionPolicy: { zones: 'At least one zone required.' } });
451
+ expect(validateGceServerGroupCapacity(values)).toEqual({});
452
+ expect(validateGceServerGroupCapacity({ ...values, selectZones: true })).toEqual({
453
+ distributionPolicy: { zones: 'At least one zone required.' },
454
+ });
453
455
  });
454
456
 
455
457
  it('renders the React instance flexibility configurer and immutably persists policy changes', () => {
@@ -465,25 +467,31 @@ describe('GCE server group Capacity page', () => {
465
467
  instanceFlexibilityPolicy,
466
468
  });
467
469
  const { formik } = testProps(values);
468
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
469
- const configurer = wrapper.find(GceInstanceFlexibilityConfigurer);
470
-
471
- expect(configurer.props()).toEqual(
472
- jasmine.objectContaining({
473
- instanceFlexibilityPolicy,
474
- regional: true,
475
- targetShape: 'BALANCED',
476
- }),
470
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
471
+
472
+ expect(wrapper.getByRole('textbox', { name: 'Selection name' })).toHaveValue('preferred');
473
+ expect(wrapper.getByRole('spinbutton', { name: 'Rank (optional)' })).toHaveValue(1);
474
+ expect(wrapper.getByRole('textbox', { name: 'Machine type 1 for selection preferred' })).toHaveValue(
475
+ 'n2-standard-8',
477
476
  );
477
+ // Regional BALANCED commands satisfy the configurer's regional and target shape requirements.
478
+ expect(wrapper.queryByText('Flexibility requires a regional server group.')).not.toBeInTheDocument();
479
+ expect(wrapper.queryByText(/Flexibility requires target shape/)).not.toBeInTheDocument();
478
480
 
479
- const nextPolicy = {
481
+ fireEvent.change(wrapper.getByRole('textbox', { name: 'Machine type 1 for selection preferred' }), {
482
+ target: { value: 'e2-standard-8' },
483
+ });
484
+
485
+ expect(formik.setFieldValue).toHaveBeenCalledWith('instanceFlexibilityPolicy', {
480
486
  instanceSelections: {
481
- fallback: { machineTypes: ['e2-standard-8'] },
487
+ preferred: { rank: 1, machineTypes: ['e2-standard-8'] },
482
488
  },
483
- };
484
- configurer.prop('setInstanceFlexibilityPolicy')(nextPolicy);
485
-
486
- expect(formik.setFieldValue).toHaveBeenCalledWith('instanceFlexibilityPolicy', nextPolicy);
489
+ });
490
+ expect(instanceFlexibilityPolicy).toEqual({
491
+ instanceSelections: {
492
+ preferred: { rank: 1, machineTypes: ['n2-standard-8'] },
493
+ },
494
+ });
487
495
  });
488
496
 
489
497
  it('passes the Formik flexibility validation error to the configurer', () => {
@@ -501,16 +509,15 @@ describe('GCE server group Capacity page', () => {
501
509
  (formik as any).errors = {
502
510
  instanceFlexibilityPolicy: 'Instance flexibility policy is invalid.',
503
511
  };
504
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
512
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
505
513
 
506
- expect(wrapper.find(GceInstanceFlexibilityConfigurer).prop('validationError')).toBe(
514
+ expect(wrapper.getByRole('alert')).toHaveTextContent('Instance flexibility policy is invalid.');
515
+ expect(wrapper.getByRole('spinbutton', { name: 'Rank (optional)' })).toHaveAccessibleDescription(
507
516
  'Instance flexibility policy is invalid.',
508
517
  );
509
518
  });
510
519
 
511
520
  it('blocks invalid enabled flexibility while allowing absent and explicitly empty policies', () => {
512
- const { formik } = testProps();
513
- const page = new ServerGroupCapacity({ app: {} as any, formik } as any);
514
521
  const enabledPolicy = {
515
522
  instanceSelections: {
516
523
  preferred: { machineTypes: ['n2-standard-8'] },
@@ -518,7 +525,7 @@ describe('GCE server group Capacity page', () => {
518
525
  };
519
526
 
520
527
  expect(
521
- page.validate(
528
+ validateGceServerGroupCapacity(
522
529
  command({
523
530
  regional: false,
524
531
  distributionPolicy: { zones: [], targetShape: 'BALANCED' },
@@ -527,7 +534,7 @@ describe('GCE server group Capacity page', () => {
527
534
  ).instanceFlexibilityPolicy,
528
535
  ).toBeDefined();
529
536
  expect(
530
- page.validate(
537
+ validateGceServerGroupCapacity(
531
538
  command({
532
539
  regional: true,
533
540
  zone: null,
@@ -539,7 +546,7 @@ describe('GCE server group Capacity page', () => {
539
546
 
540
547
  ['BALANCED', 'ANY', 'ANY_SINGLE_ZONE'].forEach((targetShape) => {
541
548
  expect(
542
- page.validate(
549
+ validateGceServerGroupCapacity(
543
550
  command({
544
551
  regional: true,
545
552
  zone: null,
@@ -550,9 +557,9 @@ describe('GCE server group Capacity page', () => {
550
557
  ).toEqual({});
551
558
  });
552
559
 
553
- expect(page.validate(command({ instanceFlexibilityPolicy: undefined }))).toEqual({});
560
+ expect(validateGceServerGroupCapacity(command({ instanceFlexibilityPolicy: undefined }))).toEqual({});
554
561
  expect(
555
- page.validate(
562
+ validateGceServerGroupCapacity(
556
563
  command({
557
564
  instanceFlexibilityPolicy: { instanceSelections: {} },
558
565
  }),
@@ -569,7 +576,7 @@ describe('GCE server group Capacity page', () => {
569
576
  },
570
577
  });
571
578
  const { formik } = testProps(values);
572
- const wrapper = shallow(<ServerGroupCapacity app={{} as any} formik={formik} />);
579
+ const wrapper = renderPage(<ServerGroupCapacity app={{} as any} formik={formik} />);
573
580
 
574
581
  expect(selectOptions(wrapper, 'Target shape')).toEqual([
575
582
  ['', 'Select...'],
@@ -581,45 +588,52 @@ describe('GCE server group Capacity page', () => {
581
588
  });
582
589
  });
583
590
 
584
- function selectOptions(wrapper: ReturnType<typeof shallow>, label: string): string[][] {
585
- return wrapper
586
- .find(`select[aria-label="${label}"] option`)
587
- .map((option) => [option.prop('value') as string, option.text()]);
591
+ function selectOptions(wrapper: RenderResult, label: string): string[][] {
592
+ return Array.from(wrapper.getByLabelText(label).querySelectorAll('option')).map((option) => [
593
+ option.value,
594
+ option.textContent || '',
595
+ ]);
596
+ }
597
+
598
+ function input(wrapper: RenderResult, selector: string): HTMLInputElement {
599
+ const element = wrapper.container.querySelector<HTMLInputElement>(selector);
600
+ if (!element) {
601
+ throw new Error(`Missing input: ${selector}`);
602
+ }
603
+ return element;
588
604
  }
589
605
 
590
606
  function testProps(values = command()) {
591
607
  const formik = ({
592
608
  values,
593
- setFieldValue: jasmine.createSpy('setFieldValue').and.callFake((field: string, value: any) => {
609
+ setFieldValue: vi.fn().mockImplementation((field: string, value: any) => {
594
610
  values[field] = value;
595
611
  }),
596
- setValues: jasmine.createSpy('setValues'),
612
+ setValues: vi.fn(),
597
613
  } as unknown) as FormikProps<IGceServerGroupCommand>;
598
614
  const reconciled = command({ region: 'reconciled-region' });
599
615
  const adapter = ({
600
- applyCommandHandler: jasmine
601
- .createSpy('applyCommandHandler')
602
- .and.resolveTo({ command: reconciled, result: { dirty: {} } }),
603
- } as unknown) as jasmine.SpyObj<IGceServerGroupWizardAdapter>;
616
+ applyCommandHandler: vi.fn().mockResolvedValue({ command: reconciled, result: { dirty: {} } }),
617
+ } as unknown) as Mocked<IGceServerGroupWizardAdapter>;
604
618
  return { adapter, formik, reconciled };
605
619
  }
606
620
 
607
621
  function transitionProps(values: IGceServerGroupCommand, transitions: Array<ReturnType<typeof deferredCommand>>) {
608
622
  const formik = ({
609
623
  values,
610
- setFieldValue: jasmine.createSpy('setFieldValue'),
611
- setValues: jasmine.createSpy('setValues'),
624
+ setFieldValue: vi.fn(),
625
+ setValues: vi.fn(),
612
626
  } as unknown) as FormikProps<IGceServerGroupCommand>;
613
627
  const adapter = ({
614
- applyCommandHandler: jasmine
615
- .createSpy('applyCommandHandler')
616
- .and.callFake(() => transitions[(adapter.applyCommandHandler as jasmine.Spy).calls.count() - 1].promise),
617
- } as unknown) as jasmine.SpyObj<IGceServerGroupWizardAdapter>;
628
+ applyCommandHandler: vi
629
+ .fn()
630
+ .mockImplementation(() => transitions[(adapter.applyCommandHandler as Mock).mock.calls.length - 1].promise),
631
+ } as unknown) as Mocked<IGceServerGroupWizardAdapter>;
618
632
  return { adapter, formik };
619
633
  }
620
634
 
621
- function handlerNames(adapter: jasmine.SpyObj<IGceServerGroupWizardAdapter>): string[] {
622
- return adapter.applyCommandHandler.calls.allArgs().map((args) => args[1]);
635
+ function handlerNames(adapter: Mocked<IGceServerGroupWizardAdapter>): string[] {
636
+ return adapter.applyCommandHandler.mock.calls.map((args) => args[1]);
623
637
  }
624
638
 
625
639
  function update(commandValue: IGceServerGroupCommand) {
@@ -657,3 +671,8 @@ async function flush(): Promise<void> {
657
671
  await Promise.resolve();
658
672
  await Promise.resolve();
659
673
  }
674
+
675
+ function renderPage(component: React.ReactElement): RenderResult {
676
+ const container = document.body.appendChild(document.createElement('div'));
677
+ return render(component, { baseElement: container, container });
678
+ }