Skip to content
This repository was archived by the owner on Feb 24, 2026. It is now read-only.
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -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<typeof createTestContext>,
flowPath: string,
envVars: Record<string, string>
): 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');
});
});
21 changes: 20 additions & 1 deletion packages/build-tools/src/steps/functionGroups/maestroTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, string>): string {
return envVars
? Object.entries(envVars)
.map(([key, value]) => `-e ${key}=${value}`)
.join(' ')
: '';
}

function getMaestroTestCommand(flowPath: string, envVars?: Record<string, string>): string {
const envFlags = getEnvFlags(envVars);
return envFlags ? `maestro test ${envFlags} ${flowPath}` : `maestro test ${flowPath}`;
}

export function createEasMaestroTestFunctionGroup(
buildToolsContext: CustomBuildContext
): BuildFunctionGroup {
Expand All @@ -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[] = [
Expand Down Expand Up @@ -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<string, string> | 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),
})
);
}
Expand Down