Skip to content
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
6 changes: 5 additions & 1 deletion .github/actions/maestro-android/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ inputs:
required: false
default: x86
description: The architecture of the emulator to run
test-state-path:
required: false
default: /tmp/maestro-android-state/results.json
description: The path used to persist per-flow test results between retries

runs:
using: composite
Expand Down Expand Up @@ -64,7 +68,7 @@ runs:
cores: '4'
disable-animations: false
avd-name: e2e_emulator
script: node .github/workflow-scripts/maestro-android.js ${{ inputs.app-path }} ${{ inputs.app-id }} ${{ inputs.maestro-flow }} ${{ inputs.flavor }} ${{ inputs.working-directory }}
script: node .github/workflow-scripts/maestro-android.js ${{ inputs.app-path }} ${{ inputs.app-id }} ${{ inputs.maestro-flow }} ${{ inputs.flavor }} ${{ inputs.working-directory }} ${{ inputs.test-state-path }}
- name: Normalize APP_ID
id: normalize-app-id
shell: bash
Expand Down
89 changes: 89 additions & 0 deletions .github/workflow-scripts/__tests__/maestro-android-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

const fs = require('fs');
const os = require('os');
const path = require('path');

const {
collectFlows,
executeFlowSuite,
loadState,
} = require('../maestro-android');

describe('Maestro Android runner', () => {
let temporaryDirectory;

beforeEach(() => {
temporaryDirectory = fs.mkdtempSync(
path.join(os.tmpdir(), 'maestro-android-test-'),
);
jest.spyOn(console, 'error').mockImplementation(() => {});
});

afterEach(() => {
jest.restoreAllMocks();
fs.rmSync(temporaryDirectory, {recursive: true, force: true});
});

it('collects flows recursively in a stable order', () => {
const nestedDirectory = path.join(temporaryDirectory, 'nested');
fs.mkdirSync(nestedDirectory);
fs.writeFileSync(path.join(temporaryDirectory, 'second.yaml'), 'appId: x');
fs.writeFileSync(path.join(temporaryDirectory, 'image.png'), 'not a flow');
fs.writeFileSync(path.join(nestedDirectory, 'first.yml'), 'appId: x');

expect(collectFlows(temporaryDirectory)).toEqual([
path.join(nestedDirectory, 'first.yml'),
path.join(temporaryDirectory, 'second.yaml'),
]);
});

it('runs every flow and retries only flows that have not passed', () => {
const flows = ['first.yml', 'second.yml', 'third.yml'].map(file =>
path.join(temporaryDirectory, file),
);
const statePath = path.join(temporaryDirectory, 'state', 'results.json');
const firstAttempt = jest.fn(flow => {
if (flow.endsWith('second.yml')) {
throw new Error('failed assertion');
}
});

expect(() =>
executeFlowSuite({
flows,
appId: 'com.example',
state: loadState(statePath),
statePath,
executeFlow: firstAttempt,
}),
).toThrow('1 Maestro flow(s) failed');
expect(firstAttempt).toHaveBeenCalledTimes(3);

const retry = jest.fn();
executeFlowSuite({
flows,
appId: 'com.example',
state: loadState(statePath),
statePath,
executeFlow: retry,
});

expect(retry).toHaveBeenCalledTimes(1);
expect(retry.mock.calls[0][0]).toBe(flows[1]);

const finalState = loadState(statePath);
expect(Object.values(finalState.flows)).toEqual([
{status: 'passed', attempts: 1},
{status: 'passed', attempts: 2},
{status: 'passed', attempts: 1},
]);
});
});
Loading
Loading