in lib/cmds/start.ts [345:467]
function signalWhenReady(
signal: string, viaIPC: boolean, outputDir: string, seleniumPort: string, appiumPort: string,
androidSDK: Binary, avdPort: number, avdNames: string[]) {
const maxWait = 10 * 60 * 1000; // Ten minutes
function waitFor(
getStatus: () => Promise<string>, testStatus: (status: string) => boolean, desc?: string) {
const checkInterval = 100;
return new Promise<void>((resolve, reject) => {
let waited = 0;
(function recursiveCheck() {
setTimeout(() => {
getStatus()
.then<void>((status: string) => {
if (!testStatus(status)) {
return Promise.reject(
'Invalid status' + (desc ? ' for ' + desc : '') + ': ' + status);
}
})
.then(
() => {
resolve();
},
(error: any) => {
waited += checkInterval;
if (waited < maxWait) {
recursiveCheck();
} else {
reject(
'Timed out' + (desc ? ' wating for' + desc : '') +
'. Final rejection reason: ' + JSON.stringify(error));
}
});
}, checkInterval);
})();
});
};
function waitForAndroid(avdPort: number, avdName: string, appiumPort: string): Promise<void> {
let sdkPath = path.resolve(outputDir, androidSDK.executableFilename());
logger.info('Waiting for ' + avdName + '\'s emulator to start');
return adb(sdkPath, avdPort, 'wait-for-device', maxWait)
.then<void>(
() => {
logger.info('Waiting for ' + avdName + '\'s OS to boot up');
return waitFor(
() => {
return adb(
sdkPath, avdPort, 'shell', maxWait, ['getprop', 'sys.boot_completed']);
},
(status: string) => {
return status.trim() == '1';
},
avdName + '\'s OS');
},
(error: {code: string|number, message: string}) => {
return Promise.reject(
'Failed to wait for ' + avdName + '\'s emulator to start (' + error.code + ': ' +
error.message + ')');
})
.then<string>(() => {
logger.info('Waiting for ' + avdName + ' to be ready to launch chrome');
let version = AndroidSDK.VERSIONS[parseInt(avdName.slice('android-'.length))];
return request('POST', appiumPort, '/wd/hub/session', maxWait, {
desiredCapabilities: {
browserName: 'chrome',
platformName: 'Android',
platformVersion: version,
deviceName: 'Android Emulator'
}
})
.then(
(data) => {
return JSON.parse(data)['sessionId'];
},
(error: {code: string|number, message: string}) => {
return Promise.reject(
'Could not start chrome on ' + avdName + ' (' + error.code + ': ' +
error.message + ')');
});
})
.then<void>((sessionId: string) => {
logger.info('Shutting down dummy chrome instance for ' + avdName);
return request('DELETE', appiumPort, '/wd/hub/session/' + sessionId)
.then<void>(() => {}, (error: {code: string|number, message: string}) => {
return Promise.reject(
'Could not close chrome on ' + avdName + ' (' + error.code + ': ' +
error.message + ')');
});
});
}
let pending = [waitFor(
() => {
return request('GET', seleniumPort, '/wd/hub/status', maxWait);
},
(status) => {
return JSON.parse(status).status == 0;
},
'selenium server')];
if (appiumPort) {
pending.push(waitFor(
() => {
return request('GET', appiumPort, '/wd/hub/status', maxWait);
},
(status) => {
return JSON.parse(status).status == 0;
},
'appium server'));
}
if (androidSDK && avdPort) {
for (let i = 0; i < avdNames.length; i++) {
pending.push(waitForAndroid(avdPort + 2 * i, avdNames[i], appiumPort));
}
}
Promise.all(pending).then(
() => {
logger.info('Everything started');
sendStartedSignal(signal, viaIPC);
},
(error) => {
logger.error(error);
shutdownEverything(seleniumPort);
process.exitCode = 1;
});
}