@spinnaker/cloudfoundry 2026.2.2 → 2026.3.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 (46) hide show
  1. package/dist/image/image.reader.cf.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/instance/details/CloudFoundryInstanceActions.d.ts +5 -2
  5. package/dist/loadBalancer/configure/CloudFoundryMapLoadBalancerModal.d.ts +5 -4
  6. package/dist/loadBalancer/details/CloudFoundryLoadBalancerActions.d.ts +5 -2
  7. package/dist/loadBalancer/loadBalancer.transformer.d.ts +2 -5
  8. package/dist/pipeline/stages/cloneServerGroup/CloudFoundryCloneServerGroupStageConfig.d.ts +3 -0
  9. package/dist/serverGroup/configure/serverGroupCommandBuilderShim.service.cf.d.ts +5 -9
  10. package/dist/serverGroup/configure/wizard/CreateServerGroupModal.d.ts +5 -3
  11. package/dist/serverGroup/details/cloudFoundryServerGroupActions.d.ts +8 -2
  12. package/dist/serverGroup/details/mapLoadBalancers/CloudFoundryMapLoadBalancersModal.d.ts +5 -3
  13. package/dist/serverGroup/details/mapLoadBalancers/CloudFoundryUnmapLoadBalancersModal.d.ts +5 -3
  14. package/dist/serverGroup/details/resize/CloudFoundryResizeServerGroupModal.d.ts +5 -3
  15. package/dist/serverGroup/details/rollback/CloudFoundryRollbackServerGroupModal.d.ts +5 -3
  16. package/dist/serverGroup/serverGroup.transformer.d.ts +1 -4
  17. package/package.json +3 -9
  18. package/src/image/image.reader.cf.ts +1 -1
  19. package/src/instance/details/CloudFoundryInstanceActions.tsx +11 -6
  20. package/src/instance/details/CloudFoundryInstanceDetails.tsx +0 -2
  21. package/src/loadBalancer/configure/CloudFoundryMapLoadBalancerModal.tsx +10 -16
  22. package/src/loadBalancer/details/CloudFoundryLoadBalancerActions.tsx +10 -7
  23. package/src/loadBalancer/details/CloudFoundryLoadBalancerDetails.tsx +1 -3
  24. package/src/loadBalancer/loadBalancer.transformer.ts +3 -6
  25. package/src/pipeline/stages/cloneServerGroup/CloudFoundryCloneServerGroupStageConfig.tsx +13 -7
  26. package/src/pipeline/stages/createServiceBindings/CloudFoundryCreateServiceBindingsStageConfigForm.tsx +1 -2
  27. package/src/pipeline/stages/createServiceBindings/cloudFoundryCreateServiceBindingsStageForm.spec.tsx +25 -21
  28. package/src/pipeline/stages/deleteServiceBindings/CloudFoundryDeleteServiceBindingsStageConfigForm.tsx +1 -2
  29. package/src/pipeline/stages/deleteServiceBindings/cloudFoundryDeleteServiceBindingsStageForm.spec.tsx +23 -18
  30. package/src/pipeline/stages/resizeAsg/CloudFoundryResizeAsgStageConfig.tsx +1 -2
  31. package/src/pipeline/stages/rollbackCluster/cloudFoundryRollbackClusterStage.module.ts +0 -1
  32. package/src/pipeline/stages/runJob/CloudFoundryRunJobStageConfig.tsx +1 -2
  33. package/src/pipeline/stages/unshareService/cloudFoundryUnshareServiceStage.module.ts +0 -1
  34. package/src/presentation/pipeline/stages/CloudFoundryAsgStageConfig.tsx +1 -2
  35. package/src/presentation/pipeline/stages/CloudFoundryLoadBalancersStageConfig.tsx +1 -2
  36. package/src/presentation/widgets/accountRegionClusterSelector/AccountRegionClusterSelector.spec.tsx +41 -43
  37. package/src/routerActions.spec.tsx +76 -0
  38. package/src/serverGroup/configure/serverGroupCommandBuilderShim.service.cf.ts +12 -18
  39. package/src/serverGroup/configure/wizard/CreateServerGroupModal.tsx +13 -7
  40. package/src/serverGroup/details/cfServerGroupDetailsGetter.ts +1 -1
  41. package/src/serverGroup/details/cloudFoundryServerGroupActions.tsx +39 -25
  42. package/src/serverGroup/details/mapLoadBalancers/CloudFoundryMapLoadBalancersModal.tsx +19 -6
  43. package/src/serverGroup/details/mapLoadBalancers/CloudFoundryUnmapLoadBalancersModal.tsx +19 -6
  44. package/src/serverGroup/details/resize/CloudFoundryResizeServerGroupModal.tsx +19 -6
  45. package/src/serverGroup/details/rollback/CloudFoundryRollbackServerGroupModal.tsx +13 -6
  46. package/src/serverGroup/serverGroup.transformer.ts +2 -5
@@ -1,20 +1,24 @@
1
- import { mock } from 'angular';
2
- import type { IStage } from 'core';
3
- import { ApplicationModelBuilder, REACT_MODULE, SpinFormik, StageConfigField } from 'core';
4
- import { mount } from 'enzyme';
1
+ import type { Application, IStage } from 'core';
2
+ import { AccountService, StageConfigField } from 'core';
3
+ import { shallow } from 'enzyme';
5
4
  import React from 'react';
