summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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) {};