From 456792292e6da79b0135b10f0c67fccaac4e9f00 Mon Sep 17 00:00:00 2001 From: Julien Ambrosio Date: Thu, 23 Jan 2025 21:02:55 -0300 Subject: [PATCH 1/3] feat: generate params for maestro based on envs --- .../__tests__/maestroTest.test.ts | 95 +++++++++++++++++++ .../src/steps/functionGroups/maestroTest.ts | 18 +++- 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts 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..9b0e81541 --- /dev/null +++ b/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts @@ -0,0 +1,95 @@ +import path from 'path'; + +import { Platform } from '@expo/eas-build-job'; +import { BuildStepGlobalContext, BuildStep } from '@expo/steps'; + +import { createEasMaestroTestFunctionGroup, getEnvFlags } from '../maestroTest'; +import { CustomBuildContext } from '../../../customBuildContext'; +import { createMockLogger } from '../../../__tests__/utils/logger'; + +export function createMockGlobalContext(): BuildStepGlobalContext { + const logger = createMockLogger(); + const stepContextMock = { + logger, + workingDirectory: '/fake/path', + interpolate: jest.fn().mockImplementation((val) => val), + getStepOutputValue: jest.fn().mockImplementation(() => undefined), + }; + + return { + baseLogger: logger, + logger, + defaultWorkingDirectory: '/fake/path', + env: {}, + stepsInternalBuildDirectory: path.join('/fake/path', 'internal'), + stepCtx: jest.fn().mockImplementation(() => stepContextMock), + registerStep: jest.fn().mockImplementation((_: BuildStep) => {}), + interpolate: stepContextMock.interpolate, + getStepOutputValue: stepContextMock.getStepOutputValue, + } as unknown as BuildStepGlobalContext; +} + +function createTestContext(platform: Platform): { + globalCtx: BuildStepGlobalContext; + buildContext: CustomBuildContext; +} { + return { + globalCtx: createMockGlobalContext(), + 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]); + }); + }); +}); + +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..e37dea343 100644 --- a/packages/build-tools/src/steps/functionGroups/maestroTest.ts +++ b/packages/build-tools/src/steps/functionGroups/maestroTest.ts @@ -12,6 +12,14 @@ 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(' ') + : ''; +} + export function createEasMaestroTestFunctionGroup( buildToolsContext: CustomBuildContext ): BuildFunctionGroup { @@ -34,8 +42,14 @@ 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[] = [ createInstallMaestroBuildFunction().createBuildStepFromFunctionCall(globalCtx), ]; @@ -119,13 +133,15 @@ 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; + const envFlags = getEnvFlags(envVars); steps.push( new BuildStep(globalCtx, { id: BuildStep.getNewId(), name: 'maestro_test', ifCondition: '${ always() }', displayName: `maestro test ${flowPath}`, - command: `maestro test ${flowPath}`, + command: `maestro test ${envFlags} ${flowPath}`, }) ); } From b12f614c7e890c8ae2587a1920b3dc1e6087781b Mon Sep 17 00:00:00 2001 From: Julien Ambrosio Date: Thu, 23 Jan 2025 22:31:23 -0300 Subject: [PATCH 2/3] test: improve maestro test, using createGlobalContextMock --- .../__tests__/maestroTest.test.ts | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts b/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts index 9b0e81541..5aa71e260 100644 --- a/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts +++ b/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts @@ -1,40 +1,16 @@ -import path from 'path'; - -import { Platform } from '@expo/eas-build-job'; import { BuildStepGlobalContext, BuildStep } from '@expo/steps'; +import { Platform } from '@expo/eas-build-job'; import { createEasMaestroTestFunctionGroup, getEnvFlags } from '../maestroTest'; import { CustomBuildContext } from '../../../customBuildContext'; -import { createMockLogger } from '../../../__tests__/utils/logger'; - -export function createMockGlobalContext(): BuildStepGlobalContext { - const logger = createMockLogger(); - const stepContextMock = { - logger, - workingDirectory: '/fake/path', - interpolate: jest.fn().mockImplementation((val) => val), - getStepOutputValue: jest.fn().mockImplementation(() => undefined), - }; - - return { - baseLogger: logger, - logger, - defaultWorkingDirectory: '/fake/path', - env: {}, - stepsInternalBuildDirectory: path.join('/fake/path', 'internal'), - stepCtx: jest.fn().mockImplementation(() => stepContextMock), - registerStep: jest.fn().mockImplementation((_: BuildStep) => {}), - interpolate: stepContextMock.interpolate, - getStepOutputValue: stepContextMock.getStepOutputValue, - } as unknown as BuildStepGlobalContext; -} +import { createGlobalContextMock } from '../../../__tests__/utils/context'; function createTestContext(platform: Platform): { globalCtx: BuildStepGlobalContext; buildContext: CustomBuildContext; } { return { - globalCtx: createMockGlobalContext(), + globalCtx: createGlobalContextMock(), buildContext: { job: { platform } } as CustomBuildContext, }; } From 9f87c50d8b109dab24e145be227578195d89495b Mon Sep 17 00:00:00 2001 From: Julien Ambrosio Date: Thu, 23 Jan 2025 22:49:13 -0300 Subject: [PATCH 3/3] fix: adjust the maestro command without parameter --- .../functionGroups/__tests__/maestroTest.test.ts | 15 +++++++++++++++ .../src/steps/functionGroups/maestroTest.ts | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts b/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts index 5aa71e260..ed159b5c9 100644 --- a/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts +++ b/packages/build-tools/src/steps/functionGroups/__tests__/maestroTest.test.ts @@ -59,6 +59,21 @@ describe('Maestro Test Commands', () => { 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', () => { diff --git a/packages/build-tools/src/steps/functionGroups/maestroTest.ts b/packages/build-tools/src/steps/functionGroups/maestroTest.ts index e37dea343..5093b0e67 100644 --- a/packages/build-tools/src/steps/functionGroups/maestroTest.ts +++ b/packages/build-tools/src/steps/functionGroups/maestroTest.ts @@ -20,6 +20,11 @@ export function getEnvFlags(envVars?: Record): string { : ''; } +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 { @@ -49,7 +54,6 @@ export function createEasMaestroTestFunctionGroup( }), ], createBuildStepsFromFunctionGroupCall: (globalCtx, { inputs }) => { - const steps: BuildStep[] = [ createInstallMaestroBuildFunction().createBuildStepFromFunctionCall(globalCtx), ]; @@ -134,14 +138,13 @@ export function createEasMaestroTestFunctionGroup( .filter((entry) => entry); for (const flowPath of flowPaths) { const envVars = inputs.env?.value as Record | undefined; - const envFlags = getEnvFlags(envVars); steps.push( new BuildStep(globalCtx, { id: BuildStep.getNewId(), name: 'maestro_test', ifCondition: '${ always() }', displayName: `maestro test ${flowPath}`, - command: `maestro test ${envFlags} ${flowPath}`, + command: getMaestroTestCommand(flowPath, envVars), }) ); }