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

[Mobile] Improve Screen recording on Android for Appium 2 #55443

Merged
Merged
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,30 @@
/**
* External dependencies
*/
const childProcess = require( 'child_process' );

export function getAndroidEmulatorID() {
try {
const adbOutput = childProcess.execSync( 'adb devices -l' ).toString();

const lines = adbOutput
.split( '\n' )
.filter( ( line ) => line && line.includes( 'emulator-' ) );

if ( lines.length === 0 ) {
// eslint-disable-next-line no-console
console.error( 'No available emulators found.' );
return null;
}
const firstEmulatorLine = lines[ 0 ];
// Extract device ID from the beginning of the line
return firstEmulatorLine.split( /\s+/ )[ 0 ];
} catch ( error ) {
// eslint-disable-next-line no-console
console.error(
'Failed to fetch the first available emulator ID:',
error.message
);
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const {
prefixKeysWithAppium,
} = require( './caps' );
const AppiumLocal = require( './appium-local' );
import { getAndroidEmulatorID } from './get-android-emulator-id';

// Platform setup.
const defaultPlatform = 'android';
Expand Down Expand Up @@ -104,10 +105,14 @@ const setupDriver = async () => {
if ( isAndroid() ) {
desiredCaps = { ...android };
if ( isLocalEnvironment() ) {
const androidDeviceID = getAndroidEmulatorID();
desiredCaps.app = path.resolve( localAndroidAppPath );
desiredCaps.udid = androidDeviceID;
try {
const androidVersion = childProcess
.execSync( 'adb shell getprop ro.build.version.release' )
.execSync(
`adb -s ${ androidDeviceID } shell getprop ro.build.version.release`
)
.toString()
.replace( /^\s+|\s+$/g, '' );
delete desiredCaps.platformVersion;
Expand Down
61 changes: 52 additions & 9 deletions packages/react-native-editor/jest_ui_setup_after_env.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,40 @@ const childProcess = require( 'child_process' );
* Internal dependencies
*/
const { isAndroid } = require( './__device-tests__/helpers/utils' );
import { getAndroidEmulatorID } from './__device-tests__/helpers/get-android-emulator-id';

jest.setTimeout( 1000000 ); // In milliseconds.

let iOSScreenRecordingProcess;
let androidScreenRecordingProcess;
let androidDeviceID;

const isMacOSEnvironment = () => {
return process.platform === 'darwin';
};

const IOS_RECORDINGS_DIR = './ios-screen-recordings';
const ANDROID_RECORDINGS_DIR = './android-screen-recordings';
const ANDROID_EMULATOR_DIR = '/sdcard/';

const getScreenRecordingFileNameBase = ( testPath, id ) => {
const suiteName = path.basename( testPath, '.test.js' );
return `${ suiteName }.${ id }`;
};

function deleteRecordingFile( filePath ) {
if ( fs.existsSync( filePath ) ) {
try {
fs.unlinkSync( filePath );
} catch ( error ) {
// eslint-disable-next-line no-console
console.error(
`Failed to delete ${ filePath }. Error: ${ error.message }`
);
}
}
}

let allPassed = true;

// eslint-disable-next-line jest/no-jasmine-globals, no-undef
Expand All @@ -40,26 +56,40 @@ jasmine.getEnv().addReporter( {
getScreenRecordingFileNameBase( testPath, id ) + '.mp4';

if ( isAndroid() ) {
androidDeviceID = getAndroidEmulatorID();

if ( ! fs.existsSync( ANDROID_RECORDINGS_DIR ) ) {
fs.mkdirSync( ANDROID_RECORDINGS_DIR );
}

// Use the "mkdir -p" command to create the
// ANDROID_EMULATOR_DIR directory if it doesn't exist.
try {
childProcess.execSync(
`adb -s ${ androidDeviceID } shell "mkdir -p ${ ANDROID_EMULATOR_DIR }" 2>/dev/null`
);
} catch ( error ) {
// eslint-disable-next-line no-console
console.error( `Failed to create the directory: ${ error }` );
}

androidScreenRecordingProcess = childProcess.spawn( 'adb', [
'-s',
androidDeviceID,
'shell',
'screenrecord',
'--verbose',
'--bit-rate',
'1M',
'--size',
'720x1280',
`/sdcard/${ fileName }`,
`${ ANDROID_EMULATOR_DIR }${ fileName }`,
] );

androidScreenRecordingProcess.stderr.on( 'data', ( data ) => {
// eslint-disable-next-line no-console
console.log( `Android screen recording error => ${ data }` );
} );

return;
}

Expand All @@ -85,6 +115,7 @@ jasmine.getEnv().addReporter( {
},
specDone: ( { testPath, id, status } ) => {
allPassed = allPassed && status !== 'failed';
const isTestSkipped = status === 'pending';

if ( ! isMacOSEnvironment() ) {
return;
Expand All @@ -97,9 +128,16 @@ jasmine.getEnv().addReporter( {
// Wait for kill.
childProcess.execSync( 'sleep 1' );

const recordingFilePath = `${ ANDROID_RECORDINGS_DIR }/${ fileNameBase }.mp4`;

if ( isTestSkipped ) {
deleteRecordingFile( recordingFilePath );
return;
}

try {
childProcess.execSync(
`adb pull /sdcard/${ fileNameBase }.mp4 ${ ANDROID_RECORDINGS_DIR }`
`adb -s ${ androidDeviceID } pull ${ ANDROID_EMULATOR_DIR }${ fileNameBase }.mp4 ${ ANDROID_RECORDINGS_DIR }`
);
} catch ( error ) {
// Some (old) Android devices don't support screen recording or
Expand All @@ -113,22 +151,27 @@ jasmine.getEnv().addReporter( {
);
}

const oldPath = `${ ANDROID_RECORDINGS_DIR }/${ fileNameBase }.mp4`;
const newPath = `${ ANDROID_RECORDINGS_DIR }/${ fileNameBase }.${ status }.mp4`;

if ( fs.existsSync( oldPath ) ) {
fs.renameSync( oldPath, newPath );
if ( fs.existsSync( recordingFilePath ) ) {
fs.renameSync( recordingFilePath, newPath );
}
return;
}

iOSScreenRecordingProcess.kill( 'SIGINT' );

const oldPath = `${ IOS_RECORDINGS_DIR }/${ fileNameBase }.mp4`;
const recordingFilePath = `${ IOS_RECORDINGS_DIR }/${ fileNameBase }.mp4`;

if ( isTestSkipped ) {
deleteRecordingFile( recordingFilePath );
return;
}

const newPath = `${ IOS_RECORDINGS_DIR }/${ fileNameBase }.${ status }.mp4`;

if ( fs.existsSync( oldPath ) ) {
fs.renameSync( oldPath, newPath );
if ( fs.existsSync( recordingFilePath ) ) {
fs.renameSync( recordingFilePath, newPath );
}
},
} );