@spinnaker/core 0.24.1 → 0.25.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 (40) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/config/settings.d.ts +1 -0
  3. package/dist/domain/IArtifact.d.ts +2 -0
  4. package/dist/domain/ITrigger.d.ts +5 -0
  5. package/dist/index.js +36 -36
  6. package/dist/index.js.map +1 -1
  7. package/dist/manifest/ManifestYaml.d.ts +10 -4
  8. package/dist/pipeline/config/stages/bakeManifest/ManifestRenderers.d.ts +2 -0
  9. package/dist/pipeline/config/stages/bakeManifest/helmfile/BakeHelmfileConfigForm.d.ts +18 -0
  10. package/dist/pipeline/config/stages/bakeManifest/utils/getBakedArtifacts.d.ts +4 -0
  11. package/dist/pipeline/config/stages/bakeManifest/utils/getContentReference.d.ts +1 -0
  12. package/dist/pipeline/config/triggers/artifacts/ArtifactService.d.ts +3 -0
  13. package/dist/pipeline/config/triggers/cdevents/CDEventsTrigger.d.ts +7 -0
  14. package/dist/pipeline/config/triggers/cdevents/cdevents.trigger.d.ts +1 -0
  15. package/dist/pipeline/config/triggers/index.d.ts +1 -0
  16. package/dist/pipeline/index.d.ts +2 -0
  17. package/dist/presentation/forms/inputs/NumberConcurrencyInput.d.ts +7 -0
  18. package/package.json +2 -2
  19. package/src/artifact/ArtifactIconService.ts +1 -0
  20. package/src/artifact/ArtifactTypes.ts +1 -0
  21. package/src/config/settings.ts +1 -0
  22. package/src/domain/IArtifact.ts +3 -0
  23. package/src/domain/ITrigger.ts +4 -0
  24. package/src/help/help.contents.ts +10 -0
  25. package/src/manifest/ManifestYaml.tsx +29 -7
  26. package/src/pipeline/config/stages/bakeManifest/BakeManifestConfig.tsx +4 -1
  27. package/src/pipeline/config/stages/bakeManifest/BakeManifestDetailsTab.tsx +24 -12
  28. package/src/pipeline/config/stages/bakeManifest/BakeManifestStageForm.tsx +9 -2
  29. package/src/pipeline/config/stages/bakeManifest/ManifestRenderers.ts +2 -0
  30. package/src/pipeline/config/stages/bakeManifest/helmfile/BakeHelmfileConfigForm.spec.tsx +132 -0
  31. package/src/pipeline/config/stages/bakeManifest/helmfile/BakeHelmfileConfigForm.tsx +271 -0
  32. package/src/pipeline/config/stages/bakeManifest/utils/getBakedArtifacts.ts +13 -0
  33. package/src/pipeline/config/stages/bakeManifest/utils/getContentReference.ts +3 -0
  34. package/src/pipeline/config/triggers/artifacts/ArtifactService.ts +4 -0
  35. package/src/pipeline/config/triggers/cdevents/CDEventsTrigger.tsx +48 -0
  36. package/src/pipeline/config/triggers/cdevents/cdevents.trigger.ts +19 -0
  37. package/src/pipeline/config/triggers/index.ts +1 -0
  38. package/src/pipeline/create/CreatePipelineModal.tsx +1 -1
  39. package/src/pipeline/index.ts +2 -0
  40. package/src/presentation/forms/inputs/NumberConcurrencyInput.tsx +29 -0
@@ -1,7 +1,13 @@
1
1
  /// <reference types="react" />
2
- export interface IManifestYamlProps {
2
+ declare type IManifestYamlProps = {
3
3
  linkName: string;
4
- manifestText: string;
5
4
  modalTitle: string;
6
- }
7
- export declare function ManifestYaml(props: IManifestYamlProps): JSX.Element;
5
+ } & ({
6
+ manifestText: string;
7
+ manifestUri?: never;
8
+ } | {
9
+ manifestText?: never;
10
+ manifestUri: string;
11
+ });
12
+ export declare function ManifestYaml({ linkName, modalTitle, manifestText, manifestUri }: IManifestYamlProps): JSX.Element;
13
+ export {};
@@ -1,8 +1,10 @@
1
1
  export declare enum ManifestRenderers {
2
2
  HELM2 = "HELM2",
3
3
  HELM3 = "HELM3",
4
+ HELMFILE = "HELMFILE",
4
5
  KUSTOMIZE = "KUSTOMIZE",
5
6
  KUSTOMIZE4 = "KUSTOMIZE4"
6
7
  }
