diff --git a/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts b/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts new file mode 100644 index 000000000..ed159b5c9 --- /dev/null +++ b/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts @@ -0,0 +1,86 @@ +import { BuildStepGlobalContext, BuildStep } from '@expo/steps'; +import { Platform } from '@expo/eas-build-job'; + +import { createEasMaestroTestFunctionGroup, getEnvFlags } from '../maestroTest'; +import { CustomBuildContext } from '../../../customBuildContext'; +import { createGlobalContextMock } from '../../../__tests__/utils/context'; + +function createTestContext(platform: Platform): { + globalCtx: BuildStepGlobalContext; + buildContext: CustomBuildContext; +} { + return { + globalCtx: createGlobalContextMock(), + buildContext: { job: { platform } } as CustomBuildContext, + }; +} + +function getMaestroSteps(steps: BuildStep[]): BuildStep[] { + return steps.filter((step: BuildStep) => step.name === 'maestro_test'); +} + +function createMaestroSteps( + context: ReturnType, + flowPath: string, + envVars: Record +): BuildStep[] { + return createEasMaestroTestFunctionGroup( + context.buildContext + ).createBuildStepsFromFunctionGroupCall(context.globalCtx, { + callInputs: { flow_path: flowPath, env: envVars }, + }); +} + +describe('Maestro Test Commands', () => { + it('generates single command for iOS', () => { + const context = createTestContext(Platform.IOS); + const envVars = { TEST_VAR: 'hello' }; + const flowPath = 'myflow.yaml'; + const expectedCommand = `maestro test ${getEnvFlags(envVars)} ${flowPath}`; + + const maestroSteps = getMaestroSteps(createMaestroSteps(context, flowPath, envVars)); + expect(maestroSteps).toHaveLength(1); + expect(maestroSteps[0].command).toBe(expectedCommand); + }); + + it('generates multiple commands for Android flows', () => { + const context = createTestContext(Platform.ANDROID); + const envVars = { API_KEY: '12345' }; + const flowPaths = ['flow1.yaml', 'flow2.yaml']; + const expectedCommands = flowPaths.map( + (flowPath) => `maestro test ${getEnvFlags(envVars)} ${flowPath}` + ); + + const maestroSteps = getMaestroSteps( + createMaestroSteps(context, flowPaths.join('\n'), envVars) + ); + expect(maestroSteps).toHaveLength(2); + maestroSteps.forEach((step: BuildStep, index: number) => { + expect(step.command).toBe(expectedCommands[index]); + }); + }); + + it('generates commands without env vars', () => { + const iosContext = createTestContext(Platform.IOS); + const androidContext = createTestContext(Platform.ANDROID); + const flowPath = 'test.yaml'; + const expectedCommand = `maestro test ${flowPath}`; + + const iosSteps = getMaestroSteps(createMaestroSteps(iosContext, flowPath, {})); + expect(iosSteps).toHaveLength(1); + expect(iosSteps[0].command).toBe(expectedCommand); + + const androidSteps = getMaestroSteps(createMaestroSteps(androidContext, flowPath, {})); + expect(androidSteps).toHaveLength(1); + expect(androidSteps[0].command).toBe(expectedCommand); + }); +}); + +describe('getEnvFlags', () => { + it('generates correct env flags string', () => { + expect(getEnvFlags()).toBe(''); + expect(getEnvFlags({})).toBe(''); + expect(getEnvFlags({ TEST_VAR: 'hello' })).toBe('-e TEST_VAR=hello'); + expect(getEnvFlags({ VAR1: 'value1', VAR2: 'value2' })).toBe('-e VAR1=value1 -e VAR2=value2'); + }); +}); diff --git a/packages/build-tools/src/steps/functionGroups/maestroTest.ts b/packages/build-tools/src/steps/functionGroups/maestroTest.ts index 1d12fd1d3..5093b0e67 100644 --- a/packages/build-tools/src/steps/functionGroups/maestroTest.ts +++ b/packages/build-tools/src/steps/functionGroups/maestroTest.ts @@ -12,6 +12,19 @@ import { createStartIosSimulatorBuildFunction } from '../functions/startIosSimul import { createStartAndroidEmulatorBuildFunction } from '../functions/startAndroidEmulator'; import { createUploadArtifactBuildFunction } from '../functions/uploadArtifact'; +export function getEnvFlags(envVars?: Record): string { + return envVars + ? Object.entries(envVars) + .map(([key, value]) => `-e ${key}=${value}`) + .join(' ') + : ''; +} + +function getMaestroTestCommand(flowPath: string, envVars?: Record): string { + const envFlags = getEnvFlags(envVars); + return envFlags ? `maestro test ${envFlags} ${flowPath}` : `maestro test ${flowPath}`; +} + export function createEasMaestroTestFunctionGroup( buildToolsContext: CustomBuildContext ): BuildFunctionGroup { @@ -34,6 +47,11 @@ export function createEasMaestroTestFunctionGroup( id: 'android_emulator_system_image_package', required: false, }), + BuildStepInput.createProvider({ + allowedValueTypeName: BuildStepInputValueTypeName.JSON, + id: 'env', + required: false, + }), ], createBuildStepsFromFunctionGroupCall: (globalCtx, { inputs }) => { const steps: BuildStep[] = [ @@ -119,13 +137,14 @@ export function createEasMaestroTestFunctionGroup( .split('\n') // It's easy to get an empty line with YAML .filter((entry) => entry); for (const flowPath of flowPaths) { + const envVars = inputs.env?.value as Record | undefined; steps.push( new BuildStep(globalCtx, { id: BuildStep.getNewId(), name: 'maestro_test', ifCondition: '${ always() }', displayName: `maestro test ${flowPath}`, - command: `maestro test ${flowPath}`, + command: getMaestroTestCommand(flowPath, envVars), }) ); }