Launching iOS and Android simulators with Node.js

I wanted a script to open iOS and Android device simulators and open their browsers to a URL (my karma.js test URL) so wrote one in node.js, using child_process to execute bash commands to get the job done.

It’s far from perfect and will probably explode if you don’t have devices already installed, but it’s more than suitable for my needs. This is a post for self reference more than anything.

It:

  • Gets the list of all available iOS devices and picks the fourth one in the list (position 3 in the array generated from the text output) (at the time of writing, on my system this is the iPhone 5)
  • Executes the command to load the selected iOS emulator
  • Gets the list of all available Android AVDs (Android Virtual Devices) and selects the first one (unlike iOS devices through xcode, AVDs have to be manually installed in Android Studio so it can be a pain if you don’t have more than 1 set up)
  • Executes the command to load the AVD
  • Starts a loop to check if the AVD has loaded yet – it does this by checking the AVD’s bootanim property. If this is ‘stopped’ we know the device has loaded (I couldn’t find a better way of detecting device load. If anyone has one, please let me know)
  • Once the Android emulator has loaded – we can assume the iOS emulator has also loaded as the AVD always takes much longer to load – we execute the command to unlock the screen on the AVD.
  • We execute the commands to launch the browser to our required URL.
var process 		= require('child_process'),
	url 			= "http://192.168.10.126:9876",
	iosdevice, bootChecker;
 
process.exec('xcrun simctl list', function(error, stdout, stderr){
	var splitRows = stdout.split("\n");
	iosdevice = splitRows[3].split(" (")[0];
	launchIOSDevice();
});
 
process.exec('~/Library/Android/sdk/tools/android list avd', function(error, stdout, stderr){
	var splitRows = stdout.split('\n'),
		deviceObj = {};
	// console.log(splitRows);
	splitRows.forEach(function(row){
		var splitRow = row.split(':');
		deviceObj[splitRow[0].replace(/\s/g, '')] = splitRow[1];
	});
	console.log(deviceObj.Name.replace(/\s/g, ''));
 
	process.exec('~/Library/Android/sdk/tools/emulator -avd ' + deviceObj.Name.replace(/\s/g, ''));
 
	isAndroidEmulatorBooted(function(response){
		if (!response){
			kickOffBootChecker();
		} else {
			unlockAndLoadURL();
		}
	});
 
});
 
function launchIOSDevice(){
	console.log('xcrun instruments -w "'+iosdevice+'"');
	process.exec('xcrun instruments -w "'+iosdevice+'"', function(error, stdout, stderr){})
}
 
function kickOffBootChecker() {
	bootChecker = setInterval(function(){
		isAndroidEmulatorBooted(function(response){});
	},1000);
}
 
function isAndroidEmulatorBooted(callback){
	process.exec('~/Library/Android/sdk/platform-tools/adb shell getprop init.svc.bootanim', function(error, stdout, stderr){
 
		if (stdout.toString().indexOf("stopped")>-1){
			clearInterval(bootChecker);
			unlockAndLoadURL();
			return callback(true);
		} else {
			console.log('loading Android emulator ...');
			return callback(false);
		}
	});
}
 
function unlockAndLoadURL(){
	//unlock the device
	console.log('unlocking the android emulator');
	process.exec('~/Library/Android/sdk/platform-tools/adb shell input keyevent 82');
 
	//gotourl
	console.log('launching the url on android');
	process.exec('~/Library/Android/sdk/platform-tools/adb shell am start -a android.intent.action.VIEW -d ' + url);
 
	console.log('android emulator takes longer to load so we can be sure the ios emu has loaded. launch url there too');
	process.exec('xcrun simctl openurl "'+iosdevice+'" ' + url);
}
Did you like this post? Why not subscribe?

Leave a Reply

Your email address will not be published. Required fields are marked *