It seems protractor doesn't provide any out of the box solution for starting a server before it runs. Having to run multiple commands before functional tests will run is a bad user experience and bad for automated testing.
Angular-cli has its own solution that is rather complicated, which this plugin claims to duplicate, although it doesn't work for me and may be unmaintained. https://www.npmjs.com/package/protractor-webpack
EDIT: BETTER SOLUTION ACCEPTED BELOW
I came up with a solution using child_process.exec that seems to work well, although I don't like it very much. I'd like to share it in case anyone needs it and to see if anyone can come up with a better solution.
Launch the process in the beforeLaunch hook of protractor:
beforeLaunch: () => {
webpackServerProcess = exec(`webpack-dev-server --port=3003 --open=false`, null, () => {
console.log(`Webpack Server process reports that it exited. Its possible a server was already running on port ${port}`)
});
},
Then above the configuration block we set up the exit handlers to make positively sure that server gets killed when we are done.
let webpackServerProcess; // Set below in beforeLaunch hook
function cleanUpServer(eventType) {
console.log(`Server Cleanup caught ${eventType}, killing server`);
if (webpackServerProcess) {
webpackServerProcess.kill();
console.log(`SERVER KILLED`);
}
}
[`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`].forEach((eventType) => {
process.on(eventType, cleanUpServer.bind(null, eventType));
})
The various event listeners are needed to handle cntrl+c events and situations where the process is killed by ID. Strange that node does not provide an event to encompass all of these.
I found a much more reliable way to do it using the webpack-dev-server node api. That way no separate process is spawned and we don't have to clean anything. Also, it blocks protractor until webpack is ready.
beforeLaunch: () => {
return new Promise((resolve, reject) => {
new WebpackDevServer(webpack(require('./webpack.config.js')()), {
// Do stuff
}).listen(APP_PORT, '0.0.0.0', function(err) {
console.log('webpack dev server error is ', err)
resolve()
}).on('error', (error) => {
console.log('dev server error ', error)
reject(error)
})
})
},