diff --git a/src/components/experiment-list/experiment-list-element.css b/src/components/experiment-list/experiment-list-element.css
index 8ce8b3aa4cfec6c89bd6a5cf73575b243461ab9f..f45970b4109246b1302fb5ce1f85a74457d0abd3 100644
--- a/src/components/experiment-list/experiment-list-element.css
+++ b/src/components/experiment-list/experiment-list-element.css
@@ -12,27 +12,15 @@
 }
 
 .flex-container .selected {
-  background: linear-gradient(
-    to right,
-    rgba(255, 255, 255, 0.8) 0%,
-    rgba(245, 245, 245, 0.8) 100%
-  );
+  background: linear-gradient( to right, rgba(255, 255, 255, 0.8) 0%, rgba(245, 245, 245, 0.8) 100%);
 }
 
 .flex-container.hover-wizard {
-  background: linear-gradient(
-    to right,
-    rgba(255, 255, 255, 0.8) 0%,
-    rgba(230, 239, 255, 0.8) 100%
-  );
+  background: linear-gradient( to right, rgba(255, 255, 255, 0.8) 0%, rgba(230, 239, 255, 0.8) 100%);
 }
 
 .flex-container.selected-wizard {
-  background: linear-gradient(
-    to right,
-    rgba(255, 255, 255, 0.8) 0%,
-    rgba(230, 239, 255, 0.8) 100%
-  );
+  background: linear-gradient( to right, rgba(255, 255, 255, 0.8) 0%, rgba(230, 239, 255, 0.8) 100%);
 }
 
 .flex-container.left-right {
@@ -69,12 +57,13 @@
   width: 359px;
 }
 