6
5
 
7
- import { mockServerGroupDataSourceConfig } from '@spinnaker/mocks';
8
-
9
6
  import { CloudFoundryCreateServiceBindingsStageConfigForm } from './CloudFoundryCreateServiceBindingsStageConfigForm';
10
7
 
11
8
  describe('<CloudFoundryCreateServiceBindingsStageConfigForm/>', function () {
12
- beforeEach(mock.module(REACT_MODULE));
13
- beforeEach(mock.inject());
9
+ const application = {
10
+ ready: () => Promise.resolve(),
11
+ getDataSource: () => ({ data: [] }),
12
+ } as Application;
13
+
14
+ beforeEach(() => {
15
+ spyOn(AccountService, 'listAccounts').and.returnValue(Promise.resolve([]));
16
+ spyOn(AccountService, 'getRegionsForAccount').and.returnValue(Promise.resolve([]));
17
+ });
14
18
 
15
19
  const getProps = () => {
16
20
  return {
17
- application: ApplicationModelBuilder.createApplicationForTests('my-application', mockServerGroupDataSourceConfig),
21
+ application,
18
22
  pipeline: {
19
23
  application: 'my-application',
20
24
  id: 'pipeline-id',
@@ -32,19 +36,19 @@ describe('<CloudFoundryCreateServiceBindingsStageConfigForm/>', function () {
32
36
  const stage = ({
33
37
  serviceBindingRequests: [{ serviceInstanceName: 'service1' }, { serviceInstanceName: 'service2' }],
34
38
  } as unknown) as IStage;
39
+ const formik = {
40
+ values: stage,
41
+ setFieldValue: jasmine.createSpy('setFieldValue'),
42
+ } as any;
35
43
 
36
44
  const props = getProps();
37
- const component = mount(
38
- <SpinFormik
39
- initialValues={stage}
40
- onSubmit={() => null}
41
- validate={() => null}
42
- render={(formik) => <CloudFoundryCreateServiceBindingsStageConfigForm {...props} formik={formik} />}
43
- />,
44
- );
45
- expect(component.find(StageConfigField).findWhere((x) => x.text() === 'Target').length).toBe(1);
46
- expect(component.find(StageConfigField).findWhere((x) => x.text() === 'Restage Required').length).toBe(1);
47
- expect(component.find(StageConfigField).findWhere((x) => x.text() === 'Restart Required').length).toBe(1);
48
- expect(component.find(StageConfigField).findWhere((x) => x.text() === 'Service Instance Name').length).toBe(2);
45
+ const component = shallow(<CloudFoundryCreateServiceBindingsStageConfigForm {...props} formik={formik} />);
46
+
47
+ expect(component.find(StageConfigField).filterWhere((x) => x.prop('label') === 'Target').length).toBe(1);
48
+ expect(component.find(StageConfigField).filterWhere((x) => x.prop('label') === 'Restage Required').length).toBe(1);
49
+ expect(component.find(StageConfigField).filterWhere((x) => x.prop('label') === 'Restart Required').length).toBe(1);
50
+ expect(
51
+ component.find(StageConfigField).filterWhere((x) => x.prop('label') === 'Service Instance Name').length,
52
+ ).toBe(2);
49
53
  });
50
54
  });
@@ -3,7 +3,7 @@ import { from as observableFrom, Subject } from 'rxjs';
3
3
  import { takeUntil } from 'rxjs/operators';
4
4
 
5
5
  import type { Application, IAccount, IFormikStageConfigInjectedProps, IRegion } from '@spinnaker/core';
6
- import { AccountService, NgReact, StageConfigField, StageConstants, TextInput } from '@spinnaker/core';
6
+ import { AccountService, StageConfigField, StageConstants, TargetSelect, TextInput } from '@spinnaker/core';
7
7
 
8
8
  import { AccountRegionClusterSelector } from '../../../presentation/widgets/accountRegionClusterSelector';
9
9
 
@@ -96,7 +96,6 @@ export class CloudFoundryDeleteServiceBindingsStageConfigForm extends React.Comp
96
96
  const stage = this.props.formik.values;
97
97
  const { accounts, application } = this.state;
98
98
  const { target } = stage;
99
- const { TargetSelect } = NgReact;
100
99
 
101
100
  return (
102
101
  <>
@@ -1,20 +1,25 @@
1
- import { mock } from 'angular';
2
- import { mount } from 'enzyme';
1
+ import { shallow } from 'enzyme';
3
2
  import React from 'react';
4
3
 
5
- import type { IStage } from '@spinnaker/core';
6
- import { ApplicationModelBuilder, REACT_MODULE, SpinFormik, StageConfigField } from '@spinnaker/core';
7
- import { mockServerGroupDataSourceConfig } from '@spinnaker/mocks';
4
+ import type { Application, IStage } from '@spinnaker/core';
5
+ import { AccountService, StageConfigField } from '@spinnaker/core';
8
6
 
9
7
  import { CloudFoundryDeleteServiceBindingsStageConfigForm } from './CloudFoundryDeleteServiceBindingsStageConfigForm';
10
8
 
11
9
  describe('<CloudFoundryDeleteServiceBindingsStageConfigForm/>', function () {
12
- beforeEach(mock.module(REACT_MODULE));
13
- beforeEach(mock.inject());
10
+ const application = {
11
+ ready: () => Promise.resolve(),
12
+ getDataSource: () => ({ data: [] }),
13
+ } as Application;
14
+
15
+ beforeEach(() => {
16
+ spyOn(AccountService, 'listAccounts').and.returnValue(Promise.resolve([]));
17
+ spyOn(AccountService, 'getRegionsForAccount').and.returnValue(Promise.resolve([]));
18
+ });
14
19
 
15
20
  const getProps = () => {
16
21
  return {
17
- application: ApplicationModelBuilder.createApplicationForTests('my-application', mockServerGroupDataSourceConfig),
22
+ application,
18
23
  pipeline: {
19
24
  application: 'my-application',
20
25
  id: 'pipeline-id',
@@ -32,18 +37,18 @@ describe('<CloudFoundryDeleteServiceBindingsStageConfigForm/>', function () {
32
37
  const stage = ({
33
38
  serviceUnbindingRequests: [{ serviceInstanceName: 'service1' }, { serviceInstanceName: 'service2' }],
34
39
  } as unknown) as IStage;
40
+ const formik = {
41
+ values: stage,
42
+ setFieldValue: jasmine.createSpy('setFieldValue'),
43
+ } as any;
35
44
 
36
45
  const props = getProps();
37
46
 
38
- const component = mount(
39
- <SpinFormik
40
- initialValues={stage}
41
- onSubmit={() => null}
42
- validate={() => null}
43
- render={(formik) => <CloudFoundryDeleteServiceBindingsStageConfigForm {...props} formik={formik} />}
44
- />,
45
- );
46
- expect(component.find(StageConfigField).findWhere((x) => x.text() === 'Target').length).toBe(1);
47
- expect(component.find(StageConfigField).findWhere((x) => x.text() === 'Service Instance Name').length).toBe(2);
47
+ const component = shallow(<CloudFoundryDeleteServiceBindingsStageConfigForm {...props} formik={formik} />);
48
+
49
+ expect(component.find(StageConfigField).filterWhere((x) => x.prop('label') === 'Target').length).toBe(1);
50
+ expect(
51
+ component.find(StageConfigField).filterWhere((x) => x.prop('label') === 'Service Instance Name').length,
52
+ ).toBe(2);
48
53
  });
49
54
  });
@@ -3,7 +3,7 @@ import { from as observableFrom, Subject } from 'rxjs';
3
3
  import { takeUntil } from 'rxjs/operators';
4
4
 
5
5
  import type { IAccount, IStageConfigProps } from '@spinnaker/core';
6
- import { AccountService, NgReact, StageConfigField, StageConstants } from '@spinnaker/core';
6
+ import { AccountService, StageConfigField, StageConstants, TargetSelect } from '@spinnaker/core';
7
7
  import { AccountRegionClusterSelector } from '../../../presentation';
8
8
 
9
9
  export interface ICloudFoundryResizeAsgStageConfigState {
@@ -87,7 +87,6 @@ export class CloudFoundryResizeAsgStageConfig extends React.Component<
87
87
  const { application, pipeline, stage } = this.props;
88
88
  const { capacity, diskQuota, memory, target } = stage;
89
89
  const instanceCount = capacity.desired;
90
- const { TargetSelect } = NgReact;
91
90
  return (
92
91
  <div className="cloudfoundry-resize-asg-stage form-horizontal">
93
92
  {!pipeline.strategy && (
@@ -10,7 +10,6 @@ Registry.pipeline.registerStage({
10
10
  key: 'rollbackCluster',
11
11
  cloudProvider: 'cloudfoundry',
12
12
  component: CloudFoundryRollbackClusterStageConfig,
13
- controller: 'cfRollbackClusterStageCtrl',
14
13
  validators: [
15
14
  { type: 'requiredField', preventSave: true, fieldName: 'cluster' },
16
15
  { type: 'requiredField', preventSave: true, fieldName: 'regions' },
@@ -3,7 +3,7 @@ import { from as observableFrom, Subject } from 'rxjs';
3
3
  import { takeUntil } from 'rxjs/operators';
4
4
 
5
5
  import type { IAccount, IStageConfigProps } from '@spinnaker/core';
6
- import { AccountService, NgReact, SpelText, StageConfigField, StageConstants, TextInput } from '@spinnaker/core';
6
+ import { AccountService, SpelText, StageConfigField, StageConstants, TargetSelect, TextInput } from '@spinnaker/core';
7
7
  import { AccountRegionClusterSelector } from '../../../presentation';
8
8
 
9
9
  export interface ICloudFoundryRunTaskStageConfigState {
@@ -49,7 +49,6 @@ export class CloudFoundryRunJobStageConfig extends React.Component<
49
49
  const { application, stage } = this.props;
50
50
  const { target, jobName, command, logsUrl } = stage;
51
51
  const { accounts } = this.state;
52
- const { TargetSelect } = NgReact;
53
52
 
54
53
  return (
55
54
  <div className="cloudfoundry-resize-asg-stage form-horizontal">
@@ -11,7 +11,6 @@ Registry.pipeline.registerStage({
11
11
  key: 'unshareService',
12
12
  cloudProvider: 'cloudfoundry',
13
13
  component: CloudFoundryUnshareServiceStageConfig,
14
- controller: 'cfUnshareServiceStageCtrl',
15
14
  executionDetailsSections: [CloudFoundryUnshareServiceExecutionDetails, ExecutionDetailsTasks],
16
15
  supportsCustomTimeout: true,
17
16
  validators: [
@@ -3,7 +3,7 @@ import { from as observableFrom, Subject } from 'rxjs';
3
3
  import { takeUntil } from 'rxjs/operators';
4
4
 
5
5
  import type { IAccount, IStageConfigProps } from '@spinnaker/core';
6
- import { AccountService, NgReact, StageConfigField, StageConstants } from '@spinnaker/core';
6
+ import { AccountService, StageConfigField, StageConstants, TargetSelect } from '@spinnaker/core';
7
7
 
8
8
  import { AccountRegionClusterSelector } from '../../widgets/accountRegionClusterSelector';
9
9
 
@@ -46,7 +46,6 @@ export class CloudFoundryAsgStageConfig extends React.Component<IStageConfigProp
46
46
  const { application, pipeline, stage } = this.props;
47
47
  const { target } = stage;
48
48
  const { accounts } = this.state;
49
- const { TargetSelect } = NgReact;
50
49
  return (
51
50
  <div className="form-horizontal">
52
51
  {!pipeline.strategy && (
@@ -4,7 +4,7 @@ import { from as observableFrom, Subject } from 'rxjs';
4
4
  import { takeUntil } from 'rxjs/operators';
5
5
 
6
6
  import type { Application, IAccount, IPipeline, IStageConfigProps } from '@spinnaker/core';
7
- import { AccountService, NgReact, SpinFormik, StageConfigField, StageConstants } from '@spinnaker/core';
7
+ import { AccountService, SpinFormik, StageConfigField, StageConstants, TargetSelect } from '@spinnaker/core';
8
8
 
9
9
  import { Routes } from '../../forms/serverGroup';
10
10
  import { AccountRegionClusterSelector } from '../../widgets/accountRegionClusterSelector';
@@ -76,7 +76,6 @@ export class CloudFoundryLoadBalancersStageConfig extends React.Component<
76
76
  const { stage } = this.props;
77
77
  const { accounts, application, initialValues, pipeline } = this.state;
78
78
  const { target } = stage;
79
- const { TargetSelect } = NgReact;
80
79
  return (
81
80
  <div className="form-horizontal">
82
81
  {!pipeline.strategy && (
@@ -1,17 +1,15 @@
1
- import type { IScope } from 'angular';
2
- import { mock, noop } from 'angular';
1
+ import type { ReactWrapper, ShallowWrapper } from 'enzyme';
3
2
  import { mount, shallow } from 'enzyme';
4
3
  import React from 'react';
5
4
 
6
- import type { Application, ApplicationDataSource, IMoniker, IServerGroup } from '@spinnaker/core';
7
- import { ApplicationModelBuilder, REACT_MODULE } from '@spinnaker/core';
5
+ import type { Application, IMoniker, IServerGroup } from '@spinnaker/core';
8
6
 
9
7
  import type { IAccountRegionClusterSelectorProps } from './AccountRegionClusterSelector';
10
8
  import { AccountRegionClusterSelector } from './AccountRegionClusterSelector';
11
9
 
12
10
  describe('<AccountRegionClusterSelector />', () => {
13
- let $scope: IScope;
14
11
  let application: Application;
12
+ const noop = () => {};
15
13
 
16
14
  function createServerGroup(account: string, cluster: string, name: string, region: string): IServerGroup {
17
15
  return {
@@ -26,27 +24,27 @@ describe('<AccountRegionClusterSelector />', () => {
26
24
  } as IServerGroup;
27
25
  }
28
26
 
29
- beforeEach(mock.module(REACT_MODULE));
30
- beforeEach(
31
- mock.inject(($rootScope: IScope) => {
32
- $scope = $rootScope.$new();
33
- application = ApplicationModelBuilder.createApplicationForTests('app', {
34
- key: 'serverGroups',
35
- loaded: true,
36
- data: [
37
- createServerGroup('account-name-one', 'app-stack-detailOne', 'app', 'region-one'),
38
- createServerGroup('account-name-two', 'app-stack-detailTwo', 'app', 'region-two'),
39
- createServerGroup('account-name-one', 'app-stack-detailOne', 'app', 'region-three'),
40
- createServerGroup('account-name-one', 'app-stack-detailThree', 'app', 'region-one'),
41
- createServerGroup('account-name-one', 'app-stack-detailFour', 'app', 'region-three'),
42
- createServerGroup('account-name-one', 'app-stack-detailFive', 'app', 'region-two'),
43
- ],
44
- defaultData: [] as IServerGroup[],
45
- } as ApplicationDataSource<IServerGroup[]>);
46
- }),
47
- );
48
-
49
- it('initializes properly with provided component', () => {
27
+ async function flushComponent(component: ReactWrapper | ShallowWrapper): Promise<void> {
28
+ await new Promise((resolve) => setTimeout(resolve, 0));
29
+ component.update();
30
+ }
31
+
32
+ beforeEach(() => {
33
+ const serverGroups = [
34
+ createServerGroup('account-name-one', 'app-stack-detailOne', 'app', 'region-one'),
35
+ createServerGroup('account-name-two', 'app-stack-detailTwo', 'app', 'region-two'),
36
+ createServerGroup('account-name-one', 'app-stack-detailOne', 'app', 'region-three'),
37
+ createServerGroup('account-name-one', 'app-stack-detailThree', 'app', 'region-one'),
38
+ createServerGroup('account-name-one', 'app-stack-detailFour', 'app', 'region-three'),
39
+ createServerGroup('account-name-one', 'app-stack-detailFive', 'app', 'region-two'),
40
+ ];
41
+ application = {
42
+ ready: () => Promise.resolve(),
43
+ getDataSource: () => ({ data: serverGroups }),
44
+ } as Application;
45
+ });
46
+
47
+ it('initializes properly with provided component', async () => {
50
48
  const accountRegionClusterProps: IAccountRegionClusterSelectorProps = {
51
49
  accounts: [
52
50
  {
@@ -68,7 +66,7 @@ describe('<AccountRegionClusterSelector />', () => {
68
66
  const component = shallow<AccountRegionClusterSelector>(
69
67
  <AccountRegionClusterSelector {...accountRegionClusterProps} />,
70
68
  );
71
- $scope.$digest();
69
+ await flushComponent(component);
72
70
 
73
71
  expect(component.state().availableRegions.length).toBe(3, 'number of available regions does not match');
74
72
  expect(component.state().availableRegions).toContain('region-one');
@@ -81,7 +79,7 @@ describe('<AccountRegionClusterSelector />', () => {
81
79
  expect(component.state().componentName).toBe('');
82
80
  });
83
81
 
84
- it('retrieves the correct list of regions when account is changed', () => {
82
+ it('retrieves the correct list of regions when account is changed', async () => {
85
83
  let credentials = '';
86
84
  let region = 'SHOULD-CHANGE';
87
85
  let regions = ['SHOULD-CHANGE'];
@@ -118,7 +116,7 @@ describe('<AccountRegionClusterSelector />', () => {
118
116
  const component = mount<AccountRegionClusterSelector>(
119
117
  <AccountRegionClusterSelector {...accountRegionClusterProps} />,
120
118
  );
121
- $scope.$digest();
119
+ await flushComponent(component);
122
120
 
123
121
  expect(component.state().availableRegions.length).toBe(3, 'number of available regions does not match');
124
122
  expect(component.state().availableRegions).toContain('region-one');
@@ -132,7 +130,7 @@ describe('<AccountRegionClusterSelector />', () => {
132
130
  accountSelectComponent.simulate('mouseDown');
133
131
  accountSelectComponent.simulate('change', { target: { value: 'account-name-two' } });
134
132
  accountSelectComponent.simulate('keyDown', { keyCode: 9, key: 'Tab' });
135
- $scope.$digest();
133
+ await flushComponent(component);
136
134
 
137
135
  expect(component.state().availableRegions.length).toBe(1, 'number of available regions does not match');
138
136
  expect(component.state().availableRegions).toContain('region-two');
@@ -143,7 +141,7 @@ describe('<AccountRegionClusterSelector />', () => {
143
141
  expect(cluster).toBeUndefined('selected cluster is not cleared');
144
142
  });
145
143
 
146
- it('retrieves the correct list of clusters when the selector is multi-region and the region is changed', () => {
144
+ it('retrieves the correct list of clusters when the selector is multi-region and the region is changed', async () => {
147
145
  let regions: string[] = [];
148
146
  let cluster = 'SHOULD-CHANGE';
149
147
  const accountRegionClusterProps: IAccountRegionClusterSelectorProps = {
@@ -177,7 +175,7 @@ describe('<AccountRegionClusterSelector />', () => {
177
175
  const component = mount<AccountRegionClusterSelector>(
178
176
  <AccountRegionClusterSelector {...accountRegionClusterProps} />,
179
177
  );
180
- $scope.$digest();
178
+ await flushComponent(component);
181
179
 
182
180
  expect(component.state().availableRegions.length).toBe(3, 'number of available regions does not match');
183
181
  expect(component.state().availableRegions).toContain('region-one');
@@ -191,7 +189,7 @@ describe('<AccountRegionClusterSelector />', () => {
191
189
  accountSelectComponent.simulate('mouseDown');
192
190
  accountSelectComponent.simulate('change', { target: { value: 'region-three' } });
193
191
  accountSelectComponent.simulate('keyDown', { keyCode: 9, key: 'Tab' });
194
- $scope.$digest();
192
+ await flushComponent(component);
195
193
 
196
194
  expect(component.state().clusters.length).toBe(3, 'number of clusters does not match');
197
195
  expect(component.state().clusters).toContain('app-stack-detailOne');
@@ -203,7 +201,7 @@ describe('<AccountRegionClusterSelector />', () => {
203
201
  expect(regions).toContain('region-three');
204
202
  });
205
203
 
206
- it('retrieves the correct list of clusters on startup and the selector is single-region', () => {
204
+ it('retrieves the correct list of clusters on startup and the selector is single-region', async () => {
207
205
  const accountRegionClusterProps: IAccountRegionClusterSelectorProps = {
208
206
  accounts: [
209
207
  {
@@ -233,7 +231,7 @@ describe('<AccountRegionClusterSelector />', () => {
233
231
  const component = mount<AccountRegionClusterSelector>(
234
232
  <AccountRegionClusterSelector {...accountRegionClusterProps} />,
235
233
  );
236
- $scope.$digest();
234
+ await flushComponent(component);
237
235
 
238
236
  expect(component.state().availableRegions.length).toBe(3, 'number of available regions does not match');
239
237
  expect(component.state().availableRegions).toContain('region-one');
@@ -244,7 +242,7 @@ describe('<AccountRegionClusterSelector />', () => {
244
242
  expect(component.state().clusters).toContain('app-stack-detailThree');
245
243
  });
246
244
 
247
- it('the cluster value is updated in the component when cluster is changed', () => {
245
+ it('the cluster value is updated in the component when cluster is changed', async () => {
248
246
  let cluster = '';
249
247
  let moniker: IMoniker = { app: '' };
250
248
  const accountRegionClusterProps: IAccountRegionClusterSelectorProps = {
@@ -287,7 +285,7 @@ describe('<AccountRegionClusterSelector />', () => {
287
285
  const component = mount<AccountRegionClusterSelector>(
288
286
  <AccountRegionClusterSelector {...accountRegionClusterProps} />,
289
287
  );
290
- $scope.$digest();
288
+ await flushComponent(component);
291
289
 
292
290
  expect(component.state().availableRegions.length).toBe(3, 'number of available regions does not match');
293
291
  expect(component.state().availableRegions).toContain('region-one');
@@ -301,13 +299,13 @@ describe('<AccountRegionClusterSelector />', () => {
301
299
  clusterSelectComponent.simulate('mouseDown');
302
300
  clusterSelectComponent.simulate('change', { target: { value: 'app-stack-detailThree' } });
303
301
  clusterSelectComponent.simulate('keyDown', { keyCode: 9, key: 'Tab' });
304
- $scope.$digest();
302
+ await flushComponent(component);
305
303
 
306
304
  expect(cluster).toBe('app-stack-detailThree');
307
305
  expect(moniker).toEqual(expectedMoniker);
308
306
  });
309
307
 
310
- it('the cluster value is updated in the component when cluster is changed to freeform value', () => {
308
+ it('the cluster value is updated in the component when cluster is changed to freeform value', async () => {
311
309
  let cluster = '';
312
310
  let moniker: IMoniker = { app: '' };
313
311
  const accountRegionClusterProps: IAccountRegionClusterSelectorProps = {
@@ -342,20 +340,20 @@ describe('<AccountRegionClusterSelector />', () => {
342
340
  const component = mount<AccountRegionClusterSelector>(
343
341
  <AccountRegionClusterSelector {...accountRegionClusterProps} />,
344
342
  );
345
- $scope.$digest();
343
+ await flushComponent(component);
346
344
 
347
345
  const clusterSelectComponent = component.find('Select[name="newCluster"] .Select-control input');
348
346
  clusterSelectComponent.simulate('mouseDown');
349
347
  clusterSelectComponent.simulate('change', { target: { value: 'app-stack-freeform' } });
350
348
  clusterSelectComponent.simulate('keyDown', { keyCode: 9, key: 'Tab' });
351
- $scope.$digest();
349
+ await flushComponent(component);
352
350
 
353
351
  expect(cluster).toBe('app-stack-freeform');
354
352
  expect(moniker).toBeUndefined();
355
353
  expect(component.state().clusters).toContain('app-stack-freeform');
356
354
  });
357
355
 
358
- it('initialize with form names', () => {
356
+ it('initialize with form names', async () => {
359
357
  const accountRegionClusterProps: IAccountRegionClusterSelectorProps = {
360
358
  accounts: [
361
359
  {
@@ -376,7 +374,7 @@ describe('<AccountRegionClusterSelector />', () => {
376
374
  };
377
375
 
378
376
  const component = shallow(<AccountRegionClusterSelector {...accountRegionClusterProps} />);
379
- $scope.$digest();
377
+ await flushComponent(component);
380
378
 
381
379
  expect(component.find('Select[name="form.credentials"]').length).toBe(1, 'select for account not found');
382
380
  expect(component.find('Select[name="form.regions"]').length).toBe(1, 'select for regions not found');
@@ -0,0 +1,76 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+
4
+ import { ConfirmationModalService, ServerGroupWarningMessageService } from '@spinnaker/core';
5
+
6
+ import { CloudFoundryInstanceActionsComponent } from './instance/details/CloudFoundryInstanceActions';
7
+ import { CloudFoundryLoadBalancerActionsComponent } from './loadBalancer/details/CloudFoundryLoadBalancerActions';
8
+ import { CloudFoundryServerGroupActionsComponent } from './serverGroup/details/cloudFoundryServerGroupActions';
9
+
10
+ describe('Cloud Foundry routed actions', () => {
11
+ const routerProps = (includes: jasmine.Spy, go: jasmine.Spy) =>
12
+ ({ router: {}, stateParams: {}, stateService: { go, includes } } as any);
13
+
14
+ it('closes instance details through the injected state service', () => {
15
+ const includes = jasmine.createSpy('includes').and.returnValue(true);
16
+ const go = jasmine.createSpy('go');
17
+ const confirm = spyOn(ConfirmationModalService, 'confirm');
18
+ const component = shallow(
19
+ <CloudFoundryInstanceActionsComponent
20
+ {...routerProps(includes, go)}
21
+ application={{} as any}
22
+ instance={{ account: 'test', name: 'instance-id' } as any}
23
+ />,
24
+ );
25
+
26
+ (component.instance() as any).terminateInstance();
27
+ confirm.calls.mostRecent().args[0].taskMonitorConfig.onTaskComplete();
28
+
29
+ expect(go).toHaveBeenCalledWith('^');
30
+ });
31
+
32
+ it('closes load balancer details through the injected state service', () => {
33
+ const includes = jasmine.createSpy('includes').and.returnValue(true);
34
+ const go = jasmine.createSpy('go');
35
+ const confirm = spyOn(ConfirmationModalService, 'confirm');
36
+ const component = shallow(
37
+ <CloudFoundryLoadBalancerActionsComponent
38
+ {...routerProps(includes, go)}
39
+ application={{} as any}
40
+ loadBalancer={{ account: 'test', name: 'route', region: 'test' } as any}
41
+ />,
42
+ );
43
+
44
+ (component.instance() as any).deleteLoadBalancer();
45
+ confirm.calls.mostRecent().args[0].taskMonitorConfig.onTaskComplete();
46
+
47
+ expect(go).toHaveBeenCalledWith('^');
48
+ });
49
+
50
+ it('closes server group details through the injected state service', () => {
51
+ const includes = jasmine.createSpy('includes').and.returnValue(true);
52
+ const go = jasmine.createSpy('go');
53
+ spyOn(ServerGroupWarningMessageService, 'addDestroyWarningMessage');
54
+ const confirm = spyOn(ConfirmationModalService, 'confirm');
55
+ const component = shallow(
56
+ <CloudFoundryServerGroupActionsComponent
57
+ {...routerProps(includes, go)}
58
+ app={{ attributes: {} } as any}
59
+ serverGroup={
60
+ {
61
+ account: 'test',
62
+ cloudProvider: 'cloudfoundry',
63
+ moniker: { cluster: 'app' },
64
+ name: 'app-v001',
65
+ region: 'test',
66
+ } as any
67
+ }
68
+ />,
69
+ );
70
+
71
+ (component.instance() as any).destroyServerGroup();
72
+ confirm.calls.mostRecent().args[0].taskMonitorConfig.onTaskComplete();
73
+
74
+ expect(go).toHaveBeenCalledWith('^');
75
+ });
76
+ });
@@ -1,5 +1,3 @@
1
- import type { IQService } from 'angular';
2
-
3
1
  import type { Application, IPipeline, IStage } from '@spinnaker/core';
4
2
  import type { ICloudFoundryApplication, ICloudFoundryServerGroup } from '../../domain';
5
3
 
@@ -10,22 +8,16 @@ import type {
10
8
  } from './serverGroupConfigurationModel.cf';
11
9
 
12
10
  export class CloudFoundryServerGroupCommandBuilderShim {
13
- public static $inject = ['$q'];
14
- constructor(private $q: IQService) {}
15
-
16
- public buildNewServerGroupCommand(
17
- app: Application,
18
- defaults: any,
19
- ): PromiseLike<ICloudFoundryCreateServerGroupCommand> {
20
- return this.$q.when(CloudFoundryServerGroupCommandBuilder.buildNewServerGroupCommand(app, defaults));
11
+ public buildNewServerGroupCommand(app: Application, defaults: any): Promise<ICloudFoundryCreateServerGroupCommand> {
12
+ return Promise.resolve(CloudFoundryServerGroupCommandBuilder.buildNewServerGroupCommand(app, defaults));
21
13
  }
22
14
 
23
15
  public buildServerGroupCommandFromExisting(
24
16
  app: Application,
25
17
  serverGroup: ICloudFoundryServerGroup,
26
18
  mode = 'clone',
27
- ): PromiseLike<ICloudFoundryCreateServerGroupCommand> {
28
- return this.$q.when(
19
+ ): Promise<ICloudFoundryCreateServerGroupCommand> {
20
+ return Promise.resolve(
29
21
  CloudFoundryServerGroupCommandBuilder.buildServerGroupCommandFromExisting(app, serverGroup, mode),
30
22
  );
31
23
  }
@@ -33,8 +25,10 @@ export class CloudFoundryServerGroupCommandBuilderShim {
33
25
  public buildNewServerGroupCommandForPipeline(
34
26
  stage: IStage,
35
27
  pipeline: IPipeline,
36
- ): PromiseLike<ICloudFoundryCreateServerGroupCommand> {
37
- return this.$q.when(CloudFoundryServerGroupCommandBuilder.buildNewServerGroupCommandForPipeline(stage, pipeline));
28
+ ): Promise<ICloudFoundryCreateServerGroupCommand> {
29
+ return Promise.resolve(
30
+ CloudFoundryServerGroupCommandBuilder.buildNewServerGroupCommandForPipeline(stage, pipeline),
31
+ );
38
32
  }
39
33
 
40
34
  public buildServerGroupCommandFromPipeline(
@@ -42,8 +36,8 @@ export class CloudFoundryServerGroupCommandBuilderShim {
42
36
  originalCluster: ICloudFoundryDeployConfiguration,
43
37
  stage: IStage,
44
38
  pipeline: IPipeline,
45
- ): PromiseLike<ICloudFoundryCreateServerGroupCommand> {
46
- return this.$q.when(
39
+ ): Promise<ICloudFoundryCreateServerGroupCommand> {
40
+ return Promise.resolve(
47
41
  CloudFoundryServerGroupCommandBuilder.buildServerGroupCommandFromPipeline(
48
42
  application,
49
43
  originalCluster,
@@ -56,8 +50,8 @@ export class CloudFoundryServerGroupCommandBuilderShim {
56
50
  public buildCloneServerGroupCommandFromPipeline(
57
51
  stage: IStage,
58
52
  pipeline: IPipeline,
59
- ): PromiseLike<ICloudFoundryCreateServerGroupCommand> {
60
- return this.$q.when(
53
+ ): Promise<ICloudFoundryCreateServerGroupCommand> {
54
+ return Promise.resolve(
61
55
  CloudFoundryServerGroupCommandBuilder.buildCloneServerGroupCommandFromPipeline(stage, pipeline),
62
56
  );
63
57
  }
@@ -1,8 +1,8 @@
1
1
  import { get } from 'lodash';
2
2
  import React from 'react';
3
3
 
4
- import type { Application, IModalComponentProps, IPipeline, IStage } from '@spinnaker/core';
5
- import { noop, ReactInjector, ReactModal, TaskMonitor, WizardModal, WizardPage } from '@spinnaker/core';
4
+ import type { Application, DeckRuntimeServices, IModalComponentProps, IPipeline, IStage } from '@spinnaker/core';
5
+ import { DeckRuntimeContext, noop, ReactModal, TaskMonitor, WizardModal, WizardPage } from '@spinnaker/core';
6
6
 
7
7
  import { ServerGroupTemplateSelection } from './ServerGroupTemplateSelection';
8
8
  import type { ICloudFoundryServerGroup } from '../../../domain';
@@ -36,14 +36,20 @@ export class CloudFoundryCreateServerGroupModal extends React.Component<
36
36
  ICloudFoundryCreateServerGroupProps,
37
37
  ICloudFoundryCreateServerGroupState
38
38
  > {
39
+ public static contextType = DeckRuntimeContext;
40
+ public declare context: React.ContextType<typeof DeckRuntimeContext>;
41
+
39
42
  public static defaultProps: Partial<ICloudFoundryCreateServerGroupProps> = {
40
43
  closeModal: noop,
41
44
  dismissModal: noop,
42
45
  };
43
46
 
44
- public static show(props: ICloudFoundryCreateServerGroupProps): Promise<ICloudFoundryCreateServerGroupCommand> {
47
+ public static show(
48
+ props: ICloudFoundryCreateServerGroupProps,
49
+ runtimeServices: DeckRuntimeServices,
50
+ ): Promise<ICloudFoundryCreateServerGroupCommand> {
45
51
  const modalProps = { dialogClassName: 'wizard-modal modal-lg' };
46
- return ReactModal.show(CloudFoundryCreateServerGroupModal, props, modalProps);
52
+ return ReactModal.show(CloudFoundryCreateServerGroupModal, props, modalProps, runtimeServices);
47
53
  }
48
54
 
49
55
  constructor(props: ICloudFoundryCreateServerGroupProps) {
@@ -60,7 +66,7 @@ export class CloudFoundryCreateServerGroupModal extends React.Component<
60
66
  taskMonitor: new TaskMonitor({
61
67
  application: props.application,
62
68
  title: 'Creating your server group',
63
- modalInstance: TaskMonitor.modalInstanceEmulation(() => this.props.dismissModal()),
69
+ onDismiss: () => this.props.dismissModal(),
64
70
  onTaskComplete: this.onTaskComplete,
65
71
  }),
66
72
  };
@@ -89,11 +95,11 @@ export class CloudFoundryCreateServerGroupModal extends React.Component<
89
95
  this.props.closeModal && this.props.closeModal(command);
90
96
  } else if (command.viewState.mode === 'clone') {
91
97
  this.state.taskMonitor.submit(() =>
92
- ReactInjector.serverGroupWriter.cloneServerGroup(command, this.props.application),
98
+ this.context.services.serverGroupWriter.cloneServerGroup(command, this.props.application),
93
99
  );
94
100
  } else {
95
101
  this.state.taskMonitor.submit(() =>
96
- ReactInjector.serverGroupWriter.cloneServerGroup(command, this.props.application),
102
+ this.context.services.serverGroupWriter.cloneServerGroup(command, this.props.application),
97
103
  );
98
104
  }
99
105
  };