7
8
  export declare const HELM_RENDERERS: Readonly<ManifestRenderers[]>;
9
+ export declare const HELMFILE_RENDERER: Readonly<ManifestRenderers>;
8
10
  export declare const KUSTOMIZE_RENDERERS: Readonly<ManifestRenderers[]>;
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { IFormikStageConfigInjectedProps } from '../../FormikStageConfig';
3
+ export interface IBakeHelmfileConfigFormState {
4
+ gitRepoArtifactAccountNames: string[];
5
+ }
6
+ export declare class BakeHelmfileConfigForm extends React.Component<IFormikStageConfigInjectedProps, IBakeHelmfileConfigFormState> {
7
+ constructor(props: IFormikStageConfigInjectedProps);
8
+ private static readonly excludedArtifactTypes;
9
+ componentDidMount(): void;
10
+ private onTemplateArtifactEdited;
11
+ private onTemplateArtifactSelected;
12
+ private addInputArtifact;
13
+ private removeInputArtifact;
14
+ private getInputArtifact;
15
+ private outputNameChange;
16
+ private overrideChanged;
17
+ render(): JSX.Element;
18
+ }
@@ -0,0 +1,4 @@
1
+ import type { IArtifact, IExecutionContext } from '../../../../../domain';
2
+ export declare const getBakedArtifacts: (context: IExecutionContext) => Array<IArtifact & {
3
+ reference: string;
4
+ }>;
@@ -0,0 +1 @@
1
+ export declare const getContentReference: (uri: string) => string;
@@ -1,4 +1,7 @@
1
1
  export declare class ArtifactService {
2
2
  static getArtifactNames(type: string, accountName: string): PromiseLike<string[]>;
3
3
  static getArtifactVersions(type: string, accountName: string, artifactName: string): PromiseLike<string[]>;
4
+ static getArtifactByContentReference(contentRef: string): PromiseLike<{
5
+ reference: string;
6
+ }>;
4
7
  }
@@ -0,0 +1,7 @@
1
+ /// <reference types="react" />
2
+ import type { FormikProps } from 'formik';
3
+ import type { ICDEventsTrigger } from '../../../../domain';
4
+ export interface ICDEventsTriggerProps {
5
+ formik: FormikProps<ICDEventsTrigger>;
6
+ }
7
+ export declare function CDEventsTrigger(cdeventsTriggerProps: ICDEventsTriggerProps): JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import './artifactory/artifactory.trigger';
2
+ import './cdevents/cdevents.trigger';
2
3
  import './concourse/concourse.trigger';
3
4
  import './cron/cron.trigger';
4
5
  import './git/git.trigger';
@@ -23,3 +23,5 @@ export * from './manualExecution/TriggerTemplate';
23
23
  export * from './service/ExecutionsTransformer';
24
24
  export * from './service/execution.service';
25
25
  export * from './status/ArtifactList';
26
+ export * from './config/stages/bakeManifest/utils/getBakedArtifacts';
27
+ export * from './config/stages/bakeManifest/utils/getContentReference';
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { IFormInputProps, OmitControlledInputPropsFrom } from './interface';
3
+ interface INumberInputProps extends IFormInputProps, OmitControlledInputPropsFrom<React.InputHTMLAttributes<any>> {
4
+ inputClassName?: string;
5
+ }
6
+ export declare function NumberConcurrencyInput(props: INumberInputProps): JSX.Element;
7
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spinnaker/core",
3
3
  "license": "Apache-2.0",
4
- "version": "0.24.1",
4
+ "version": "0.25.0",
5
5
  "module": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "scripts": {
@@ -120,5 +120,5 @@
120
120
  "shx": "0.3.3",
121
121
  "typescript": "4.3.5"
122
122
  },
123
- "gitHead": "eb226ee64d23c8c34be5ace816809a2262917e3f"
123
+ "gitHead": "8de1e3c576a24ce9a62290f86bc52772df4842d2"
124
124
  }
