Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[steps] Add support for ${{ ... }} interpolations to function inputs #503

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions packages/steps/src/BuildStep.ts
Original file line number Diff line number Diff line change
@@ -319,7 +319,9 @@ export class BuildStep extends BuildStepOutputAccessor {
inputs:
this.inputs?.reduce(
(acc, input) => {
acc[input.id] = input.value;
acc[input.id] = input.getValue({
interpolationContext: this.getInterpolationContext(),
});
return acc;
},
{} as Record<string, unknown>
@@ -346,7 +348,7 @@ export class BuildStep extends BuildStepOutputAccessor {
);
}

private getInterpolationContext(): JobInterpolationContext {
public getInterpolationContext(): JobInterpolationContext {
const hasAnyPreviousStepFailed = this.ctx.global.hasAnyPreviousStepFailed;

return {
@@ -415,10 +417,14 @@ export class BuildStep extends BuildStepOutputAccessor {
}
const vars = inputs.reduce(
(acc, input) => {
const inputValue = input.getValue({
interpolationContext: this.getInterpolationContext(),
});

acc[input.id] =
typeof input.value === 'object'
? JSON.stringify(input.value)
: input.value?.toString() ?? '';
typeof inputValue === 'object'
? JSON.stringify(inputValue)
: inputValue?.toString() ?? '';
return acc;
},
{} as Record<string, string>
46 changes: 25 additions & 21 deletions packages/steps/src/BuildStepInput.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import assert from 'assert';

import { bunyan } from '@expo/logger';
import { JobInterpolationContext } from '@expo/eas-build-job';

import { BuildStepGlobalContext, SerializedBuildStepGlobalContext } from './BuildStepContext.js';
import { BuildStepRuntimeError } from './errors.js';
import {
BUILD_STEP_OR_BUILD_GLOBAL_CONTEXT_REFERENCE_REGEX,
interpolateWithOutputs,
} from './utils/template.js';
import { interpolateJobContext } from './interpolation.js';

export enum BuildStepInputValueTypeName {
STRING = 'string',
@@ -95,34 +97,44 @@ export class BuildStepInput<
this.allowedValueTypeName = allowedValueTypeName;
}

public get value(): R extends true
? BuildStepInputValueType<T>
: BuildStepInputValueType<T> | undefined {
public getValue({
interpolationContext,
}: {
interpolationContext: JobInterpolationContext;
}): R extends true ? BuildStepInputValueType<T> : BuildStepInputValueType<T> | undefined {
const rawValue = this._value ?? this.defaultValue;
if (this.required && rawValue === undefined) {
throw new BuildStepRuntimeError(
`Input parameter "${this.id}" for step "${this.stepDisplayName}" is required but it was not set.`
);
}

const interpolatedValue = interpolateJobContext({
target: rawValue,
context: interpolationContext,
});

const valueDoesNotRequireInterpolation =
rawValue === undefined ||
rawValue === null ||
typeof rawValue === 'boolean' ||
typeof rawValue === 'number';
interpolatedValue === undefined ||
interpolatedValue === null ||
typeof interpolatedValue === 'boolean' ||
typeof interpolatedValue === 'number';
let returnValue;
if (valueDoesNotRequireInterpolation) {
if (typeof rawValue !== this.allowedValueTypeName && rawValue !== undefined) {
if (
typeof interpolatedValue !== this.allowedValueTypeName &&
interpolatedValue !== undefined
) {
throw new BuildStepRuntimeError(
`Input parameter "${this.id}" for step "${this.stepDisplayName}" must be of type "${this.allowedValueTypeName}".`
);
}
returnValue = rawValue as BuildStepInputValueType<T>;
returnValue = interpolatedValue as BuildStepInputValueType<T>;
} else {
// `valueDoesNotRequireInterpolation` checks that `rawValue` is not undefined
// `valueDoesNotRequireInterpolation` checks that `interpolatedValue` is not undefined
// so this will never be true.
assert(rawValue !== undefined);
const valueInterpolatedWithGlobalContext = this.ctx.interpolate(rawValue);
assert(interpolatedValue !== undefined);
const valueInterpolatedWithGlobalContext = this.ctx.interpolate(interpolatedValue);
const valueInterpolatedWithOutputsAndGlobalContext = interpolateWithOutputs(
valueInterpolatedWithGlobalContext,
(path) => this.ctx.getStepOutputValue(path) ?? ''
@@ -208,15 +220,7 @@ export class BuildStepInput<
}

private parseInputValueToString(value: string): string {
let parsedValue = value;
try {
parsedValue = JSON.parse(`"${value}"`);
} catch (err) {
if (!(err instanceof SyntaxError)) {
throw err;
}
}
return parsedValue;
return `${value}`;
}

private parseInputValueToNumber(value: string): number {
12 changes: 8 additions & 4 deletions packages/steps/src/BuildWorkflowValidator.ts
Original file line number Diff line number Diff line change
@@ -55,9 +55,11 @@ export class BuildWorkflowValidator {
typeof currentStepInput.rawValue === 'object'
? BuildStepInputValueTypeName.JSON
: typeof currentStepInput.rawValue;
const rawValueMayRequireInterpolation =
typeof currentStepInput.rawValue === 'string' && currentStepInput.rawValue.includes('${');
if (
currentStepInput.rawValue !== undefined &&
!currentStepInput.isRawValueStepOrContextReference() &&
!rawValueMayRequireInterpolation &&
currentType !== currentStepInput.allowedValueTypeName
) {
const error = new BuildConfigError(
@@ -81,9 +83,11 @@ export class BuildWorkflowValidator {
const error = new BuildConfigError(
`Input parameter "${currentStepInput.id}" for step "${
currentStep.displayName
}" is set to "${
currentStepInput.value
}" which is not one of the allowed values: ${nullthrows(currentStepInput.allowedValues)
}" is set to "${currentStepInput.getValue({
interpolationContext: currentStep.getInterpolationContext(),
})}" which is not one of the allowed values: ${nullthrows(
currentStepInput.allowedValues
)
.map((i) => `"${i}"`)
.join(', ')}.`
);
43 changes: 33 additions & 10 deletions packages/steps/src/__tests__/BuildConfigParser-test.ts
Original file line number Diff line number Diff line change
@@ -224,26 +224,37 @@ describe(BuildConfigParser, () => {
// property3: value4
// command: echo "Hi, ${ inputs.name }, ${ inputs.boolean_value }!"
const step1 = buildSteps[0];
const step1InterpolationContext = step1.getInterpolationContext();
expect(step1.id).toMatch(UUID_REGEX);
expect(step1.name).toBe('Say HI');
expect(step1.command).toBe('echo "Hi, ${ inputs.name }, ${ inputs.boolean_value }!"');
expect(step1.ctx.workingDirectory).toBe(ctx.defaultWorkingDirectory);
expect(step1.shell).toBe(getDefaultShell());
expect(step1.inputs).toBeDefined();
expect(step1.inputs?.[0].id).toBe('name');
expect(step1.inputs?.[0].value).toBe('Dominik Sokal');
expect(step1.inputs?.[0].getValue({ interpolationContext: step1InterpolationContext })).toBe(
'Dominik Sokal'
);
expect(step1.inputs?.[0].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step1.inputs?.[1].id).toBe('country');
expect(step1.inputs?.[1].value).toBe('Poland');
expect(step1.inputs?.[1].getValue({ interpolationContext: step1InterpolationContext })).toBe(
'Poland'
);
expect(step1.inputs?.[1].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step1.inputs?.[2].id).toBe('boolean_value');
expect(step1.inputs?.[2].value).toBe(true);
expect(step1.inputs?.[2].getValue({ interpolationContext: step1InterpolationContext })).toBe(
true
);
expect(step1.inputs?.[2].allowedValueTypeName).toBe(BuildStepInputValueTypeName.BOOLEAN);
expect(step1.inputs?.[3].id).toBe('number_value');
expect(step1.inputs?.[3].value).toBe(123);
expect(step1.inputs?.[3].getValue({ interpolationContext: step1InterpolationContext })).toBe(
123
);
expect(step1.inputs?.[3].allowedValueTypeName).toBe(BuildStepInputValueTypeName.NUMBER);
expect(step1.inputs?.[4].id).toBe('json_value');
expect(step1.inputs?.[4].value).toMatchObject({
expect(
step1.inputs?.[4].getValue({ interpolationContext: step1InterpolationContext })
).toMatchObject({
property1: 'value1',
property2: ['value2', { value3: { property3: 'value4' } }],
});
@@ -328,19 +339,24 @@ describe(BuildConfigParser, () => {
// - aaa
// - bbb
const step1 = buildSteps[0];
const step1InterpolationContext = step1.getInterpolationContext();
expect(step1.id).toMatch(UUID_REGEX);
expect(step1.name).toBe('Hi!');
expect(step1.command).toBe('echo "Hi, ${ inputs.name }!"');
expect(step1.ctx.workingDirectory).toBe(ctx.defaultWorkingDirectory);
expect(step1.shell).toBe(getDefaultShell());
expect(step1.inputs?.[0].id).toBe('name');
expect(step1.inputs?.[0].value).toBe('Dominik');
expect(step1.inputs?.[0].getValue({ interpolationContext: step1InterpolationContext })).toBe(
'Dominik'
);
expect(step1.inputs?.[0].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step1.inputs?.[1].id).toBe('build_number');
expect(step1.inputs?.[1].rawValue).toBe('${ eas.job.version.buildNumber }');
expect(step1.inputs?.[1].allowedValueTypeName).toBe(BuildStepInputValueTypeName.NUMBER);
expect(step1.inputs?.[2].id).toBe('json_input');
expect(step1.inputs?.[2].value).toMatchObject({
expect(
step1.inputs?.[2].getValue({ interpolationContext: step1InterpolationContext })
).toMatchObject({
property1: 'value1',
property2: ['aaa', 'bbb'],
});
@@ -356,19 +372,26 @@ describe(BuildConfigParser, () => {
// name: Szymon
// build_number: 122
const step2 = buildSteps[1];
const step2InterpolationContext = step2.getInterpolationContext();
expect(step2.id).toMatch(UUID_REGEX);
expect(step2.name).toBe('Hi, Szymon!');
expect(step2.command).toBe('echo "Hi, ${ inputs.name }!"');
expect(step2.ctx.workingDirectory).toBe(ctx.defaultWorkingDirectory);
expect(step2.shell).toBe(getDefaultShell());
expect(step2.inputs?.[0].id).toBe('name');
expect(step2.inputs?.[0].value).toBe('Szymon');
expect(step2.inputs?.[0].getValue({ interpolationContext: step2InterpolationContext })).toBe(
'Szymon'
);
expect(step2.inputs?.[0].allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(step2.inputs?.[1].id).toBe('build_number');
expect(step2.inputs?.[1].value).toBe(122);
expect(step2.inputs?.[1].getValue({ interpolationContext: step2InterpolationContext })).toBe(
122
);
expect(step2.inputs?.[1].allowedValueTypeName).toBe(BuildStepInputValueTypeName.NUMBER);
expect(step2.inputs?.[2].id).toBe('json_input');
expect(step2.inputs?.[2].value).toMatchObject({
expect(
step2.inputs?.[2].getValue({ interpolationContext: step2InterpolationContext })
).toMatchObject({
property1: 'value1',
property2: ['value2', { value3: { property3: 'value4' } }],
});
9 changes: 5 additions & 4 deletions packages/steps/src/__tests__/BuildFunction-test.ts
Original file line number Diff line number Diff line change
@@ -252,10 +252,11 @@ describe(BuildFunction, () => {
},
workingDirectory: ctx.defaultWorkingDirectory,
});
expect(step.inputs?.[0].value).toBe('abc');
expect(step.inputs?.[1].value).toBe('def');
expect(step.inputs?.[2].value).toBe(false);
expect(step.inputs?.[3].value).toMatchObject({
const interpolationContext = step.getInterpolationContext();
expect(step.inputs?.[0].getValue({ interpolationContext })).toBe('abc');
expect(step.inputs?.[1].getValue({ interpolationContext })).toBe('def');
expect(step.inputs?.[2].getValue({ interpolationContext })).toBe(false);
expect(step.inputs?.[3].getValue({ interpolationContext })).toMatchObject({
b: 2,
});
});
19 changes: 11 additions & 8 deletions packages/steps/src/__tests__/BuildStep-test.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { jest } from '@jest/globals';
import { instance, mock, verify, when } from 'ts-mockito';
import { v4 as uuidv4 } from 'uuid';

import { BuildStep, BuildStepFunction, BuildStepStatus } from '../BuildStep.js';
import { BuildStep, BuildStepStatus } from '../BuildStep.js';
import {
BuildStepInput,
BuildStepInputById,
@@ -623,18 +623,21 @@ describe(BuildStep, () => {
}),
];

const fn: BuildStepFunction = (_ctx, { inputs, outputs }) => {
outputs.abc.set(
`${inputs.foo1.value} ${inputs.foo2.value} ${inputs.foo3.value} ${inputs.foo4.value}`
);
};

const step = new BuildStep(baseStepCtx, {
id,
displayName,
inputs,
outputs,
fn,
fn: (_ctx, { inputs, outputs }) => {
const interpolationContext = step.getInterpolationContext();
outputs.abc.set(
`${inputs.foo1.getValue({ interpolationContext })} ${inputs.foo2.getValue({
interpolationContext,
})} ${inputs.foo3.getValue({ interpolationContext })} ${inputs.foo4.getValue({
interpolationContext,
})}`
);
},
});

await step.executeAsync();
90 changes: 61 additions & 29 deletions packages/steps/src/__tests__/BuildStepInput-test.ts
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@ import {
import { createGlobalContextMock } from './utils/context.js';
import { createMockLogger } from './utils/logger.js';

const emptyInterpolationContext = {} as JobInterpolationContext;

describe(BuildStepInput, () => {
test('basic case string', () => {
const ctx = createGlobalContextMock();
@@ -21,7 +23,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
});
i.set('bar');
expect(i.value).toBe('bar');
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toBe('bar');
});

test('basic case boolean', () => {
@@ -33,7 +35,7 @@ describe(BuildStepInput, () => {
required: true,
});
i.set(false);
expect(i.value).toBe(false);
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toBe(false);
});

test('basic case number', () => {
@@ -45,7 +47,7 @@ describe(BuildStepInput, () => {
required: true,
});
i.set(42);
expect(i.value).toBe(42);
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toBe(42);
});

test('basic case json', () => {
@@ -57,7 +59,7 @@ describe(BuildStepInput, () => {
required: true,
});
i.set({ foo: 'bar' });
expect(i.value).toEqual({ foo: 'bar' });
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual({ foo: 'bar' });
});

test('basic case undefined', () => {
@@ -69,7 +71,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
});
i.set(undefined);
expect(i.value).toBeUndefined();
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toBeUndefined();
});

test('default value string', () => {
@@ -81,7 +83,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
required: true,
});
expect(i.value).toBe('baz');
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toBe('baz');
});

test('default value boolean', () => {
@@ -93,7 +95,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.BOOLEAN,
required: true,
});
expect(i.value).toBe(true);
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toBe(true);
});

test('default value json', () => {
@@ -105,7 +107,9 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.JSON,
required: true,
});
expect(i.value).toEqual({ foo: 'bar' });
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual({
foo: 'bar',
});
});

test('context value string', () => {
@@ -117,7 +121,7 @@ describe(BuildStepInput, () => {
required: true,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
});
expect(i.value).toEqual('linux');
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual('linux');
});

test('context value string with newline characters', () => {
@@ -135,7 +139,9 @@ describe(BuildStepInput, () => {
required: true,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
});
expect(i.value).toEqual('Line 1\nLine 2\n\nLine 3');
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual(
'Line 1\nLine 2\n\nLine 3'
);
});

test('context value string with doubly escaped newline characters', () => {
@@ -153,7 +159,31 @@ describe(BuildStepInput, () => {
required: true,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
});
expect(i.value).toEqual('Line 1\nLine 2\n\nLine 3');
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual(
'Line 1\\nLine 2\\n\\nLine 3'
);
});

it('interpolates correctly', () => {
const ctx = createGlobalContextMock();
const i = new BuildStepInput(ctx, {
id: 'foo',
stepDisplayName: BuildStep.getDisplayName({ id: 'test1' }),
required: false,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
});
i.set('${{ env.MY_ENV_VAR }}');

const step = new BuildStep(ctx, {
id: 'test1',
displayName: 'test1',
inputs: [i],
command: '',
env: {
MY_ENV_VAR: 'bar',
},
});
expect(i.getValue({ interpolationContext: step.getInterpolationContext() })).toEqual('bar');
});

test('context value number', () => {
@@ -178,7 +208,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.NUMBER,
required: true,
});
expect(i.value).toEqual(42);
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual(42);
});

test('context value boolean', () => {
@@ -205,7 +235,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.BOOLEAN,
required: true,
});
expect(i.value).toEqual(false);
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual(false);
});

test('context value JSON', () => {
@@ -232,7 +262,9 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.JSON,
required: true,
});
expect(i.value).toMatchObject({ bar: [1, 2, 3, { baz: { qux: false } }] });
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toMatchObject({
bar: [1, 2, 3, { baz: { qux: false } }],
});
});

test('invalid context value type number', () => {
@@ -259,7 +291,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.NUMBER,
required: true,
});
expect(() => i.value).toThrowError(
expect(() => i.getValue({ interpolationContext: emptyInterpolationContext })).toThrowError(
'Input parameter "foo" for step "test1" must be of type "number".'
);
});
@@ -288,7 +320,7 @@ describe(BuildStepInput, () => {
required: true,
allowedValueTypeName: BuildStepInputValueTypeName.BOOLEAN,
});
expect(() => i.value).toThrowError(
expect(() => i.getValue({ interpolationContext: emptyInterpolationContext })).toThrowError(
'Input parameter "foo" for step "test1" must be of type "boolean".'
);
});
@@ -317,7 +349,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.JSON,
required: true,
});
expect(() => i.value).toThrowError(
expect(() => i.getValue({ interpolationContext: emptyInterpolationContext })).toThrowError(
'Input parameter "foo" for step "test1" must be of type "json".'
);
});
@@ -346,7 +378,7 @@ describe(BuildStepInput, () => {
bazbaz: ['bazbaz', '${ eas.context_val_1 }', '${ eas.context_val_2.in_val_1 }'],
},
});
expect(i.value).toEqual({
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual({
foo: 'foo',
bar: 'val_1',
baz: {
@@ -377,7 +409,7 @@ describe(BuildStepInput, () => {
bazbaz: ['bazbaz', '${ eas.context_val_1 }'],
},
});
expect(i.value).toEqual({
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toEqual({
foo: 'foo',
bar: 'Line 1\nLine 2\n\nLine 3',
baz: {
@@ -396,7 +428,7 @@ describe(BuildStepInput, () => {
allowedValueTypeName: BuildStepInputValueTypeName.NUMBER,
required: true,
});
expect(i.value).toBe(42);
expect(i.getValue({ interpolationContext: emptyInterpolationContext })).toBe(42);
});

test('enforces required policy when reading value', () => {
@@ -409,7 +441,7 @@ describe(BuildStepInput, () => {
});
expect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
i.value;
i.getValue({ interpolationContext: emptyInterpolationContext });
}).toThrowError(
new BuildStepRuntimeError(
'Input parameter "foo" for step "test1" is required but it was not set.'
@@ -428,7 +460,7 @@ describe(BuildStepInput, () => {
i.set('bar');
expect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
i.value;
i.getValue({ interpolationContext: emptyInterpolationContext });
}).toThrowError(
new BuildStepRuntimeError('Input parameter "foo" for step "test1" must be of type "boolean".')
);
@@ -445,7 +477,7 @@ describe(BuildStepInput, () => {
i.set('${ eas.runtimePlatform }');
expect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
i.value;
i.getValue({ interpolationContext: emptyInterpolationContext });
}).toThrowError('Input parameter "foo" for step "test1" must be of type "json".');
});

@@ -460,7 +492,7 @@ describe(BuildStepInput, () => {
i.set('${ eas.runtimePlatform }');
expect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
i.value;
i.getValue({ interpolationContext: emptyInterpolationContext });
}).toThrowError(
new BuildStepRuntimeError('Input parameter "foo" for step "test1" must be of type "number".')
);
@@ -477,7 +509,7 @@ describe(BuildStepInput, () => {
i.set('${ eas.runtimePlatform }');
expect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
i.value;
i.getValue({ interpolationContext: emptyInterpolationContext });
}).toThrowError(
new BuildStepRuntimeError('Input parameter "foo" for step "test1" must be of type "boolean".')
);
@@ -542,7 +574,7 @@ describe(BuildStepInput, () => {
expect(input.allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(input.allowedValues).toEqual(['bar', 'baz']);
expect(input.required).toBe(true);
expect(input.value).toBe('bar');
expect(input.getValue({ interpolationContext: emptyInterpolationContext })).toBe('bar');
});
});

@@ -580,8 +612,8 @@ describe(makeBuildStepInputByIdMap, () => {
expect(Object.keys(result).length).toBe(3);
expect(result.foo1).toBeDefined();
expect(result.foo2).toBeDefined();
expect(result.foo1.value).toBe('bar1');
expect(result.foo2.value).toBe('bar2');
expect(result.foo3.value).toBe(true);
expect(result.foo1.getValue({ interpolationContext: emptyInterpolationContext })).toBe('bar1');
expect(result.foo2.getValue({ interpolationContext: emptyInterpolationContext })).toBe('bar2');
expect(result.foo3.getValue({ interpolationContext: emptyInterpolationContext })).toBe(true);
});
});
3 changes: 0 additions & 3 deletions packages/steps/src/__tests__/BuildWorkflowValidator-test.ts
Original file line number Diff line number Diff line change
@@ -219,9 +219,6 @@ describe(BuildWorkflowValidator, () => {
expect((error as BuildWorkflowError).errors[2].message).toBe(
'Input parameter "id3" for step "step_id" is set to "abc" which is not of type "json" or is not step or context reference.'
);
expect((error as BuildWorkflowError).errors[3].message).toBe(
'Input parameter "id6" for step "step_id" is set to "${ wrong.aaa }" which is not of type "number" or is not step or context reference.'
);
});
test('output from future step', async () => {
const ctx = createGlobalContextMock();
7 changes: 4 additions & 3 deletions packages/steps/src/__tests__/StepsConfigParser-test.ts
Original file line number Diff line number Diff line change
@@ -335,22 +335,23 @@ describe(StepsConfigParser, () => {
expect(step4.inputs).toBeDefined();
assert(step4.inputs);
const [input1, input2, input3, input4] = step4.inputs;
const step4InterpolationContext = step4.getInterpolationContext();
expect(input1.id).toBe('arg1');
expect(input1.value).toBe('value1');
expect(input1.getValue({ interpolationContext: step4InterpolationContext })).toBe('value1');
expect(input1.allowedValueTypeName).toBe(BuildStepInputValueTypeName.STRING);
expect(input1.allowedValues).toBeUndefined();
expect(input1.defaultValue).toBeUndefined();
expect(input1.rawValue).toBe('value1');
expect(input1.required).toBe(true);
expect(input2.id).toBe('arg2');
expect(input2.value).toBe(2);
expect(input2.getValue({ interpolationContext: step4InterpolationContext })).toBe(2);
expect(input2.allowedValueTypeName).toBe(BuildStepInputValueTypeName.NUMBER);
expect(input2.allowedValues).toBeUndefined();
expect(input2.defaultValue).toBeUndefined();
expect(input2.rawValue).toBe(2);
expect(input2.required).toBe(true);
expect(input3.id).toBe('arg3');
expect(input3.value).toMatchObject({
expect(input3.getValue({ interpolationContext: step4InterpolationContext })).toMatchObject({
key1: 'value1',
key2: ['value1'],
});