-.list-entry-left > img {
+.list-entry-left>img {
   width: 100%;
   object-fit: contain;
   object-position: top;
   height: 100%;
 }
+
 .list-entry-middle {
   flex: 1;
 }
@@ -124,4 +113,9 @@
 .exp-title-sim-info {
   padding-left: 20px;
   font-style: italic;
+}
+
+.entity-thumbnail {
+  max-width: 230px;
+  max-height: 160px;
 }
\ No newline at end of file
diff --git a/src/components/experiment-list/experiment-list-element.js b/src/components/experiment-list/experiment-list-element.js
index 907d5af87ac91816090a1dea720b4b524767fd06..5dd6543404d8eeb67897c6bc0eebadd0ded5068a 100644
--- a/src/components/experiment-list/experiment-list-element.js
+++ b/src/components/experiment-list/experiment-list-element.js
@@ -53,10 +53,8 @@ export default class ExperimentListElement extends React.Component {
   }
 
   getAvailabilityInfo() {
-    const clusterAvailability = ExperimentServerService.instance.getClusterAvailability();
-
     let status;
-    if (clusterAvailability && clusterAvailability.free > CLUSTER_THRESHOLDS.AVAILABLE) {
+    if (this.props.availableServers && this.props.availableServers.length > CLUSTER_THRESHOLDS.AVAILABLE) {
       status = 'Available';
     }
     else if (!this.props.availableServers || this.props.availableServers.length === 0) {
@@ -66,17 +64,14 @@ export default class ExperimentListElement extends React.Component {
       status = 'Restricted';
     }
 
-    let cluster = `Cluster availability: ${clusterAvailability.free} / ${clusterAvailability.total}`;
     let backends = `Backends: ${this.props.availableServers.length}`;
 
-    return `${status}\n${cluster}\n${backends}`;
+    return `${status}\n${backends}`;
   }
 
   getServerStatusClass() {
-    const clusterAvailability = ExperimentServerService.instance.getClusterAvailability();
-
     let status = '';
-    if (clusterAvailability && clusterAvailability.free > CLUSTER_THRESHOLDS.AVAILABLE) {
+    if (this.props.availableServers && this.props.availableServers.length > CLUSTER_THRESHOLDS.AVAILABLE) {
       status = 'server-status-available';
     }
     else if (!this.props.availableServers || this.props.availableServers.length === 0) {
@@ -171,12 +166,13 @@ export default class ExperimentListElement extends React.Component {
               <div className='btn-group' role='group' >
                 {this.canLaunchExperiment &&
                   exp.configuration.experimentFile && exp.configuration.bibiConfSrc ?
-                  <button onClick={() => {
-                    ExperimentExecutionService.instance.startNewExperiment(exp, false);
-                  }}
-                  disabled={this.isLaunchDisabled()}
-                  className='btn btn-default'
-                  title={this.launchButtonTitle} >
+                  <button
+                    onClick={() => {
+                      ExperimentExecutionService.instance.startNewExperiment(exp, false);
+                    }}
+                    disabled={this.isLaunchDisabled()}
+                    className='btn btn-default'
+                    title={this.launchButtonTitle} >
                     <i className='fa fa-plus'></i> Launch
                   </button>
                   : null}
diff --git a/src/components/user-menu/user-menu.js b/src/components/user-menu/user-menu.js
index 1f7625065e73187987655d38142e81c893e79dcd..a287c8620d25ab87dcf6068bbf47e830b3bbebb4 100644
--- a/src/components/user-menu/user-menu.js
+++ b/src/components/user-menu/user-menu.js
@@ -16,21 +16,18 @@ export default class UserMenu extends React.Component {
     };
   }
 
-  componentDidMount() {
-    this._userRequest = NrpUserService.instance
-      .getCurrentUser()
-      .then((currentUser) => {
-        this._userRequest = null;
-        this.setState(() => ({
+  async componentDidMount() {
+    NrpUserService.instance.getCurrentUser().then((currentUser) => {
+      if (!this.cancelGetCurrentUser) {
+        this.setState({
           user: currentUser
-        }));
-      });
+        });
+      }
+    });
   }
 
   componentWillUnmount() {
-    if (this._userRequest) {
-      this._userRequest.cancel();
-    }
+    this.cancelGetCurrentUser = true;
   }
 
   onClickLogout() {
diff --git a/src/mocks/handlers.js b/src/mocks/handlers.js
index 3ceb2fb4598a9be38a31ae04257bd49c84c62cd3..aeb98a0a1bc27e8b82988d9469316c9881bf0830 100644
--- a/src/mocks/handlers.js
+++ b/src/mocks/handlers.js
@@ -5,6 +5,9 @@ import endpoints from '../services/proxy/data/endpoints';
 import MockExperiments from './mock_experiments.json';
 import MockAvailableServers from './mock_available-servers.json';
 import MockSimulationResources from './mock_simulation-resources.json';
+import MockServerConfig from './mock_server-config.json';
+import MockUser from './mock_user.json';
+import MockSimulations from './mock_simulations.json';
 
 import ImageAI from '../assets/images/Artificial_Intelligence_2.jpg';
 
@@ -13,14 +16,10 @@ const experiments = MockExperiments;
 
 export const handlers = [
   rest.get(`${config.api.proxy.url}${endpoints.proxy.storage.experiments.url}`, (req, res, ctx) => {
-    return res(
-      ctx.json(experiments)
-    );
+    return res(ctx.json(experiments));
   }),
   rest.get(`${config.api.proxy.url}${endpoints.proxy.availableServers.url}`, (req, res, ctx) => {
-    return res(
-      ctx.json(availableServers)
-    );
+    return res(ctx.json(availableServers));
   }),
   rest.get(`${config.api.proxy.url}${endpoints.proxy.storage.url}/:experimentName/:thumbnailFilename`,
     (req, res, ctx) => {
@@ -35,5 +34,14 @@ export const handlers = [
     else {
       throw new Error('Simulation resource error');
     }
+  }),
+  rest.get('http://:serverIP/simulation/', (req, res, ctx) => {
+    return res(ctx.json(MockSimulations[0]));
+  }),
+  rest.get(`${config.api.proxy.url}${endpoints.proxy.server.url}/:serverID`, (req, res, ctx) => {
+    return res(ctx.json(MockServerConfig));
+  }),
+  rest.get(`${config.api.proxy.url}${endpoints.proxy.identity.me.url}`, (req, res, ctx) => {
+    return res(ctx.json(MockUser));
   })
 ];
\ No newline at end of file
diff --git a/src/mocks/mock_available-servers.json b/src/mocks/mock_available-servers.json
index 070ca4fb03ff24725e274e4167749663dd85bdd0..eb2935a8de8620279047a52a5283da502b534a0e 100644
--- a/src/mocks/mock_available-servers.json
+++ b/src/mocks/mock_available-servers.json
@@ -25,6 +25,6 @@
             "websocket": "ws://1.2.3.4:8080/rosbridge"
         },
         "serverJobLocation": "1.2.3.4",
-        "id": "1.2.3.4:8080"
+        "id": "1.2.3.4-port-8080"
     }
 ]
\ No newline at end of file
diff --git a/src/mocks/mock_server-config.json b/src/mocks/mock_server-config.json
new file mode 100644
index 0000000000000000000000000000000000000000..e19865ef1cd727f4305a7fc52aec77858a90da4e
--- /dev/null
+++ b/src/mocks/mock_server-config.json
@@ -0,0 +1,14 @@
+{
+    "internalIp": "http://localhost:8080",
+    "gzweb": {
+        "assets": "http://localhost:8080/assets",
+        "nrp-services": "http://localhost:8080",
+        "videoStreaming": "http://localhost:8080/webstream/",
+        "websocket": "ws://localhost:8080/gzbridge"
+    },
+    "rosbridge": {
+        "websocket": "ws://localhost:8080/rosbridge"
+    },
+    "serverJobLocation": "local",
+    "id": "localhost"
+}
\ No newline at end of file
diff --git a/src/mocks/mock_user.json b/src/mocks/mock_user.json
new file mode 100644
index 0000000000000000000000000000000000000000..8225c18715fefa7b3d216274a67c9bdb662b59f0
--- /dev/null
+++ b/src/mocks/mock_user.json
@@ -0,0 +1,4 @@
+{
+    "id": "nrpuser",
+    "displayName": "nrpuser"
+}
\ No newline at end of file
diff --git a/src/services/experiments/execution/__tests__/experiment-execution-service.test.js b/src/services/experiments/execution/__tests__/experiment-execution-service.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..d49c61dc6fec8ca5d196347694d6730376526c19
--- /dev/null
+++ b/src/services/experiments/execution/__tests__/experiment-execution-service.test.js
@@ -0,0 +1,166 @@
+/**
+ * @jest-environment jsdom
+*/
+import '@testing-library/jest-dom';
+import 'jest-fetch-mock';
+
+import MockExperiments from '../../../../mocks/mock_experiments.json';
+import MockAvailableServers from '../../../../mocks/mock_available-servers.json';
+import MockServerConfig from '../../../../mocks/mock_server-config.json';
+import MockSimulations from '../../../../mocks/mock_simulations.json';
+
+import ExperimentExecutionService from '../../../../services/experiments/execution/experiment-execution-service';
+import ServerResourcesService from '../../../../services/experiments/execution/server-resources-service';
+import SimulationService from '../../../../services/experiments/execution/simulation-service';
+
+//jest.setTimeout(10000);
+
+afterEach(() => {
+  jest.restoreAllMocks();
+});
+
+test('makes sure that invoking the constructor fails with the right message', () => {
+  expect(() => {
+    new ExperimentExecutionService();
+  }).toThrow(Error);
+  expect(() => {
+    new ExperimentExecutionService();
+  }).toThrowError(Error('Use ExperimentExecutionService.instance'));
+});
+
+test('the service instance always refers to the same object', () => {
+  const instance1 = ExperimentExecutionService.instance;
+  const instance2 = ExperimentExecutionService.instance;
+  expect(instance1).toBe(instance2);
+});
+
+test('should emit an event on starting an experiment', async () => {
+  jest.spyOn(ExperimentExecutionService.instance, 'launchExperimentOnServer').mockImplementation(() => {
+    return Promise.resolve();
+  });
+  let experiment = MockExperiments[0];
+
+  let confirmStartingExperiment = (startingExperiment) => {
+    expect(startingExperiment).toEqual(experiment);
+  };
+  ExperimentExecutionService.instance.addListener(
+    ExperimentExecutionService.EVENTS.START_EXPERIMENT,
+    confirmStartingExperiment
+  );
+  await ExperimentExecutionService.instance.startNewExperiment(experiment);
+  ExperimentExecutionService.instance.removeListener(
+    ExperimentExecutionService.EVENTS.START_EXPERIMENT,
+    confirmStartingExperiment
+  );
+});
+
+test('should go through the list of available servers when trying to start an experiment', (done) => {
+  jest.spyOn(console, 'error').mockImplementation();
+  jest.spyOn(ServerResourcesService.instance, 'getServerConfig');
+  jest.spyOn(ExperimentExecutionService.instance, 'launchExperimentOnServer').mockImplementation(
+    // only the last server in the list will return a successful launch
+    (expID, isPrivate, numBrainProc, serverID) => {
+      if (serverID !== MockAvailableServers[MockAvailableServers.length - 1].id) {
+        return Promise.reject({
+          error: {
+            data: 'test rejection for launch on server ' + serverID
+          }
+        });
+      }
+
+      return Promise.resolve();
+    }
+  );
+
+  let experiment = MockExperiments[0];
+  ExperimentExecutionService.instance.startNewExperiment(experiment).then(() => {
+    MockAvailableServers.forEach(server => {
+      expect(ServerResourcesService.instance.getServerConfig).toHaveBeenCalledWith(server.id);
+    });
+    expect(console.error).toHaveBeenCalled();
+    done();
+  });
+});
+
+test('starting an experiment should abort early if a fatal error occurs', (done) => {
+  jest.spyOn(ExperimentExecutionService.instance, 'launchExperimentOnServer').mockImplementation(
+    () => {
+      return Promise.reject({
+        isFatal: true
+      });
+    }
+  );
+
+  let experiment = MockExperiments[0];
+  ExperimentExecutionService.instance.startNewExperiment(experiment).catch(error => {
+    expect(error).toEqual(ExperimentExecutionService.ERRORS.LAUNCH_FATAL_ERROR);
+    done();
+  });
+});
+
+test('starting an experiment should fail if no server is ready', (done) => {
+  jest.spyOn(ExperimentExecutionService.instance, 'launchExperimentOnServer').mockImplementation(
+    () => {
+      return Promise.reject({});
+    }
+  );
+
+  let experiment = MockExperiments[0];
+  ExperimentExecutionService.instance.startNewExperiment(experiment).catch(error => {
+    expect(error).toEqual(ExperimentExecutionService.ERRORS.LAUNCH_NO_SERVERS_LEFT);
+    done();
+  });
+});
+
+test('respects settings for specific dev server to launch and single brain process mode', async () => {
+  jest.spyOn(ExperimentExecutionService.instance, 'launchExperimentOnServer').mockImplementation(() => {
+    return Promise.resolve();
+  });
+
+  let mockExperiment = {
+    id: 'test-experiment-id',
+    devServer: 'test-dev-server-url'
+  };
+  await ExperimentExecutionService.instance.startNewExperiment(mockExperiment, true);
+  expect(ExperimentExecutionService.instance.launchExperimentOnServer).toHaveBeenCalledWith(
+    mockExperiment.id,
+    undefined,
+    1,
+    mockExperiment.devServer,
+    expect.any(Object),
+    undefined,
+    undefined,
+    undefined,
+    expect.any(Function)
+  );
+});
+
+test('can launch an experiment given a specific server + configuration', async () => {
+  jest.spyOn(ExperimentExecutionService.instance, 'httpRequestPOST').mockImplementation();
+  jest.spyOn(SimulationService.instance, 'registerForRosStatusInformation').mockImplementation();
+  jest.spyOn(SimulationService.instance, 'simulationReady').mockImplementation(() => {
+    return Promise.resolve(MockSimulations[0]);
+  });
+  jest.spyOn(SimulationService.instance, 'initConfigFiles').mockImplementation(() => {
+    return Promise.resolve();
+  });
+
+  let experimentID = 'test-experiment-id';
+  let privateExperiment = true;
+  let brainProcesses = 2;
+  let serverID = 'test-server-id';
+  let serverConfiguration = MockServerConfig;
+  let reservation = {};
+  let playbackRecording = {};
+  let profiler = {};
+  let progressCallback = jest.fn();
+  let callParams = [experimentID, privateExperiment, brainProcesses, serverID, serverConfiguration, reservation,
+    playbackRecording, profiler, progressCallback];
+
+  let result = await ExperimentExecutionService.instance.launchExperimentOnServer(...callParams);
+  expect(ExperimentExecutionService.instance.httpRequestPOST)
+    .toHaveBeenLastCalledWith(serverConfiguration.gzweb['nrp-services'] + '/simulation', expect.any(String));
+  expect(progressCallback).toHaveBeenCalled();
+  expect(result).toBe('esv-private/experiment-view/' + serverID + '/' + experimentID + '/' +
+  privateExperiment + '/' + MockSimulations[0].simulationID);
+});
diff --git a/src/services/experiments/execution/__tests__/server-resources-service.test.js b/src/services/experiments/execution/__tests__/server-resources-service.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..2345da7fc8b4b3b23b967e6640d0142d8e4a7287
--- /dev/null
+++ b/src/services/experiments/execution/__tests__/server-resources-service.test.js
@@ -0,0 +1,82 @@
+/**
+ * @jest-environment jsdom
+*/
+import '@testing-library/jest-dom';
+import 'jest-fetch-mock';
+
+import MockServerconfig from '../../../../mocks/mock_server-config.json';
+
+import ServerResourcesService from '../../../../services/experiments/execution/server-resources-service';
+import ErrorHandlerService from '../../../error-handler-service';
+
+jest.setTimeout(10000);
+
+let onWindowBeforeUnloadCb = undefined;
+beforeEach(() => {
+  jest.spyOn(window, 'addEventListener').mockImplementation((event, cb) => {
+    if (event === 'beforeunload') {
+      onWindowBeforeUnloadCb = cb;
+    }
+  });
+});
+
+afterEach(() => {
+  jest.restoreAllMocks();
+});
+
+test('makes sure that invoking the constructor fails with the right message', () => {
+  expect(() => {
+    new ServerResourcesService();
+  }).toThrow(Error);
+  expect(() => {
+    new ServerResourcesService();
+  }).toThrowError(Error('Use ServerResourcesService.instance'));
+});
+
+test('the service instance always refers to the same object', () => {
+  const instance1 = ServerResourcesService.instance;
+  const instance2 = ServerResourcesService.instance;
+  expect(instance1).toBe(instance2);
+});
+
+test('does automatic poll updates for server availability', (done) => {
+  jest.spyOn(ServerResourcesService.instance, 'getServerAvailability');
+
+  // check that getExperiments is periodically called after poll interval
+  let numCallsServerAvailabilityT0 = ServerResourcesService.instance.getServerAvailability.mock.calls.length;
+  setTimeout(() => {
+    let numCallsServerAvailabilityT1 = ServerResourcesService.instance.getServerAvailability.mock.calls.length;
+    expect(numCallsServerAvailabilityT1 > numCallsServerAvailabilityT0).toBe(true);
+
+    // stop updates and check that no more calls occur after poll interval
+    ServerResourcesService.instance.stopUpdates();
+    setTimeout(() => {
+      let numCallsServerAvailabilityT2 = ServerResourcesService.instance.getServerAvailability.mock.calls.length;
+      expect(numCallsServerAvailabilityT2 === numCallsServerAvailabilityT1).toBe(true);
+      done();
+    }, ServerResourcesService.CONSTANTS.INTERVAL_POLL_SERVER_AVAILABILITY);
+  }, ServerResourcesService.CONSTANTS.INTERVAL_POLL_SERVER_AVAILABILITY);
+});
+
+test('can get a server config', async () => {
+  // regular call with proper json
+  let config = await ServerResourcesService.instance.getServerConfig('test-server-id');
+  expect(config).toEqual(MockServerconfig);
+
+  // rejected promise on GET
+  jest.spyOn(ServerResourcesService.instance, 'httpRequestGET').mockImplementation(() => {
+    return Promise.reject();
+  });
+  jest.spyOn(ErrorHandlerService.instance, 'displayServerHTTPError').mockImplementation();
+  config = await ServerResourcesService.instance.getServerConfig('test-server-id');
+  expect(ErrorHandlerService.instance.displayServerHTTPError).toHaveBeenCalled();
+});
+
+test('should stop polling updates when window is unloaded', async () => {
+  let service = ServerResourcesService.instance;
+  expect(onWindowBeforeUnloadCb).toBeDefined();
+
+  jest.spyOn(service, 'stopUpdates');
+  onWindowBeforeUnloadCb({});
+  expect(service.stopUpdates).toHaveBeenCalled();
+});
diff --git a/src/services/experiments/execution/experiment-execution-service.js b/src/services/experiments/execution/experiment-execution-service.js
index 2ec73f548d385d9f06dd5d5de9476bb08337fd7e..193982e2112764b60bd9eebd4b7c66b6aa076499 100644
--- a/src/services/experiments/execution/experiment-execution-service.js
+++ b/src/services/experiments/execution/experiment-execution-service.js
@@ -1,6 +1,6 @@
 import _ from 'lodash';
 
-import NrpAnalyticsService from '../../nrp-analytics-service.js';
+//import NrpAnalyticsService from '../../nrp-analytics-service.js';
 import ServerResourcesService from './server-resources-service.js';
 import SimulationService from './simulation-service.js';
 import { HttpService } from '../../http-service.js';
@@ -39,22 +39,23 @@ class ExperimentExecutionService extends HttpService {
    * @param {object} playbackRecording - a recording of a previous execution
    * @param {*} profiler - a profiler option
    */
-  startNewExperiment(
+  async startNewExperiment(
     experiment,
     launchSingleMode,
     reservation,
     playbackRecording,
     profiler
   ) {
-    NrpAnalyticsService.instance.eventTrack('Start', { category: 'Experiment' });
-    NrpAnalyticsService.instance.tickDurationEvent('Server-initialization');
+    //TODO: implement NrpAnalyticsService functionality
+    //NrpAnalyticsService.instance.eventTrack('Start', { category: 'Experiment' });
+    //NrpAnalyticsService.instance.tickDurationEvent('Server-initialization');
 
     ExperimentExecutionService.instance.emit(ExperimentExecutionService.EVENTS.START_EXPERIMENT, experiment);
 
     let fatalErrorOccurred = false;
     let serversToTry = experiment.devServer
       ? [experiment.devServer]
-      : ServerResourcesService.instance.getServerAvailability(true).map(s => s.id);
+      : (await ServerResourcesService.instance.getServerAvailability(true)).map(s => s.id);
 
     let brainProcesses = launchSingleMode ? 1 : experiment.configuration.brainProcesses;
 
@@ -64,20 +65,23 @@ class ExperimentExecutionService extends HttpService {
     };
 
     let launchOnNextServer = async () => {
-      let nextServer = serversToTry.splice(0, 1);
-      if (fatalErrorOccurred || !nextServer.length) {
-        //no more servers to retry, we have failed to start experiment
-        return Promise.reject(fatalErrorOccurred);
+      if (!serversToTry.length) {
+        //TODO: GUI feedback
+        return Promise.reject(ExperimentExecutionService.ERRORS.LAUNCH_NO_SERVERS_LEFT);
+      }
+      if (fatalErrorOccurred) {
+        //TODO: GUI feedback
+        return Promise.reject(ExperimentExecutionService.ERRORS.LAUNCH_FATAL_ERROR);
       }
 
-      let server = nextServer[0];
-      let serverConfig = await ServerResourcesService.instance.getServerConfig(server);
+      let serverID = serversToTry.splice(0, 1)[0];
+      let serverConfig = await ServerResourcesService.instance.getServerConfig(serverID);
 
       return await this.launchExperimentOnServer(
         experiment.id,
         experiment.private,
         brainProcesses,
-        server,
+        serverID,
         serverConfig,
         reservation,
         playbackRecording,
@@ -85,6 +89,7 @@ class ExperimentExecutionService extends HttpService {
         progressCallback
       ).catch((failure) => {
         if (failure.error && failure.error.data) {
+          //TODO: proper ErrorHandlerService callback
           console.error('Failed to start simulation: ' + JSON.stringify(failure.error.data));
         }
         fatalErrorOccurred = fatalErrorOccurred || failure.isFatal;
@@ -101,7 +106,7 @@ class ExperimentExecutionService extends HttpService {
    * @param {string} experimentID - ID of the experiment to launch
    * @param {boolean} privateExperiment - whether the experiment is private or not
    * @param {number} brainProcesses - number of brain processes to start with
-   * @param {string} server - server ID
+   * @param {string} serverID - server ID
    * @param {object} serverConfiguration - configuration of server
    * @param {object} reservation - server reservation
    * @param {object} playbackRecording - recording
@@ -112,7 +117,7 @@ class ExperimentExecutionService extends HttpService {
     experimentID,
     privateExperiment,
     brainProcesses,
-    server,
+    serverID,
     serverConfiguration,
     reservation,
     playbackRecording,
@@ -158,7 +163,7 @@ class ExperimentExecutionService extends HttpService {
         .then((simulation) => {
           SimulationService.instance.initConfigFiles(serverURL, simulation.simulationID)
             .then(() => {
-              let simulationURL = 'esv-private/experiment-view/' + server + '/' + experimentID + '/' +
+              let simulationURL = 'esv-private/experiment-view/' + serverID + '/' + experimentID + '/' +
                 privateExperiment + '/' + simulation.simulationID;
               resolve(simulationURL);
               ExperimentExecutionService.instance.emit(ExperimentExecutionService.EVENTS.START_EXPERIMENT, undefined);
@@ -205,18 +210,34 @@ class ExperimentExecutionService extends HttpService {
               if (!data || !data.state) {
                 return Promise.reject();
               }
-              switch (data.state) {
-              case EXPERIMENT_STATE.CREATED: //CREATED --(initialize)--> PAUSED --(stop)--> STOPPED
+
+              // CREATED --(initialize)--> PAUSED --(stop)--> STOPPED
+              if (data.state === EXPERIMENT_STATE.CREATED) {
                 return updateSimulationState(EXPERIMENT_STATE.INITIALIZED).then(
                   _.partial(updateSimulationState, EXPERIMENT_STATE.STOPPED)
                 );
-              case EXPERIMENT_STATE.STARTED: //STARTED --(stop)--> STOPPED
-              case EXPERIMENT_STATE.PAUSED: //PAUSED  --(stop)--> STOPPED
-              case EXPERIMENT_STATE.HALTED: //HALTED  --(stop)--> FAILED
+              }
+              // STARTED/PAUSED/HALTED --(stop)--> STOPPED
+              else if (data.state === EXPERIMENT_STATE.STARTED ||
+                data.state === EXPERIMENT_STATE.PAUSED ||
+                data.state === EXPERIMENT_STATE.HALTED) {
                 return updateSimulationState(EXPERIMENT_STATE.STOPPED);
-              default:
-                return Promise.reject();
               }
+
+              return Promise.reject();
+
+              /*switch (data.state) {
+                case EXPERIMENT_STATE.CREATED: //CREATED --(initialize)--> PAUSED --(stop)--> STOPPED
+                  return updateSimulationState(EXPERIMENT_STATE.INITIALIZED).then(
+                    _.partial(updateSimulationState, EXPERIMENT_STATE.STOPPED)
+                  );
+                case EXPERIMENT_STATE.STARTED: //STARTED --(stop)--> STOPPED
+                case EXPERIMENT_STATE.PAUSED: //PAUSED  --(stop)--> STOPPED
+                case EXPERIMENT_STATE.HALTED: //HALTED  --(stop)--> FAILED
+                  return updateSimulationState(EXPERIMENT_STATE.STOPPED);
+                default:
+                  return Promise.reject();
+              }*/
             });
           /*eslint-enable camelcase*/
         })
@@ -231,4 +252,9 @@ ExperimentExecutionService.EVENTS = Object.freeze({
   STOP_EXPERIMENT: 'STOP_EXPERIMENT'
 });
 
+ExperimentExecutionService.ERRORS = Object.freeze({
+  LAUNCH_FATAL_ERROR: 'failed to launch experiment, encountered a fatal error',
+  LAUNCH_NO_SERVERS_LEFT: 'failed to launch experiment, no available server could successfully start it'
+});
+
 export default ExperimentExecutionService;
diff --git a/src/services/experiments/execution/server-resources-service.js b/src/services/experiments/execution/server-resources-service.js
index 57c2abfc9bbbc71fc64dbfd4e232233c6f203082..708a46bb5a601c105268f6b2bd4bd2e7fcb9a84e 100644
--- a/src/services/experiments/execution/server-resources-service.js
+++ b/src/services/experiments/execution/server-resources-service.js
@@ -1,23 +1,14 @@
-import _ from 'lodash';
-import { Subject, timer } from 'rxjs';
-import { switchMap, filter, map, multicast } from 'rxjs/operators';
-
 import ErrorHandlerService from '../../error-handler-service.js';
 import { HttpService } from '../../http-service.js';
 
 import endpoints from '../../proxy/data/endpoints.json';
 import config from '../../../config.json';
 const proxyServerURL = `${config.api.proxy.url}${endpoints.proxy.server.url}`;
-const slurmMonitorURL = `${config.api.slurmmonitor.url}/api/v1/partitions/interactive`;
 const availableServersURL = `${config.api.proxy.url}${endpoints.proxy.availableServers.url}`;
 
 let _instance = null;
 const SINGLETON_ENFORCER = Symbol();
 
-const INTERVAL_POLL_SLURM_MONITOR = 5000;
-const INTERVAL_POLL_SERVER_AVAILABILITY = 3000;
-let clusterAvailability = { free: 'N/A', total: 'N/A' };
-
 /**
  * Service handling server resources for simulating experiments.
  */
@@ -31,9 +22,10 @@ class ServerResourcesService extends HttpService {
     this.availableServers = [];
 
     this.startUpdates();
-    window.onbeforeunload = () => {
+    window.addEventListener('beforeunload', (event) => {
       this.stopUpdates();
-    };
+      event.returnValue = '';
+    });
   }
 
   static get instance() {
@@ -48,17 +40,11 @@ class ServerResourcesService extends HttpService {
    * Start polling updates.
    */
   startUpdates() {
-    this.clusterAvailabilityObservable = this._createSlurmMonitorObservable();
-    this.clusterAvailabilitySubscription = this.clusterAvailabilityObservable.subscribe(
-      availability => (clusterAvailability = availability)
-    );
-
-    this.getServerAvailability(true);
     this.intervalGetServerAvailability = setInterval(
       () => {
         this.getServerAvailability(true);
       },
-      INTERVAL_POLL_SERVER_AVAILABILITY
+      ServerResourcesService.CONSTANTS.INTERVAL_POLL_SERVER_AVAILABILITY
     );
   }
 
@@ -66,30 +52,17 @@ class ServerResourcesService extends HttpService {
    * Stop polling updates.
    */
   stopUpdates() {
-    this.clusterAvailabilitySubscription && this.clusterAvailabilitySubscription.unsubscribe();
     this.intervalGetServerAvailability && clearInterval(this.intervalGetServerAvailability);
   }
 
-  /**
-   * Get available cluster server info.
-   * @returns {object} cluster availability info
-   */
-  getClusterAvailability() {
-    return clusterAvailability;
-  }
-
   /**
    * Return a list of available servers for starting simulations.
    * @param {boolean} forceUpdate force an update
    * @returns {Array} A list of available servers.
    */
-  getServerAvailability(forceUpdate = false) {
+  async getServerAvailability(forceUpdate = false) {
     if (!this.availableServers || forceUpdate) {
-      let update = async () => {
-        let response = await this.httpRequestGET(availableServersURL);
-        this.availableServers = await response.json();
-      };
-      update();
+      this.availableServers = await (await this.httpRequestGET(availableServersURL)).json();
       this.emit(ServerResourcesService.EVENTS.UPDATE_SERVER_AVAILABILITY, this.availableServers);
     }
 
@@ -108,32 +81,14 @@ class ServerResourcesService extends HttpService {
       })
       .catch(ErrorHandlerService.instance.displayServerHTTPError);
   }
-
-  _createSlurmMonitorObservable() {
-    return timer(0, INTERVAL_POLL_SLURM_MONITOR)
-      .pipe(switchMap(() => {
-        try {
-          return this.httpRequestGET(slurmMonitorURL);
-        }
-        catch (error) {
-          _.once(error => {
-            if (error.status === -1) {
-              error = Object.assign(error, {
-                data: 'Could not probe vizualization cluster'
-              });
-            }
-            ErrorHandlerService.instance.displayServerHTTPError(error);
-          });
-        }
-      }))
-      .pipe(filter(e => e))
-      .pipe(map(({ free, nodes }) => ({ free, total: nodes[3] })))
-      .pipe(multicast(new Subject())).refCount();
-  }
 }
 
 ServerResourcesService.EVENTS = Object.freeze({
   UPDATE_SERVER_AVAILABILITY: 'UPDATE_SERVER_AVAILABILITY'
 });
 
+ServerResourcesService.CONSTANTS = Object.freeze({
+  INTERVAL_POLL_SERVER_AVAILABILITY: 3000
+});
+
 export default ServerResourcesService;
diff --git a/src/services/experiments/storage/__tests__/experiment-storage-service.test.js b/src/services/experiments/storage/__tests__/experiment-storage-service.test.js
index 2176a8240e126d527d0eb0804fcc888ea0f85f80..c87014c4bb8018075239b6b6a867f9b6a55a6cf1 100644
--- a/src/services/experiments/storage/__tests__/experiment-storage-service.test.js
+++ b/src/services/experiments/storage/__tests__/experiment-storage-service.test.js
@@ -15,6 +15,15 @@ const experimentsUrl = `${config.api.proxy.url}${proxyEndpoint.storage.experimen
 
 jest.setTimeout(3 * ExperimentStorageService.CONSTANTS.INTERVAL_POLL_EXPERIMENTS);
 
+let onWindowBeforeUnloadCb = undefined;
+beforeEach(() => {
+  jest.spyOn(window, 'addEventListener').mockImplementation((event, cb) => {
+    if (event === 'beforeunload') {
+      onWindowBeforeUnloadCb = cb;
+    }
+  });
+});
+
 afterEach(() => {
   jest.restoreAllMocks();
 });
@@ -82,6 +91,15 @@ test('does automatic poll updates of experiment list which can be stopped', (don
   }, ExperimentStorageService.CONSTANTS.INTERVAL_POLL_EXPERIMENTS);
 });
 
+test('should stop polling updates when window is unloaded', async () => {
+  let service = ExperimentStorageService.instance;
+  expect(onWindowBeforeUnloadCb).toBeDefined();
+
+  jest.spyOn(service, 'stopUpdates');
+  onWindowBeforeUnloadCb({});
+  expect(service.stopUpdates).toHaveBeenCalled();
+});
+
 test('gets a thumbnail image for experiments', async () => {
   let experiment = MockExperiments[0];
   const imageBlob = await ExperimentStorageService.instance.getThumbnail(experiment.name,
diff --git a/src/services/experiments/storage/experiment-storage-service.js b/src/services/experiments/storage/experiment-storage-service.js
index a0f0167b666cffd03fa26f06a843575d40d89400..bb37f6ebb7f22de2125c5b30a98f08b7db92b69d 100644
--- a/src/services/experiments/storage/experiment-storage-service.js
+++ b/src/services/experiments/storage/experiment-storage-service.js
@@ -12,8 +12,6 @@ const storageImportExperiment = `${config.api.proxy.url}${endpoints.proxy.storag
 let _instance = null;
 const SINGLETON_ENFORCER = Symbol();
 
-const INTERVAL_POLL_EXPERIMENTS = 3000;
-
 /**
  * Service that fetches the template experiments list from the proxy given
  * that the user has authenticated successfully.
@@ -49,7 +47,7 @@ class ExperimentStorageService extends HttpService {
       () => {
         this.getExperiments(true);
       },
-      INTERVAL_POLL_EXPERIMENTS
+      ExperimentStorageService.CONSTANTS.INTERVAL_POLL_EXPERIMENTS
     );
   }
 
@@ -268,7 +266,7 @@ ExperimentStorageService.EVENTS = Object.freeze({
 });
 
 ExperimentStorageService.CONSTANTS = Object.freeze({
-  INTERVAL_POLL_EXPERIMENTS: INTERVAL_POLL_EXPERIMENTS
+  INTERVAL_POLL_EXPERIMENTS: 3000
 });
 
 export default ExperimentStorageService;
diff --git a/src/services/nrp-analytics-service.js b/src/services/nrp-analytics-service.js
index f00751e65afa39473be2db648291f5b587d92168..2b3c3af8ecf117d18f698335f798be789542666e 100644
--- a/src/services/nrp-analytics-service.js
+++ b/src/services/nrp-analytics-service.js
@@ -30,10 +30,10 @@ class NrpAnalyticsService {
       options.value = _.toInteger(options.value);
     }
     return NrpUserService.instance.getCurrentUser().then((user) => {
-      var extendedOptions = _.extend(options, {
+      /*var extendedOptions = _.extend(options, {
         label: user.displayName
       });
-      //$analytics.eventTrack(actionName, extendedOptions);
+      $analytics.eventTrack(actionName, extendedOptions);*/
       console.error('implement $analytics.eventTrack(actionName, extendedOptions)');
     });
   }
diff --git a/src/services/proxy/nrp-user-service.js b/src/services/proxy/nrp-user-service.js
index e528d349d9654390d0f9f376d2eb24494a0c3f0b..a81f2e76aada290798ffa98d7f42e49d96102e87 100644
--- a/src/services/proxy/nrp-user-service.js
+++ b/src/services/proxy/nrp-user-service.js
@@ -40,8 +40,7 @@ class NrpUserService extends HttpService {
    * @returns {promise} Request for the user
    */
   async getUser(userID) {
-    let response = await this.httpRequestGET(this.IDENTITY_BASE_URL + '/' + userID);
-    return response.json();
+    return await (await this.httpRequestGET(this.IDENTITY_BASE_URL + '/' + userID)).json();
   }
 
   /**
@@ -62,8 +61,7 @@ class NrpUserService extends HttpService {
    */
   async getCurrentUser() {
     if (!this.currentUser) {
-      let response = await this.httpRequestGET(this.IDENTITY_ME_URL);
-      this.currentUser = response.json();
+      this.currentUser = await (await this.httpRequestGET(this.IDENTITY_ME_URL)).json();
     }
 
     return this.currentUser;