@@ -44,6 +44,7 @@ ArtifactIconService.registerType(ArtifactTypePatterns.CUSTOM_OBJECT, unknownArti
44
44
  ArtifactIconService.registerType(ArtifactTypePatterns.DOCKER_IMAGE, dockerIcon);
45
45
  ArtifactIconService.registerType(ArtifactTypePatterns.KUBERNETES, kubernetesIcon);
46
46
  ArtifactIconService.registerType(ArtifactTypePatterns.EMBEDDED_BASE64, embeddedBase64Icon);
47
+ ArtifactIconService.registerType(ArtifactTypePatterns.REMOTE_BASE64, embeddedBase64Icon);
47
48
  ArtifactIconService.registerType(ArtifactTypePatterns.GCS_OBJECT, gcsObjectIcon);
48
49
  ArtifactIconService.registerType(ArtifactTypePatterns.GITHUB_FILE, gitHubFileIcon);
49
50
  ArtifactIconService.registerType(ArtifactTypePatterns.GIT_REPO, gitRepoIcon);
@@ -7,6 +7,7 @@ export const ArtifactTypePatterns: IArtifactTypePatterns = {
7
7
  CUSTOM_OBJECT: /custom\/object/,
8
8
  DOCKER_IMAGE: /docker\/image/,
9
9
  EMBEDDED_BASE64: /embedded\/base64/,
10
+ REMOTE_BASE64: /remote\/base64/,
10
11
  GCS_OBJECT: /gcs\/object/,
11
12
  GITHUB_FILE: /github\/file/,
12
13
  GIT_REPO: /git\/repo/,
@@ -34,6 +34,7 @@ export interface IFeatures {
34
34
  executionMarkerInformationModal?: boolean;
35
35
  fiatEnabled?: boolean;
36
36
  iapRefresherEnabled?: boolean;
37
+ lambdaAdditionalStages?: boolean;
37
38
  managedDelivery?: boolean;
38
39
  mdGitIntegration?: boolean;
39
40
  managedServiceAccounts?: boolean;
@@ -12,3 +12,6 @@ export interface IArtifact {
12
12
  kind?: string; // TODO delete
13
13
  customKind?: boolean; // TODO delete
14
14
  }
15
+
16
+ export const ARTIFACT_TYPE_EMBEDDED = 'embedded/base64';
17
+ export const ARTIFACT_TYPE_REMOTE = 'remote/base64';
@@ -91,6 +91,10 @@ export interface IWebhookTrigger extends ITrigger {
91
91
  payloadConstraints: { [key: string]: string };
92
92
  }
93
93
 
94
+ export interface ICDEventsTrigger extends IWebhookTrigger {
95
+ attributeConstraints: { [key: string]: string };
96
+ }
97
+
94
98
  export interface IWerckerTrigger extends IBuildTrigger {
95
99
  app: string;
96
100
  pipeline: string;
@@ -178,6 +178,11 @@ const helpContents: { [key: string]: string } = {
178
178
  <p>When provided, only a webhook with a payload containing at least the specified key/value pairs will be allowed to trigger this pipeline. For example, if you wanted to lock down the systems/users that can trigger this pipeline via this webhook, you could require the key "secret" and value "something-secret" as a constraint.</p>
179
179
  <p>The constraint values may be supplied as regex.</p>
180
180
  `,
181
+ 'pipeline.config.trigger.cdevents.attributeConstraints': `
182
+ <p>CDEvents messages will have mandatory headers id, source, specversion, type accompanying the payload called <b>attributes</b>.</p>
183
+ <p>When provided, only a CDEvents message with attributes containing at least the specified key/value pairs will be allowed to trigger this pipeline.</p>
184
+ <p>The constraint value is a java regex string.</p>
185
+ `,
181
186
  'pipeline.config.trigger.pubsub.attributeConstraints': `
182
187
  <p>Pubsub messages can have system-specific metadata accompanying the payload called <b>attributes</b>.</p>
183
188
  <p>When provided, only a pubsub message with attributes containing at least the specified key/value pairs will be allowed to trigger this pipeline.</p>
@@ -307,6 +312,11 @@ const helpContents: { [key: string]: string } = {
307
312
  'pipeline.config.bake.manifest.kustomize.filePath': `
308
313
  <p>This is the relative path to the kustomization.yaml file within your Git repo.</p>
309
314
  <p>e.g.: <b>examples/wordpress/mysql/kustomization.yaml</b></p>`,
315
+ 'pipeline.config.bake.manifest.helmfile.filePath': `
316
+ <p>This is the relative path to the directory containing the helmfile.yaml file within your Git repo.</p>
317
+ <p>e.g.: <b>chart/helmfile.yml</b></p>`,
318
+ 'pipeline.config.bake.manifest.helmfile.name':
319
+ '<p> Name is used to set the expected artifact in the Produces Artifact section. </p>',
310
320
  'pipeline.config.bake.cf.manifest.name':
311
321
  '<p> Name should be the same as the expected artifact in the Produces Artifact section. </p>',
312
322
  'pipeline.config.bake.cf.manifest.templateArtifact': `
@@ -1,26 +1,48 @@
1
1
  import React from 'react';
2
2
  import { Modal } from 'react-bootstrap';
3
+ import { ArtifactService } from '../pipeline/config/triggers/artifacts/ArtifactService';
4
+ import { decodeUnicodeBase64 } from '../utils';
3
5
 
4
- export interface IManifestYamlProps {
6
+ type IManifestYamlProps = {
5
7
  linkName: string;
6
- manifestText: string;
7
8
  modalTitle: string;
8
- }
9
+ } & ({ manifestText: string; manifestUri?: never } | { manifestText?: never; manifestUri: string });
9
10
 
10
- export function ManifestYaml(props: IManifestYamlProps) {
11
+ export function ManifestYaml({ linkName, modalTitle, manifestText, manifestUri }: IManifestYamlProps) {
11
12
  const [modalVisible, setModalVisible] = React.useState<boolean>(false);
12
13
  const toggle = () => setModalVisible(!modalVisible);
14
+ const [fetchedManifestText, setFetchedManifestText] = React.useState<string>('Loading...');
15
+ const [error, setError] = React.useState<string | null>(null);
16
+
17
+ React.useEffect(() => {
18
+ if (manifestUri) {
19
+ ArtifactService.getArtifactByContentReference(manifestUri)
20
+ .then((manifest) => setFetchedManifestText(decodeUnicodeBase64(manifest.reference)))
21
+ .catch((e) => setError(`Error: ${typeof e !== 'string' ? e.data?.message ?? JSON.stringify(e) : e}`));
22
+ }
23
+ }, []);
24
+
13
25
  return (
14
26
  <>
15
27
  <a key="modal-link" onClick={toggle} className="clickable">
16
- {props.linkName}
28
+ {linkName}
17
29
  </a>
18
30
  <Modal key="modal" show={modalVisible} onHide={toggle}>
19
31
  <Modal.Header closeButton={true}>
20
- <h3>{props.modalTitle}</h3>
32
+ <h3>{modalTitle}</h3>
21
33
  </Modal.Header>
22
34
  <Modal.Body>
23
- <textarea readOnly={true} rows={15} className="code" value={props.manifestText} />
35
+ {error ? (
36
+ <div className="alert alert-warning">
37
+ <p>{error}</p>
38
+ </div>
39
+ ) : null}
40
+ <textarea
41
+ readOnly={true}
42
+ rows={15}
43
+ className="code"
44
+ value={manifestUri ? fetchedManifestText : manifestText}
45
+ />
24
46
  </Modal.Body>
25
47
  <Modal.Footer>
26
48
  <button className="btn btn-primary" onClick={toggle}>
@@ -4,7 +4,7 @@ import React from 'react';
4
4
 
5
5
  import { BakeManifestStageForm, validateProducedArtifacts } from './BakeManifestStageForm';
6
6
  import { FormikStageConfig } from '../FormikStageConfig';
7
- import { HELM_RENDERERS } from './ManifestRenderers';
7
+ import { HELM_RENDERERS, HELMFILE_RENDERER } from './ManifestRenderers';
8
8
  import type { IStageConfigProps } from '../common';
9
9
  import type { IExpectedArtifact, IStage } from '../../../../domain';
10
10
  import { FormValidator } from '../../../../presentation';
@@ -46,6 +46,9 @@ export function validateBakeManifestStage(stage: IStage): FormikErrors<IStage> {
46
46
  if (HELM_RENDERERS.includes(stage.templateRenderer)) {
47
47
  formValidator.field('outputName', 'Name').required();
48
48
  }
49
+ if (HELMFILE_RENDERER === stage.templateRenderer) {
50
+ formValidator.field('outputName', 'Name').required();
51
+ }
49
52
 
50
53
  return formValidator.validateForm();
51
54
  }
@@ -3,10 +3,12 @@ import React from 'react';
3
3
  import type { IExecutionDetailsSectionProps } from '../common';
4
4
  import { ExecutionDetailsSection } from '../common';
5
5
  import { StageFailureMessage } from '../../../details';
6
- import type { IArtifact } from '../../../../domain';
6
+ import { ARTIFACT_TYPE_EMBEDDED } from '../../../../domain';
7
7
  import { ManifestYaml } from '../../../../manifest';
8
8
  import { Overridable } from '../../../../overrideRegistry';
9
9
  import { decodeUnicodeBase64 } from '../../../../utils';
10
+ import { getBakedArtifacts } from './utils/getBakedArtifacts';
11
+ import { getContentReference } from './utils/getContentReference';
10
12
 
11
13
  @Overridable('bakeManifest.bakeManifestDetailsTab')
12
14
  export class BakeManifestDetailsTab extends React.Component<IExecutionDetailsSectionProps> {
@@ -14,20 +16,30 @@ export class BakeManifestDetailsTab extends React.Component<IExecutionDetailsSec
14
16
 
15
17
  public render() {
16
18
  const { current, name, stage } = this.props;
17
- const bakedArtifacts: IArtifact[] = (stage.context.artifacts || []).filter(
18
- (a: IArtifact) => a.type === 'embedded/base64',
19
- );
19
+ const bakedArtifacts = getBakedArtifacts(stage.context);
20
+
20
21
  return (
21
22
  <ExecutionDetailsSection name={name} current={current}>
22
23
  <StageFailureMessage stage={stage} message={stage.failureMessage} />
23
- {bakedArtifacts.map((artifact, i) => (
24
- <ManifestYaml
25
- key={i}
26
- linkName={bakedArtifacts.length > 1 ? `Baked Manifest ${i} YAML` : 'Baked Manifest YAML'}
27
- manifestText={decodeUnicodeBase64(artifact.reference)}
28
- modalTitle="Baked Manifest"
29
- />
30
- ))}
24
+ {bakedArtifacts.map((artifact, i) => {
25
+ const linkName = bakedArtifacts.length > 1 ? `Baked Manifest ${i} YAML` : 'Baked Manifest YAML';
26
+
27
+ return artifact.type === ARTIFACT_TYPE_EMBEDDED ? (
28
+ <ManifestYaml
29
+ key={i}
30
+ linkName={linkName}
31
+ modalTitle="Baked Manifest"
32
+ manifestText={decodeUnicodeBase64(artifact.reference)}
33
+ />
34
+ ) : (
35
+ <ManifestYaml
36
+ key={i}
37
+ linkName={linkName}
38
+ modalTitle="Baked Manifest"
39
+ manifestUri={getContentReference(artifact.reference)}
40
+ />
41
+ );
42
+ })}
31
43
  </ExecutionDetailsSection>
32
44
  );
33
45
  }
@@ -2,11 +2,12 @@ import { isNil } from 'lodash';
2
2
  import React from 'react';
3
3
 
4
4
  import type { IFormikStageConfigInjectedProps } from '../FormikStageConfig';
5
- import { HELM_RENDERERS, KUSTOMIZE_RENDERERS } from './ManifestRenderers';
5
+ import { HELM_RENDERERS, HELMFILE_RENDERER, KUSTOMIZE_RENDERERS } from './ManifestRenderers';
6
6
  import { ExpectedArtifactService } from '../../../../artifact';
7
7
  import { StageConfigField } from '../common';
8
8
  import type { IExpectedArtifact } from '../../../../domain';
9
9
  import { BakeHelmConfigForm } from './helm/BakeHelmConfigForm';
10
+ import { BakeHelmfileConfigForm } from './helmfile/BakeHelmfileConfigForm';
10
11
  import { BakeKustomizeConfigForm } from './kustomize/BakeKustomizeConfigForm';
11
12
  import { ReactSelectInput } from '../../../../presentation';
12
13
  import { BASE_64_ARTIFACT_ACCOUNT, BASE_64_ARTIFACT_TYPE } from '../../triggers/artifacts/base64/Base64ArtifactEditor';
@@ -32,10 +33,13 @@ export function BakeManifestStageForm({ application, formik, pipeline }: IFormik
32
33
  if (HELM_RENDERERS.includes(stage.templateRenderer) && !isNil(stage.inputArtifact)) {
33
34
  formik.setFieldValue('inputArtifact', null);
34
35
  }
36
+ if (HELMFILE_RENDERER === stage.templateRenderer && !isNil(stage.inputArtifact)) {
37
+ formik.setFieldValue('inputArtifact', null);
38
+ }
35
39
  }, [stage.templateRenderer]);
36
40
 
37
41
  const templateRenderers = React.useMemo(() => {
38
- return [...KUSTOMIZE_RENDERERS, ...HELM_RENDERERS];
42
+ return [...KUSTOMIZE_RENDERERS, ...HELM_RENDERERS, HELMFILE_RENDERER];
39
43
  }, []);
40
44
 
41
45
  return (
@@ -62,6 +66,9 @@ export function BakeManifestStageForm({ application, formik, pipeline }: IFormik
62
66
  {HELM_RENDERERS.includes(stage.templateRenderer) && (
63
67
  <BakeHelmConfigForm pipeline={pipeline} application={application} formik={formik} />
64
68
  )}
69
+ {HELMFILE_RENDERER === stage.templateRenderer && (
70
+ <BakeHelmfileConfigForm pipeline={pipeline} application={application} formik={formik} />
71
+ )}
65
72
  </div>
66
73
  </div>
67
74
  );
@@ -1,11 +1,13 @@
1
1
  export enum ManifestRenderers {
2
2
  HELM2 = 'HELM2',
3
3
  HELM3 = 'HELM3',
4
+ HELMFILE = 'HELMFILE',
4
5
  KUSTOMIZE = 'KUSTOMIZE',
5
6
  KUSTOMIZE4 = 'KUSTOMIZE4',
6
7
  }
7
8
 
8
9
  export const HELM_RENDERERS: Readonly<ManifestRenderers[]> = [ManifestRenderers.HELM2, ManifestRenderers.HELM3];
10
+ export const HELMFILE_RENDERER: Readonly<ManifestRenderers> = ManifestRenderers.HELMFILE;
9
11
  export const KUSTOMIZE_RENDERERS: Readonly<ManifestRenderers[]> = [
10
12
  ManifestRenderers.KUSTOMIZE,
11
13
  ManifestRenderers.KUSTOMIZE4,
@@ -0,0 +1,132 @@
1
+ import { mock } from 'angular';
2
+ import { mount } from 'enzyme';
3
+ import React from 'react';
4
+
5
+ import { StageConfigField } from '../../../..';
6
+ import { BakeHelmfileConfigForm } from './BakeHelmfileConfigForm';
7
+ import { AccountService } from '../../../../../account';
8
+ import { ApplicationModelBuilder } from '../../../../../application';
9
+ import { ExpectedArtifactService } from '../../../../../artifact';
10
+ import type { IExpectedArtifact, IStage } from '../../../../../domain';
11
+ import { SpinFormik } from '../../../../../presentation';
12
+ import { REACT_MODULE } from '../../../../../reactShims';
13
+
14
+ describe('<BakeHelmfileConfigForm />', () => {
15
+ beforeEach(mock.module(REACT_MODULE));
16
+ beforeEach(mock.inject());
17
+
18
+ const helmfileFilePathFieldName = 'Helmfile File Path';
19
+
20
+ const getProps = () => {
21
+ return {
22
+ application: ApplicationModelBuilder.createApplicationForTests('my-application'),
23
+ pipeline: {
24
+ application: 'my-application',
25
+ id: 'pipeline-id',
26
+ limitConcurrent: true,
27
+ keepWaitingPipelines: true,
28
+ name: 'My Pipeline',
29
+ parameterConfig: [],
30
+ stages: [],
31
+ triggers: [],
32
+ },
33
+ } as any;
34
+ };
35
+
36
+ beforeEach(() =>
37
+ spyOn(AccountService, 'getArtifactAccounts').and.returnValue(
38
+ Promise.resolve([
39
+ { name: 'gitrepo', types: ['something-else', 'git/repo'] },
40
+ { name: 'notgitrepo', types: ['something-else'] },
41
+ ]),
42
+ ),
43
+ );
44
+
45
+ it('renders the helmfile file path element when the template artifact is from an account that handles git/repo artifacts', async () => {
46
+ const stage = ({
47
+ inputArtifacts: [{ account: 'gitrepo' }],
48
+ } as unknown) as IStage;
49
+
50
+ const props = getProps();
51
+
52
+ const component = mount(
53
+ <SpinFormik
54
+ initialValues={stage}
55
+ onSubmit={() => null}
56
+ validate={() => null}
57
+ render={(formik) => <BakeHelmfileConfigForm {...props} formik={formik} />}
58
+ />,
59
+ );
60
+
61
+ await new Promise((resolve) => setTimeout(resolve)); // wait one js tick for promise to resolve
62
+ component.setProps({}); // force a re-render
63
+
64
+ expect(component.find(StageConfigField).findWhere((x) => x.text() === helmfileFilePathFieldName).length).toBe(1);
65
+ });
66
+
67
+ it('does not render the helmfile file path element when the template artifact is from an account that does not handle git/repo artifacts', async () => {
68
+ const stage = ({
69
+ inputArtifacts: [{ account: 'notgitrepo' }],
70
+ } as unknown) as IStage;
71
+
72
+ const props = getProps();
73
+
74
+ const component = mount(
75
+ <SpinFormik
76
+ initialValues={stage}
77
+ onSubmit={() => null}
78
+ validate={() => null}
79
+ render={(formik) => <BakeHelmfileConfigForm {...props} formik={formik} />}
80
+ />,
81
+ );
82
+
83
+ await new Promise((resolve) => setTimeout(resolve)); // wait one js tick for promise to resolve
84
+ component.setProps({}); // force a re-render
85
+
86
+ expect(component.find(StageConfigField).findWhere((x) => x.text() === helmfileFilePathFieldName).length).toBe(0);
87
+ });
88
+
89
+ it('render the helmfile file path if the id of the git artifact is given but the account value does not exist', async () => {
90
+ const expectedArtifactDisplayName = 'test-artifact';
91
+ const expectedArtifactId = 'test-artifact-id';
92
+ const expectedGitArtifact: IExpectedArtifact = {
93
+ defaultArtifact: {
94
+ customKind: true,
95
+ id: 'defaultArtifact-id',
96
+ },
97
+ displayName: expectedArtifactDisplayName,
98
+ id: expectedArtifactId,
99
+ matchArtifact: {
100
+ artifactAccount: 'gitrepo',
101
+ id: expectedArtifactId,
102
+ reference: 'git repo',
103
+ type: 'git/repo',
104
+ version: 'master',
105
+ },
106
+ useDefaultArtifact: false,
107
+ usePriorArtifact: false,
108
+ };
109
+ const stage = ({
110
+ inputArtifacts: [{ id: expectedArtifactId }],
111
+ } as unknown) as IStage;
112
+
113
+ spyOn(ExpectedArtifactService, 'getExpectedArtifactsAvailableToStage').and.returnValue([expectedGitArtifact]);
114
+
115
+ const props = getProps();
116
+
117
+ const component = mount(
118
+ <SpinFormik
119
+ initialValues={stage}
120
+ onSubmit={() => null}
121
+ validate={() => null}
122
+ render={(formik) => <BakeHelmfileConfigForm {...props} formik={formik} />}
123
+ />,
124
+ );
125
+
126
+ await new Promise((resolve) => setTimeout(resolve)); // wait one js tick for promise to resolve
127
+ component.setProps({}); // force a re-render
128
+
129
+ expect(component.find('.Select-value-label > span').text().includes(expectedArtifactDisplayName)).toBe(true);
130
+ expect(component.find(StageConfigField).findWhere((x) => x.text() === helmfileFilePathFieldName).length).toBe(1);
131
+ });
132
+ });