summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkelvinp <kelvinp@chromium.org>2015-08-26 17:27:21 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-27 00:27:51 +0000
commit67628891dda6f69663fa152b5423d5872d88be3c (patch)
tree7d0bcebc1485378ac5cf3edec38d07b3d8123cde
parenta3a6430c4d471af3092682783ae4eed377d55d39 (diff)
downloadchromium_src-67628891dda6f69663fa152b5423d5872d88be3c.zip
chromium_src-67628891dda6f69663fa152b5423d5872d88be3c.tar.gz
chromium_src-67628891dda6f69663fa152b5423d5872d88be3c.tar.bz2
Me2Me Telemetry Integration test.
As we rely more and more on our session state changes data to determine the reliability of our connections. It is important to ensure we don't have any regressions on the telemetry events generated for a given connection. This CL implements a few integration tests for our top 7 Me2Me connection scenarios: 1. Connection Succeeded 2. Connection Failed - Signal Strategy 3. HostOffline - reconnect succeeded 4. HostOffline - reconnect failed 5. HostOffline - Jid refresh failed 6. Connection Canceled 7. Reconnect button Summary of changes: 1. Moves the creation logic of modal dialogs into a factory class so that it can be injected in test code. 2. Moves the creation of DesktopConnectedView into a factory function so that it can be overwritten in test code. 3. MockConnection injects the MockClientPlugin and the MockSignalStrategy into the ClientSessionFactory. 4. Me2MeTestDriver injects the MockConnection, MockModalDialogFactory, MockHostList and other mock objects into Me2MeActivity. BUG=521782 Review URL: https://codereview.chromium.org/1301373002 Cr-Commit-Position: refs/heads/master@{#345744}
-rw-r--r--chrome/test/remoting/me2me_browsertest.cc10
-rw-r--r--remoting/remoting_webapp_files.gypi3
-rw-r--r--remoting/webapp/base/js/client_session_factory_unittest.js6
-rw-r--r--remoting/webapp/base/js/client_session_unittest.js46
-rw-r--r--remoting/webapp/base/js/modal_dialogs.js52
-rw-r--r--remoting/webapp/browser_test/bump_scroll_browser_test.js8
-rw-r--r--remoting/webapp/browser_test/me2me_browser_test.js46
-rw-r--r--remoting/webapp/crd/js/desktop_connected_view.js12
-rw-r--r--remoting/webapp/crd/js/desktop_remoting_activity.js6
-rw-r--r--remoting/webapp/crd/js/it2me_activity.js2
-rw-r--r--remoting/webapp/crd/js/me2me_activity.js12
-rw-r--r--remoting/webapp/crd/js/me2me_telemetry_integration_test.js385
-rw-r--r--remoting/webapp/crd/js/mock_client_plugin.js101
-rw-r--r--remoting/webapp/crd/js/mock_modal_dialog_factory.js95
-rw-r--r--remoting/webapp/crd/js/remoting_activity_test_driver.js238
-rw-r--r--remoting/webapp/js_proto/sinon_proto.js5
16 files changed, 929 insertions, 98 deletions
diff --git a/chrome/test/remoting/me2me_browsertest.cc b/chrome/test/remoting/me2me_browsertest.cc
index 99f989d..3c3a031 100644
--- a/chrome/test/remoting/me2me_browsertest.cc
+++ b/chrome/test/remoting/me2me_browsertest.cc
@@ -135,16 +135,6 @@ IN_PROC_BROWSER_TEST_F(Me2MeBrowserTest, MANUAL_Me2Me_v2_Alive_OnLostFocus) {
Cleanup();
}
-IN_PROC_BROWSER_TEST_F(Me2MeBrowserTest, MANUAL_Me2Me_RetryOnHostOffline) {
- content::WebContents* content = SetUpTest();
- LoadScript(content, FILE_PATH_LITERAL("me2me_browser_test.js"));
- RunJavaScriptTest(content, "RetryOnHostOffline", "{"
- "pin: '" + me2me_pin() + "'"
- "}");
-
- Cleanup();
-}
-
IN_PROC_BROWSER_TEST_F(Me2MeBrowserTest,
MANUAL_Me2Me_Disable_Remote_Connection) {
SetUpTest();
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi
index d8f023d..f15f196 100644
--- a/remoting/remoting_webapp_files.gypi
+++ b/remoting/remoting_webapp_files.gypi
@@ -119,14 +119,17 @@
'webapp/crd/js/host_table_entry_unittest.js',
'webapp/crd/js/legacy_host_list_api_unittest.js',
'webapp/crd/js/menu_button_unittest.js',
+ 'webapp/crd/js/me2me_telemetry_integration_test.js',
'webapp/crd/js/mock_xhr_unittest.js',
],
'remoting_webapp_unittests_js_mock_files': [
'webapp/crd/js/mock_client_plugin.js',
'webapp/crd/js/mock_host_daemon_facade.js',
'webapp/crd/js/mock_host_list_api.js',
+ 'webapp/crd/js/mock_modal_dialog_factory.js',
'webapp/crd/js/mock_identity.js',
'webapp/crd/js/mock_signal_strategy.js',
+ 'webapp/crd/js/remoting_activity_test_driver.js',
'webapp/js_proto/chrome_mocks.js',
'webapp/unittests/sinon_helpers.js',
'webapp/crd/js/mock_xhr.js',
diff --git a/remoting/webapp/base/js/client_session_factory_unittest.js b/remoting/webapp/base/js/client_session_factory_unittest.js
index 93e6982..cd53d1c 100644
--- a/remoting/webapp/base/js/client_session_factory_unittest.js
+++ b/remoting/webapp/base/js/client_session_factory_unittest.js
@@ -79,8 +79,10 @@ QUnit.test('createSession() should reject on signal strategy failure',
QUnit.test('createSession() should reject on plugin initialization failure',
function(assert) {
var mockSignalStrategy = mockConnection.signalStrategy();
- var plugin = mockConnection.plugin();
- plugin.mock$initializationResult = false;
+ function onPluginCreated(/** remoting.MockClientPlugin */ plugin) {
+ plugin.mock$initializationResult = false;
+ }
+ mockConnection.pluginFactory().mock$setPluginCreated(onPluginCreated);
var signalStrategyDispose = sinon.stub(mockSignalStrategy, 'dispose');
diff --git a/remoting/webapp/base/js/client_session_unittest.js b/remoting/webapp/base/js/client_session_unittest.js
index f43a2bc..bff546c 100644
--- a/remoting/webapp/base/js/client_session_unittest.js
+++ b/remoting/webapp/base/js/client_session_unittest.js
@@ -35,19 +35,27 @@ function connect(opt_error) {
var host = new remoting.Host('fake_hostId');
host.jabberId = 'fake_jid';
- var plugin = mockConnection.plugin();
- var State = remoting.ClientSession.State;
+ function onPluginCreated(/** remoting.MockClientPlugin */ plugin) {
+ var State = remoting.ClientSession.State;
+ plugin.mock$onConnect().then(function() {
+ plugin.mock$setConnectionStatus(State.CONNECTING);
+ }).then(function() {
+ var status = (opt_error) ? State.FAILED : State.CONNECTED;
+ plugin.mock$setConnectionStatus(status, opt_error);
+ });
+ }
+ mockConnection.pluginFactory().mock$setPluginCreated(onPluginCreated);
- plugin.mock$onConnect().then(function() {
- plugin.mock$setConnectionStatus(State.CONNECTING);
- }).then(function() {
- var status = (opt_error) ? State.FAILED : State.CONNECTED;
- plugin.mock$setConnectionStatus(status, opt_error);
- });
+ var sessionFactory = new remoting.ClientSessionFactory(
+ document.createElement('div'), ['fake_capability']);
- session.connect(host, new remoting.CredentialsProvider({
- pairingInfo: { clientId: 'fake_clientId', sharedSecret: 'fake_secret' }
- }));
+ sessionFactory.createSession(listener, logger, true).then(
+ function(clientSession) {
+ session = clientSession;
+ clientSession.connect(host, new remoting.CredentialsProvider({
+ pairingInfo: { clientId: 'fake_clientId', sharedSecret: 'fake_secret' }
+ }));
+ });
listener.onConnected = function() {
deferred.resolve();
@@ -69,14 +77,6 @@ QUnit.module('ClientSession', {
logger = new remoting.SessionLogger(remoting.ChromotingEvent.Role.CLIENT,
base.doNothing);
logToServerStub = sinon.stub(logger, 'logClientSessionStateChange');
-
-
- var sessionFactory = new remoting.ClientSessionFactory(
- document.createElement('div'), ['fake_capability']);
- return sessionFactory.createSession(listener, logger, true).then(
- function(clientSession) {
- session = clientSession;
- });
},
afterEach: function() {
session.dispose();
@@ -99,8 +99,8 @@ QUnit.test('onOutgoingIq() should send Iq to signalStrategy', function(assert) {
});
QUnit.test('should foward Iq from signalStrategy to plugin', function(assert) {
- var onIncomingIq = sinon.stub(mockConnection.plugin(), 'onIncomingIq');
return connect().then(function() {
+ var onIncomingIq = sinon.stub(mockConnection.plugin(), 'onIncomingIq');
var stanza = new DOMParser()
.parseFromString('<iq>sample</iq>', 'text/xml')
.firstElementChild;
@@ -148,10 +148,4 @@ QUnit.test(
});
});
-QUnit.test('dispose() should dispose the plugin', function(assert) {
- var pluginDispose = sinon.stub(mockConnection.plugin(), 'dispose');
- session.dispose();
- assert.equal(pluginDispose.callCount, 1);
-});
-
})();
diff --git a/remoting/webapp/base/js/modal_dialogs.js b/remoting/webapp/base/js/modal_dialogs.js
index e05d226..e6b750d 100644
--- a/remoting/webapp/base/js/modal_dialogs.js
+++ b/remoting/webapp/base/js/modal_dialogs.js
@@ -288,6 +288,58 @@ remoting.ConnectingDialog.prototype.hide = function() {
this.dialog_.dispose();
};
+/**
+ * A factory object for the modal dialogs. The factory will be stubbed out in
+ * unit test to avoid UI dependencies on remoting.setMode().
+ *
+ * @constructor
+ */
+remoting.ModalDialogFactory = function() {};
+
+/**
+ * @param {Function} cancelCallback
+ * @return {remoting.ConnectingDialog}
+ */
+remoting.ModalDialogFactory.prototype.createConnectingDialog =
+ function(cancelCallback) {
+ return new remoting.ConnectingDialog(cancelCallback);
+};
+
+/**
+ * @param {remoting.Html5ModalDialog.Params} params
+ * @return {remoting.Html5ModalDialog}
+ */
+remoting.ModalDialogFactory.prototype.createHtml5ModalDialog =
+ function(params) {
+ return new remoting.Html5ModalDialog(params);
+};
+
+/**
+ * @param {remoting.AppMode} mode
+ * @param {HTMLElement} primaryButton
+ * @param {HTMLElement=} opt_secondaryButton
+ * @return {remoting.MessageDialog}
+ */
+remoting.ModalDialogFactory.prototype.createMessageDialog =
+ function(mode, primaryButton, opt_secondaryButton) {
+ return new remoting.MessageDialog(mode, primaryButton, opt_secondaryButton);
+};
+
+/**
+ * @param {remoting.AppMode} mode
+ * @param {HTMLElement} formElement
+ * @param {HTMLElement} inputField
+ * @param {HTMLElement} cancelButton
+ * @return {remoting.InputDialog}
+ */
+remoting.ModalDialogFactory.prototype.createInputDialog =
+ function(mode, formElement, inputField, cancelButton) {
+ return new remoting.InputDialog(mode, formElement, inputField, cancelButton);
+};
+
+/** @type {remoting.ModalDialogFactory} */
+remoting.modalDialogFactory = new remoting.ModalDialogFactory();
+
})();
/**
diff --git a/remoting/webapp/browser_test/bump_scroll_browser_test.js b/remoting/webapp/browser_test/bump_scroll_browser_test.js
index f59c30f..a20cd36 100644
--- a/remoting/webapp/browser_test/bump_scroll_browser_test.js
+++ b/remoting/webapp/browser_test/bump_scroll_browser_test.js
@@ -89,8 +89,12 @@ browserTest.Bump_Scroll.prototype.run = function(data) {
}
var mockConnection = new remoting.MockConnection();
- mockConnection.plugin().mock$useDefaultBehavior(
- remoting.MockClientPlugin.AuthMethod.PIN);
+
+ function onPluginCreated(/** remoting.MockClientPlugin */ plugin) {
+ plugin.mock$useDefaultBehavior(remoting.MockClientPlugin.AuthMethod.PIN);
+ }
+ mockConnection.pluginFactory().mock$setPluginCreated(onPluginCreated);
+
function cleanup() {
mockConnection.restore();
diff --git a/remoting/webapp/browser_test/me2me_browser_test.js b/remoting/webapp/browser_test/me2me_browser_test.js
index d4f0353..392d0b9 100644
--- a/remoting/webapp/browser_test/me2me_browser_test.js
+++ b/remoting/webapp/browser_test/me2me_browser_test.js
@@ -58,50 +58,4 @@ browserTest.AliveOnLostFocus.prototype.run = function(data) {
});
};
-
-/** @constructor */
-browserTest.RetryOnHostOffline = function() {
- /** @private */
- this.mockConnection_ = new remoting.MockConnection();
-
- // Fake an host offline error on first connect.
- var plugin = this.mockConnection_.plugin();
- var State = remoting.ClientSession.State;
- var Error = remoting.ClientSession.ConnectionError;
- var that = this;
-
- plugin.mock$onConnect().then(function() {
- plugin.mock$setConnectionStatus(State.CONNECTING);
- }).then(function() {
- plugin.mock$setConnectionStatus(State.FAILED, Error.HOST_IS_OFFLINE);
- }).then(function() {
- that.cleanup_();
- that.mockConnection_ = new remoting.MockConnection();
- var newPlugin = that.mockConnection_.plugin();
- // Let the second connect succeed.
- newPlugin.mock$useDefaultBehavior(remoting.MockClientPlugin.AuthMethod.PIN);
- });
-};
-
-/** @private */
-browserTest.RetryOnHostOffline.prototype.cleanup_ = function() {
- this.mockConnection_.restore();
- this.mockConnection_ = null;
-};
-
-browserTest.RetryOnHostOffline.prototype.run = function() {
- var that = this;
- browserTest.connectMe2Me().then(function() {
- return browserTest.enterPIN('123456');
- }).then(function() {
- return browserTest.disconnect();
- }).then(function() {
- that.cleanup_();
- browserTest.pass();
- }).catch(function(/** Error */ reason) {
- that.cleanup_();
- browserTest.fail(reason);
- });
-};
-
})();
diff --git a/remoting/webapp/crd/js/desktop_connected_view.js b/remoting/webapp/crd/js/desktop_connected_view.js
index ef2dae2..1f528d3 100644
--- a/remoting/webapp/crd/js/desktop_connected_view.js
+++ b/remoting/webapp/crd/js/desktop_connected_view.js
@@ -330,3 +330,15 @@ remoting.DesktopConnectedView.prototype.startStopRecording = function() {
this.videoFrameRecorder_.startStopRecording();
}
};
+
+/**
+ * Factory function so that it can be overwritten in unit test to avoid
+ * UI dependencies.
+ *
+ * @param {HTMLElement} container
+ * @param {remoting.ConnectionInfo} connectionInfo
+ * @return {remoting.DesktopConnectedView}
+ */
+remoting.DesktopConnectedView.create = function(container, connectionInfo) {
+ return new remoting.DesktopConnectedView(container, connectionInfo);
+}; \ No newline at end of file
diff --git a/remoting/webapp/crd/js/desktop_remoting_activity.js b/remoting/webapp/crd/js/desktop_remoting_activity.js
index 9e1e973..03f2ead 100644
--- a/remoting/webapp/crd/js/desktop_remoting_activity.js
+++ b/remoting/webapp/crd/js/desktop_remoting_activity.js
@@ -33,8 +33,8 @@ remoting.DesktopRemotingActivity = function(parentActivity) {
/** @private {remoting.ClientSession} */
this.session_ = null;
/** @private {remoting.ConnectingDialog} */
- this.connectingDialog_ =
- new remoting.ConnectingDialog(parentActivity.stop.bind(parentActivity));
+ this.connectingDialog_ = remoting.modalDialogFactory.createConnectingDialog(
+ parentActivity.stop.bind(parentActivity));
};
/**
@@ -90,7 +90,7 @@ remoting.DesktopRemotingActivity.prototype.onConnected =
remoting.toolbar.preview();
}
- this.connectedView_ = new remoting.DesktopConnectedView(
+ this.connectedView_ = remoting.DesktopConnectedView.create(
document.getElementById('client-container'), connectionInfo);
// Apply the default or previously-specified keyboard remapping.
diff --git a/remoting/webapp/crd/js/it2me_activity.js b/remoting/webapp/crd/js/it2me_activity.js
index 57ae172..c334ea3 100644
--- a/remoting/webapp/crd/js/it2me_activity.js
+++ b/remoting/webapp/crd/js/it2me_activity.js
@@ -26,7 +26,7 @@ remoting.It2MeActivity = function() {
var form = document.getElementById('access-code-form');
/** @private */
- this.accessCodeDialog_ = new remoting.InputDialog(
+ this.accessCodeDialog_ = remoting.modalDialogFactory.createInputDialog(
remoting.AppMode.CLIENT_UNCONNECTED,
form,
form.querySelector('#access-code-entry'),
diff --git a/remoting/webapp/crd/js/me2me_activity.js b/remoting/webapp/crd/js/me2me_activity.js
index 3aa4ad5..1141d07 100644
--- a/remoting/webapp/crd/js/me2me_activity.js
+++ b/remoting/webapp/crd/js/me2me_activity.js
@@ -195,15 +195,15 @@ remoting.Me2MeActivity.prototype.reconnectOnHostOffline_ = function(error) {
* @param {!remoting.Error} error
*/
remoting.Me2MeActivity.prototype.onConnectionFailed = function(error) {
+ base.dispose(this.desktopActivity_);
+ this.desktopActivity_ = null;
+
if (error.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) &&
this.retryOnHostOffline_) {
this.reconnectOnHostOffline_(error);
} else if (!error.isNone()) {
this.showErrorMessage_(error);
}
-
- base.dispose(this.desktopActivity_);
- this.desktopActivity_ = null;
};
/**
@@ -257,7 +257,7 @@ remoting.Me2MeActivity.prototype.showErrorMessage_ = function(error) {
* @private
*/
remoting.Me2MeActivity.prototype.showFinishDialog_ = function(mode) {
- var dialog = new remoting.MessageDialog(
+ var dialog = remoting.modalDialogFactory.createMessageDialog(
mode,
document.getElementById('client-finished-me2me-button'),
document.getElementById('client-reconnect-button'));
@@ -285,7 +285,7 @@ remoting.HostNeedsUpdateDialog = function(rootElement, host) {
/** @private */
this.host_ = host;
/** @private */
- this.dialog_ = new remoting.MessageDialog(
+ this.dialog_ = remoting.modalDialogFactory.createMessageDialog(
remoting.AppMode.CLIENT_HOST_NEEDS_UPGRADE,
rootElement.querySelector('.connect-button'),
rootElement.querySelector('.cancel-button'));
@@ -333,7 +333,7 @@ remoting.PinDialog = function(rootElement, host) {
/** @private */
this.host_ = host;
/** @private */
- this.dialog_ = new remoting.InputDialog(
+ this.dialog_ = remoting.modalDialogFactory.createInputDialog(
remoting.AppMode.CLIENT_PIN_PROMPT,
this.rootElement_.querySelector('form'),
this.pinInput_,
diff --git a/remoting/webapp/crd/js/me2me_telemetry_integration_test.js b/remoting/webapp/crd/js/me2me_telemetry_integration_test.js
new file mode 100644
index 0000000..1ec6a86
--- /dev/null
+++ b/remoting/webapp/crd/js/me2me_telemetry_integration_test.js
@@ -0,0 +1,385 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+(function() {
+
+'use strict';
+
+/** @type {remoting.Me2MeTestDriver} */
+var testDriver;
+
+QUnit.module('Me2Me Telemetry Integration', {
+ // We rely on our telemetry data to gain insight into the reliability and
+ // the durability on our connections. This test verifies the integrity of our
+ // telemetry by ensuring that certain well known connection sequences will
+ // generate the correct sequence of telemetry data.
+ beforeEach: function() {
+ testDriver = new remoting.Me2MeTestDriver();
+ },
+ afterEach: function() {
+ base.dispose(testDriver);
+ testDriver = null;
+ }
+});
+
+/**
+ * @param {remoting.Me2MeTestDriver} testDriver
+ * @param {Object} baseEvent
+ */
+var expectSucceeded = function(testDriver, baseEvent) {
+ var ChromotingEvent = remoting.ChromotingEvent;
+ var sequence = [{
+ session_state: ChromotingEvent.SessionState.STARTED,
+ },{
+ session_state: ChromotingEvent.SessionState.SIGNALING,
+ previous_session_state: ChromotingEvent.SessionState.STARTED
+ },{
+ session_state: ChromotingEvent.SessionState.CREATING_PLUGIN,
+ previous_session_state: ChromotingEvent.SessionState.SIGNALING
+ },{
+ session_state: ChromotingEvent.SessionState.CONNECTING,
+ previous_session_state: ChromotingEvent.SessionState.CREATING_PLUGIN
+ },{
+ session_state: ChromotingEvent.SessionState.AUTHENTICATED,
+ previous_session_state: ChromotingEvent.SessionState.CONNECTING
+ },{
+ session_state: ChromotingEvent.SessionState.CONNECTED,
+ previous_session_state: ChromotingEvent.SessionState.AUTHENTICATED
+ },{
+ session_state: ChromotingEvent.SessionState.CLOSED,
+ previous_session_state: ChromotingEvent.SessionState.CONNECTED
+ }];
+
+ var expectedEvents = sequence.map(function(/** Object */ sequenceValue) {
+ var event = /** @type {Object} */ (base.deepCopy(baseEvent));
+ base.mix(event, sequenceValue);
+ return event;
+ });
+ testDriver.expectEvents(expectedEvents);
+};
+
+/**
+ * @param {remoting.Me2MeTestDriver} testDriver
+ * @param {Object} baseEvent
+ */
+var expectCanceled = function(testDriver, baseEvent) {
+ var ChromotingEvent = remoting.ChromotingEvent;
+ var sequence = [{
+ session_state: ChromotingEvent.SessionState.STARTED,
+ },{
+ session_state: ChromotingEvent.SessionState.SIGNALING,
+ previous_session_state: ChromotingEvent.SessionState.STARTED
+ },{
+ session_state: ChromotingEvent.SessionState.CREATING_PLUGIN,
+ previous_session_state: ChromotingEvent.SessionState.SIGNALING
+ },{
+ session_state: ChromotingEvent.SessionState.CONNECTING,
+ previous_session_state: ChromotingEvent.SessionState.CREATING_PLUGIN
+ },{
+ session_state: ChromotingEvent.SessionState.CONNECTION_CANCELED,
+ previous_session_state: ChromotingEvent.SessionState.CONNECTING
+ }];
+
+ var expectedEvents = sequence.map(function(/** Object */ sequenceValue) {
+ var event = /** @type {Object} */ (base.deepCopy(baseEvent));
+ base.mix(event, sequenceValue);
+ return event;
+ });
+ testDriver.expectEvents(expectedEvents);
+};
+
+/**
+ * @param {remoting.Me2MeTestDriver} testDriver
+ * @param {Object} baseEvent
+ * @param {remoting.ChromotingEvent.ConnectionError} error
+ */
+var expectFailed = function(testDriver, baseEvent, error) {
+ var ChromotingEvent = remoting.ChromotingEvent;
+ var sequence = [{
+ session_state: ChromotingEvent.SessionState.STARTED,
+ },{
+ session_state: ChromotingEvent.SessionState.SIGNALING,
+ previous_session_state: ChromotingEvent.SessionState.STARTED
+ },{
+ session_state: ChromotingEvent.SessionState.CREATING_PLUGIN,
+ previous_session_state: ChromotingEvent.SessionState.SIGNALING
+ },{
+ session_state: ChromotingEvent.SessionState.CONNECTING,
+ previous_session_state: ChromotingEvent.SessionState.CREATING_PLUGIN
+ },{
+ session_state: ChromotingEvent.SessionState.CONNECTION_FAILED,
+ previous_session_state: ChromotingEvent.SessionState.CONNECTING,
+ connection_error: error
+ }];
+
+ var expectedEvents = sequence.map(function(/** Object */ sequenceValue) {
+ var event = /** @type {Object} */ (base.deepCopy(baseEvent));
+ base.mix(event, sequenceValue);
+ return event;
+ });
+ testDriver.expectEvents(expectedEvents);
+};
+
+QUnit.test('Connection succeeded', function() {
+ expectSucceeded(testDriver, {
+ session_entry_point:
+ remoting.ChromotingEvent.SessionEntryPoint.CONNECT_BUTTON,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ });
+
+ /**
+ * @param {remoting.MockClientPlugin} plugin
+ * @param {remoting.ClientSession.State} state
+ */
+ function onStatusChanged(plugin, state) {
+ if (state == remoting.ClientSession.State.CONNECTED) {
+ testDriver.me2meActivity().stop();
+ testDriver.endTest();
+ }
+ }
+
+ testDriver.mockConnection().pluginFactory().mock$setPluginStatusChanged(
+ onStatusChanged);
+
+ return testDriver.startTest();
+});
+
+QUnit.test('Connection canceled - Pin prompt', function() {
+ expectCanceled(testDriver, {
+ session_entry_point:
+ remoting.ChromotingEvent.SessionEntryPoint.CONNECT_BUTTON,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ });
+
+ /**
+ * @param {remoting.MockClientPlugin} plugin
+ * @param {remoting.ClientSession.State} state
+ */
+ function onStatusChanged(plugin, state) {
+ if (state == remoting.ClientSession.State.CONNECTING) {
+ testDriver.cancelWhenPinPrompted();
+ plugin.mock$onDisposed().then(function(){
+ testDriver.endTest();
+ });
+ }
+ }
+
+ testDriver.mockConnection().pluginFactory().mock$setPluginStatusChanged(
+ onStatusChanged);
+
+ return testDriver.startTest();
+});
+
+QUnit.test('Connection failed - Signal strategy', function() {
+ var EntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
+ testDriver.expectEvents([{
+ session_entry_point: EntryPoint.CONNECT_BUTTON,
+ session_state: remoting.ChromotingEvent.SessionState.STARTED
+ },{
+ session_entry_point: EntryPoint.CONNECT_BUTTON,
+ session_state: remoting.ChromotingEvent.SessionState.SIGNALING,
+ previous_session_state: remoting.ChromotingEvent.SessionState.STARTED
+ },{
+ session_entry_point: EntryPoint.CONNECT_BUTTON,
+ previous_session_state: remoting.ChromotingEvent.SessionState.SIGNALING,
+ session_state: remoting.ChromotingEvent.SessionState.CONNECTION_FAILED,
+ connection_error: remoting.ChromotingEvent.ConnectionError.UNEXPECTED
+ }]);
+
+ var promise = testDriver.startTest();
+
+ // The message dialog is shown when the connection fails.
+ testDriver.mockDialogFactory().messageDialog.show = function() {
+ testDriver.endTest();
+ return Promise.resolve(remoting.MessageDialog.Result.PRIMARY);
+ };
+
+ var signalStrategy = testDriver.mockConnection().signalStrategy();
+ signalStrategy.connect = function() {
+ Promise.resolve().then(function(){
+ signalStrategy.setStateForTesting(remoting.SignalStrategy.State.FAILED);
+ });
+ };
+
+ return promise;
+});
+
+QUnit.test('Reconnect', function() {
+ var EntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
+ expectSucceeded(testDriver, {
+ session_entry_point: EntryPoint.CONNECT_BUTTON,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ });
+ expectSucceeded(testDriver, {
+ session_entry_point: EntryPoint.RECONNECT_BUTTON,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ });
+
+ var count = 0;
+ /**
+ * @param {remoting.MockClientPlugin} plugin
+ * @param {remoting.ClientSession.State} state
+ */
+ function onStatusChanged(plugin, state) {
+ if (state == remoting.ClientSession.State.CONNECTED) {
+ count++;
+ if (count == 1) {
+ testDriver.clickReconnectWhenFinished();
+ testDriver.me2meActivity().stop();
+ } else if (count == 2) {
+ testDriver.clickOkWhenFinished();
+ testDriver.me2meActivity().stop();
+ testDriver.endTest();
+ }
+ }
+ }
+
+ testDriver.mockConnection().pluginFactory().mock$setPluginStatusChanged(
+ onStatusChanged);
+
+ return testDriver.startTest();
+});
+
+QUnit.test('HOST_OFFLINE - JID refresh failed', function() {
+ var EntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
+ // Expects the first connection to fail with HOST_OFFLINE
+ expectFailed(testDriver, {
+ session_entry_point:EntryPoint.CONNECT_BUTTON,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ }, remoting.ChromotingEvent.ConnectionError.HOST_OFFLINE);
+
+ function onPluginCreated(/** remoting.MockClientPlugin */ plugin) {
+ plugin.mock$returnErrorOnConnect(
+ remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE);
+ }
+
+ /**
+ * @param {remoting.MockClientPlugin} plugin
+ * @param {remoting.ClientSession.State} state
+ */
+ function onStatusChanged(plugin, state) {
+ if (state == remoting.ClientSession.State.FAILED) {
+ testDriver.endTest();
+ }
+ }
+
+ testDriver.mockConnection().pluginFactory().mock$setPluginCreated(
+ onPluginCreated);
+ testDriver.mockConnection().pluginFactory().mock$setPluginStatusChanged(
+ onStatusChanged);
+ sinon.stub(testDriver.mockHostList(), 'refresh',
+ function(/** function(boolean)*/ callback) {
+ // Fail the refresh.
+ Promise.resolve().then(function(){
+ callback(false);
+ });
+ });
+
+ return testDriver.startTest();
+});
+
+QUnit.test('HOST_OFFLINE - JID refresh succeeded', function() {
+ var EntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
+ // Expects the first connection to fail with HOST_OFFLINE
+ expectFailed(testDriver, {
+ session_entry_point:EntryPoint.CONNECT_BUTTON,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ }, remoting.ChromotingEvent.ConnectionError.HOST_OFFLINE);
+ // Expects the second connection to succeed with RECONNECT
+ expectSucceeded(testDriver, {
+ session_entry_point: EntryPoint.AUTO_RECONNECT_ON_HOST_OFFLINE,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ });
+
+ var count = 0;
+ function onPluginCreated(/** remoting.MockClientPlugin */ plugin) {
+ count++;
+ if (count == 1) {
+ plugin.mock$returnErrorOnConnect(
+ remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE);
+ } else if (count == 2) {
+ plugin.mock$useDefaultBehavior(
+ remoting.MockClientPlugin.AuthMethod.PIN);
+ }
+ }
+
+ /**
+ * @param {remoting.MockClientPlugin} plugin
+ * @param {remoting.ClientSession.State} state
+ */
+ function onStatusChanged(plugin, state) {
+ if (state == remoting.ClientSession.State.CONNECTED) {
+ testDriver.me2meActivity().stop();
+ testDriver.endTest();
+ }
+ }
+
+ testDriver.mockConnection().pluginFactory().mock$setPluginCreated(
+ onPluginCreated);
+ testDriver.mockConnection().pluginFactory().mock$setPluginStatusChanged(
+ onStatusChanged);
+
+ return testDriver.startTest();
+});
+
+QUnit.test('HOST_OFFLINE - Reconnect failed', function() {
+ var EntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
+ // Expects the first connection to fail with HOST_OFFLINE
+ expectFailed(testDriver, {
+ session_entry_point:EntryPoint.CONNECT_BUTTON,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ }, remoting.ChromotingEvent.ConnectionError.HOST_OFFLINE);
+ // Expects the second connection to fail with HOST_OVERLOAD
+ expectFailed(testDriver, {
+ session_entry_point:EntryPoint.AUTO_RECONNECT_ON_HOST_OFFLINE,
+ role: remoting.ChromotingEvent.Role.CLIENT,
+ mode: remoting.ChromotingEvent.Mode.ME2ME,
+ }, remoting.ChromotingEvent.ConnectionError.HOST_OVERLOAD);
+
+ var count = 0;
+ function onPluginCreated(/** remoting.MockClientPlugin */ plugin) {
+ count++;
+ if (count == 1) {
+ plugin.mock$returnErrorOnConnect(
+ remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE);
+ } else if (count == 2) {
+ plugin.mock$returnErrorOnConnect(
+ remoting.ClientSession.ConnectionError.HOST_OVERLOAD);
+ }
+ }
+
+ var failureCount = 0;
+ /**
+ * @param {remoting.MockClientPlugin} plugin
+ * @param {remoting.ClientSession.State} state
+ */
+ function onStatusChanged(plugin, state) {
+ if (state == remoting.ClientSession.State.FAILED) {
+ failureCount++;
+
+ if (failureCount == 2) {
+ testDriver.endTest();
+ }
+ }
+ }
+ testDriver.mockConnection().pluginFactory().mock$setPluginCreated(
+ onPluginCreated);
+ testDriver.mockConnection().pluginFactory().mock$setPluginStatusChanged(
+ onStatusChanged);
+ return testDriver.startTest();
+});
+
+})();
diff --git a/remoting/webapp/crd/js/mock_client_plugin.js b/remoting/webapp/crd/js/mock_client_plugin.js
index efb624b..319fec1 100644
--- a/remoting/webapp/crd/js/mock_client_plugin.js
+++ b/remoting/webapp/crd/js/mock_client_plugin.js
@@ -47,12 +47,22 @@ remoting.MockClientPlugin = function() {
/** @private */
this.onConnectDeferred_ = new base.Deferred();
+
+ /** @private */
+ this.onDisposedDeferred_ = new base.Deferred();
+
+ /**
+ * @private {?function(remoting.MockClientPlugin,
+ * remoting.ClientSession.State)}
+ */
+ this.mock$onPluginStatusChanged_ = null;
};
remoting.MockClientPlugin.prototype.dispose = function() {
this.container_.removeChild(this.element_);
this.element_ = null;
this.connectionStatusUpdateHandler_ = null;
+ this.onDisposedDeferred_.resolve();
};
remoting.MockClientPlugin.prototype.extensions = function() {
@@ -159,6 +169,15 @@ remoting.MockClientPlugin.prototype.mock$onConnect = function() {
};
/**
+ * @return {Promise} Returns a promise that will resolve when the plugin is
+ * disposed.
+ */
+remoting.MockClientPlugin.prototype.mock$onDisposed = function() {
+ this.onDisposedDeferred_ = new base.Deferred();
+ return this.onDisposedDeferred_.promise();
+};
+
+/**
* @param {remoting.ClientSession.State} status
* @param {remoting.ClientSession.ConnectionError=} opt_error
*/
@@ -169,6 +188,9 @@ remoting.MockClientPlugin.prototype.mock$setConnectionStatus = function(
var PluginError = remoting.ClientSession.ConnectionError;
var error = opt_error ? opt_error : PluginError.NONE;
this.connectionEventHandler_.onConnectionStatusUpdate(status, error);
+ if (this.mock$onPluginStatusChanged_) {
+ this.mock$onPluginStatusChanged_(this, status);
+ }
};
/**
@@ -201,6 +223,15 @@ remoting.MockClientPlugin.prototype.mock$authenticate = function(authMethod) {
};
/**
+ * @param {?function(remoting.MockClientPlugin, remoting.ClientSession.State)}
+ * callback
+ */
+remoting.MockClientPlugin.prototype.mock$setPluginStatusChanged =
+ function(callback) {
+ this.mock$onPluginStatusChanged_ = callback;
+};
+
+/**
* @param {remoting.MockClientPlugin.AuthMethod} authMethod
*/
remoting.MockClientPlugin.prototype.mock$useDefaultBehavior =
@@ -211,26 +242,87 @@ remoting.MockClientPlugin.prototype.mock$useDefaultBehavior =
that.mock$setConnectionStatus(State.CONNECTING);
return that.mock$authenticate(authMethod);
}).then(function() {
+ that.mock$setConnectionStatus(State.AUTHENTICATED);
+ }).then(function() {
that.mock$setConnectionStatus(State.CONNECTED);
});
};
/**
+ * @param {remoting.ClientSession.ConnectionError} error
+ */
+remoting.MockClientPlugin.prototype.mock$returnErrorOnConnect = function(error){
+ var that = this;
+ var State = remoting.ClientSession.State;
+ this.mock$onConnect().then(function() {
+ that.mock$setConnectionStatus(State.CONNECTING);
+ }).then(function() {
+ that.mock$setConnectionStatus(State.FAILED, error);
+ });
+};
+
+/**
* @constructor
* @implements {remoting.ClientPluginFactory}
*/
remoting.MockClientPluginFactory = function() {
- /** @private */
- this.plugin_ = new remoting.MockClientPlugin();
+ /** @private {?remoting.MockClientPlugin} */
+ this.plugin_ = null;
+
+ /**
+ * @private {?function(remoting.MockClientPlugin)}
+ */
+ this.onPluginCreated_ = null;
+
+ /**
+ * @private {?function(remoting.MockClientPlugin,
+ * remoting.ClientSession.State)}
+ */
+ this.onPluginStatusChanged_ = null;
};
remoting.MockClientPluginFactory.prototype.createPlugin =
function(container, capabilities) {
+ this.plugin_ = new remoting.MockClientPlugin();
this.plugin_.mock$setContainer(container);
this.plugin_.mock$capabilities = capabilities;
+
+ // Notify the listeners on plugin creation.
+ if (Boolean(this.onPluginCreated_)) {
+ this.onPluginCreated_(this.plugin_);
+ } else {
+ this.plugin_.mock$useDefaultBehavior(
+ remoting.MockClientPlugin.AuthMethod.PIN);
+ }
+
+ // Listens for plugin status changed.
+ if (this.onPluginStatusChanged_) {
+ this.plugin_.mock$setPluginStatusChanged(this.onPluginStatusChanged_);
+ }
return this.plugin_;
};
+/**
+ * Register a callback to configure the plugin before it returning to the
+ * caller.
+ *
+ * @param {function(remoting.MockClientPlugin)} callback
+ */
+remoting.MockClientPluginFactory.prototype.mock$setPluginCreated =
+ function(callback) {
+ this.onPluginCreated_ = callback;
+};
+
+/**
+ * @param {?function(remoting.MockClientPlugin, remoting.ClientSession.State)}
+ * callback
+ */
+remoting.MockClientPluginFactory.prototype.mock$setPluginStatusChanged =
+ function(callback) {
+ this.onPluginStatusChanged_ = callback;
+};
+
+
/** @return {remoting.MockClientPlugin} */
remoting.MockClientPluginFactory.prototype.plugin = function() {
return this.plugin_;
@@ -271,6 +363,11 @@ remoting.MockConnection = function() {
remoting.settings = new remoting.Settings();
};
+/** @return {remoting.MockClientPluginFactory} */
+remoting.MockConnection.prototype.pluginFactory = function() {
+ return this.pluginFactory_;
+};
+
/** @return {remoting.MockClientPlugin} */
remoting.MockConnection.prototype.plugin = function() {
return this.pluginFactory_.plugin();
diff --git a/remoting/webapp/crd/js/mock_modal_dialog_factory.js b/remoting/webapp/crd/js/mock_modal_dialog_factory.js
new file mode 100644
index 0000000..2c89c6a
--- /dev/null
+++ b/remoting/webapp/crd/js/mock_modal_dialog_factory.js
@@ -0,0 +1,95 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * Mock implementation of remoting.ModalDialogFactory for testing.
+ */
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+(function() {
+
+'use strict';
+
+/**
+ * @constructor
+ * @extends {remoting.InputDialog}
+ */
+remoting.MockInputDialog = function() {};
+
+/** @override */
+remoting.MockInputDialog.prototype.show = function() {};
+
+/**
+ * @constructor
+ * @extends {remoting.MessageDialog}
+ */
+remoting.MockMessageDialog = function() {};
+
+/** @override */
+remoting.MockMessageDialog.prototype.show = function() {};
+
+/**
+ * @constructor
+ * @extends {remoting.Html5ModalDialog}
+ */
+remoting.MockHtml5ModalDialog = function() {};
+
+/** @override */
+remoting.MockHtml5ModalDialog.prototype.show = function() {};
+
+/**
+ * @constructor
+ * @extends {remoting.ConnectingDialog}
+ */
+remoting.MockConnectingDialog = function() {};
+
+/** @override */
+remoting.MockConnectingDialog.prototype.show = function() {};
+
+/** @override */
+remoting.MockConnectingDialog.prototype.hide = function() {};
+
+/**
+ * @constructor
+ * @extends {remoting.ModalDialogFactory}
+ */
+remoting.MockModalDialogFactory = function() {
+ /** @type {remoting.MockConnectingDialog} */
+ this.connectingDialog = new remoting.MockConnectingDialog();
+ /** @type {remoting.MockMessageDialog} */
+ this.messageDialog = new remoting.MockMessageDialog();
+ /** @type {remoting.MockInputDialog} */
+ this.inputDialog = new remoting.MockInputDialog();
+ /** @type {remoting.MockHtml5ModalDialog} */
+ this.html5ModalDialog = new remoting.MockHtml5ModalDialog();
+};
+
+/** @override */
+remoting.MockModalDialogFactory.prototype.createConnectingDialog =
+ function(cancelCallback) {
+ return this.connectingDialog;
+};
+
+/** @override */
+remoting.MockModalDialogFactory.prototype.createHtml5ModalDialog =
+ function(params) {
+ return this.html5ModalDialog;
+};
+
+/** @override */
+remoting.MockModalDialogFactory.prototype.createMessageDialog =
+ function(mode, primaryButton, opt_secondaryButton) {
+ return this.messageDialog;
+};
+
+/** @override */
+remoting.MockModalDialogFactory.prototype.createInputDialog =
+ function(mode, formElement, inputField, cancelButton) {
+ return this.inputDialog;
+};
+
+})();
diff --git a/remoting/webapp/crd/js/remoting_activity_test_driver.js b/remoting/webapp/crd/js/remoting_activity_test_driver.js
new file mode 100644
index 0000000..2197db4
--- /dev/null
+++ b/remoting/webapp/crd/js/remoting_activity_test_driver.js
@@ -0,0 +1,238 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+(function() {
+
+'use strict';
+
+/**
+ * @constructor
+ *
+ * The |extends| annotation is used to make JSCompile happy. The mock object
+ * should never extends from the actual HostList as all its implementation
+ * should be mocked out. The caller of this class is responsible for ensuring
+ * the methods that they need are implemented either here or via sinon.stub().
+ *
+ * @extends {remoting.HostList}
+ */
+var MockHostList = function() {};
+
+/** @override */
+MockHostList.prototype.refresh = function(callback) {
+ Promise.resolve().then(function() {
+ callback(true);
+ });
+};
+
+/** @override */
+MockHostList.prototype.getHostForId = function(hostId) {
+ var host = new remoting.Host(hostId);
+ host.jabberId = 'fake_jabber_id';
+ host.loggingChannel = 'APIARY';
+ return host;
+};
+
+/**
+ * @constructor
+ * @extends {remoting.DesktopConnectedView}
+ */
+var MockDesktopConnectedView = function() {};
+/** @override */
+MockDesktopConnectedView.prototype.setVideoFrameRecorder = function() {};
+/** @override */
+MockDesktopConnectedView.prototype.dispose = function() {};
+
+/**
+ * A test driver that mocks out the UI components that are required by the
+ * DesktopRemotingActivity.
+ *
+ * @param {string} mockHTML
+ *
+ * @constructor
+ * @implements {base.Disposable}
+ */
+var BaseTestDriver = function(mockHTML) {
+ /** @private */
+ this.deferred_ = new base.Deferred();
+ /** @protected */
+ this.mockConnection_ = new remoting.MockConnection();
+ /** @private */
+ this.originalDialogFactory_ = remoting.modalDialogFactory;
+ /** @protected */
+ this.mockDialogFactory_ = new remoting.MockModalDialogFactory();
+ /** @private */
+ this.desktopConnectedViewCreateStub_ =
+ sinon.stub(remoting.DesktopConnectedView, 'create');
+ /** @private */
+ this.eventWriterMock_ = sinon.mock(remoting.TelemetryEventWriter.Client);
+ /** @private */
+ this.setModeStub_ = sinon.stub(remoting, 'setMode');
+ /**
+ * Use fake timers to prevent the generation of session ID expiration events.
+ * @private
+ */
+ this.clock_ = sinon.useFakeTimers();
+
+ this.init_(mockHTML);
+};
+
+/**
+ * @param {string} mockHTML
+ */
+BaseTestDriver.prototype.init_ = function(mockHTML) {
+ document.getElementById('qunit-fixture').innerHTML = mockHTML;
+ // Return a token to pretend that we are signed-in.
+ chromeMocks.identity.mock$setToken('fake_token');
+ this.desktopConnectedViewCreateStub_.returns(new MockDesktopConnectedView());
+ remoting.modalDialogFactory = this.mockDialogFactory_;
+};
+
+BaseTestDriver.prototype.dispose = function() {
+ this.clock_.restore();
+ remoting.modalDialogFactory = this.originalDialogFactory_;
+ this.setModeStub_.restore();
+ this.eventWriterMock_.restore();
+ this.desktopConnectedViewCreateStub_.restore();
+
+ if (Boolean(this.mockConnection_)) {
+ this.mockConnection_.restore();
+ this.mockConnection_ = null;
+ }
+};
+
+/** @return {remoting.MockConnection} */
+BaseTestDriver.prototype.mockConnection = function() {
+ return this.mockConnection_;
+};
+
+/** @return {remoting.MockModalDialogFactory} */
+BaseTestDriver.prototype.mockDialogFactory = function() {
+ return this.mockDialogFactory_;
+};
+
+/** @param {Array<Object>} events */
+BaseTestDriver.prototype.expectEvents = function(events) {
+ var that = this;
+ events.forEach(function(/** Object */ event){
+ that.eventWriterMock_.expects('write').withArgs(sinon.match(event));
+ });
+};
+
+/**
+ * @return {Promise} A promise that will be resolved when endTest() is called.
+ */
+BaseTestDriver.prototype.startTest = function() {
+ return this.deferred_.promise();
+};
+
+/**
+ * Resolves the promise that is returned by startTest().
+ */
+BaseTestDriver.prototype.endTest = function() {
+ try {
+ this.eventWriterMock_.verify();
+ this.deferred_.resolve();
+ } catch (/** @type {*} */ reason) {
+ this.deferred_.reject(reason);
+ }
+};
+
+/**
+ * The Me2MeTest Driver mocks out the UI components that are required by the
+ * Me2MeActivity. It provides test hooks for the caller to fake behavior of
+ * those components.
+ *
+ * @constructor
+ * @extends {BaseTestDriver}
+ */
+remoting.Me2MeTestDriver = function() {
+ base.inherits(this, BaseTestDriver, remoting.Me2MeTestDriver.FIXTURE);
+ /** @private */
+ this.mockHostList_ = new MockHostList();
+ /** @private {?remoting.Me2MeActivity} */
+ this.me2meActivity_ = null;
+};
+
+/** @override */
+remoting.Me2MeTestDriver.prototype.dispose = function() {
+ base.dispose(this.me2meActivity_);
+ this.me2meActivity_ = null;
+
+ BaseTestDriver.prototype.dispose.call(this);
+};
+
+remoting.Me2MeTestDriver.prototype.enterPinWhenPrompted = function() {
+ this.mockDialogFactory().inputDialog.show = function() {
+ return Promise.resolve('fake_pin');
+ };
+};
+
+remoting.Me2MeTestDriver.prototype.cancelWhenPinPrompted = function() {
+ this.mockDialogFactory().inputDialog.show = function() {
+ return Promise.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
+ };
+};
+
+remoting.Me2MeTestDriver.prototype.clickOkWhenFinished = function() {
+ this.mockDialogFactory().messageDialog.show = function() {
+ return Promise.resolve(remoting.MessageDialog.Result.PRIMARY);
+ };
+};
+
+remoting.Me2MeTestDriver.prototype.clickReconnectWhenFinished = function() {
+ this.mockDialogFactory().messageDialog.show = function() {
+ return Promise.resolve(remoting.MessageDialog.Result.SECONDARY);
+ };
+};
+
+/** @return {MockHostList} */
+remoting.Me2MeTestDriver.prototype.mockHostList = function() {
+ return this.mockHostList_;
+};
+
+/** @return {remoting.Me2MeActivity} */
+remoting.Me2MeTestDriver.prototype.me2meActivity = function() {
+ return this.me2meActivity_;
+};
+
+/** @return {Promise} */
+remoting.Me2MeTestDriver.prototype.startTest = function() {
+ var host = new remoting.Host('fake_host_id');
+ host.loggingChannel = 'APIARY';
+
+ // Default behavior.
+ this.enterPinWhenPrompted();
+ this.clickOkWhenFinished();
+
+ this.me2meActivity_ = new remoting.Me2MeActivity(host, this.mockHostList_);
+ this.me2meActivity_.start();
+ return BaseTestDriver.prototype.startTest.call(this);
+};
+
+remoting.Me2MeTestDriver.FIXTURE =
+ '<div id="connect-error-message"></div>' +
+ '<div id="client-container">' +
+ '<div class="client-plugin-container">' +
+ '</div>' +
+ '<div id="pin-dialog">' +
+ '<form>' +
+ '<input type="password" class="pin-inputField" />' +
+ '<button class="cancel-button"></button>' +
+ '</form>' +
+ '<div class="pairing-section">' +
+ '<input type="checkbox" class="pairing-checkbox" />' +
+ '<div class="pin-message"></div>' +
+ '</div>' +
+ '</div>' +
+ '<div id="host-needs-update-dialog">' +
+ '<input class="connect-button" />' +
+ '<input class="cancel-button" />' +
+ '<div class="host-needs-update-message"></div>' +
+ '</div>';
+
+})(); \ No newline at end of file
diff --git a/remoting/webapp/js_proto/sinon_proto.js b/remoting/webapp/js_proto/sinon_proto.js
index c6cd0a8..7679b9c 100644
--- a/remoting/webapp/js_proto/sinon_proto.js
+++ b/remoting/webapp/js_proto/sinon_proto.js
@@ -24,6 +24,11 @@ sinon.assert.calledOnce = function(f) {};
sinon.assert.calledWith = function(f, data) {};
/**
+ * @param {*} value
+ * @return {Object}
+ */
+sinon.match = function(value) {};
+/**
* @param {(sinon.Spy|Function)} f
*/
sinon.assert.notCalled = function(f) {};