| // Copyright 2014 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 |
| * @suppress {checkTypes} By default, JSCompile is not run on test files. |
| * However, you can modify |remoting_webapp_files.gypi| locally to include |
| * the test in the package to expedite local development. This suppress |
| * is here so that JSCompile won't complain. |
| * |
| * Provides basic functionality for JavaScript based browser test. |
| * |
| * To define a browser test, create a class under the browserTest namespace. |
| * You can pass arbitrary object literals to the browser test from the C++ test |
| * harness as the test data. Each browser test class should implement the run |
| * method. |
| * For example: |
| * |
| * browserTest.My_Test = function() {}; |
| * browserTest.My_Test.prototype.run(myObjectLiteral) = function() { ... }; |
| * |
| * The browser test is async in nature. It will keep running until |
| * browserTest.fail("My error message.") or browserTest.pass() is called. |
| * |
| * For example: |
| * |
| * browserTest.My_Test.prototype.run(myObjectLiteral) = function() { |
| * window.setTimeout(function() { |
| * if (doSomething(myObjectLiteral)) { |
| * browserTest.pass(); |
| * } else { |
| * browserTest.fail('My error message.'); |
| * } |
| * }, 1000); |
| * }; |
| * |
| * You will then invoke the test in C++ by calling: |
| * |
| * RunJavaScriptTest(web_content, "My_Test", "{" |
| * "pin: '123123'" |
| * "}"); |
| */ |
| |
| 'use strict'; |
| |
| var browserTest = {}; |
| |
| browserTest.init = function() { |
| // The domAutomationController is used to communicate progress back to the |
| // C++ calling code. It will only exist if chrome is run with the flag |
| // --dom-automation. It is stubbed out here so that browser test can be run |
| // under the regular app. |
| browserTest.automationController_ = window.domAutomationController || { |
| send: function(json) { |
| var result = JSON.parse(json); |
| if (result.succeeded) { |
| console.log('Test Passed.'); |
| } else { |
| console.error('Test Failed.\n' + |
| result.error_message + '\n' + result.stack_trace); |
| } |
| } |
| }; |
| }; |
| |
| browserTest.expect = function(expr, message) { |
| if (!expr) { |
| message = (message) ? '<' + message + '>' : ''; |
| browserTest.fail('Expectation failed.' + message); |
| } |
| }; |
| |
| browserTest.fail = function(error) { |
| var error_message = error; |
| var stack_trace = base.debug.callstack(); |
| |
| if (error instanceof Error) { |
| error_message = error.toString(); |
| stack_trace = error.stack; |
| } |
| |
| // To run browserTest locally: |
| // 1. Go to |remoting_webapp_files| and look for |
| // |remoting_webapp_js_browser_test_files| and uncomment it |
| // 2. gclient runhooks |
| // 3. rebuild the webapp |
| // 4. Run it in the console browserTest.runTest(browserTest.MyTest, {}); |
| // 5. The line below will trap the test in the debugger in case of |
| // failure. |
| debugger; |
| |
| browserTest.automationController_.send(JSON.stringify({ |
| succeeded: false, |
| error_message: error_message, |
| stack_trace: stack_trace |
| })); |
| }; |
| |
| browserTest.pass = function() { |
| browserTest.automationController_.send(JSON.stringify({ |
| succeeded: true, |
| error_message: '', |
| stack_trace: '' |
| })); |
| }; |
| |
| browserTest.clickOnControl = function(id) { |
| var element = document.getElementById(id); |
| browserTest.expect(element); |
| element.click(); |
| }; |
| |
| /** @enum {number} */ |
| browserTest.Timeout = { |
| NONE: -1, |
| DEFAULT: 5000 |
| }; |
| |
| browserTest.onUIMode = function(expectedMode, opt_timeout) { |
| if (expectedMode == remoting.currentMode) { |
| // If the current mode is the same as the expected mode, return a fulfilled |
| // promise. For some reason, if we fulfill the promise in the same |
| // callstack, V8 will assert at V8RecursionScope.h(66) with |
| // ASSERT(!ScriptForbiddenScope::isScriptForbidden()). |
| // To avoid the assert, execute the callback in a different callstack. |
| return base.Promise.sleep(0); |
| } |
| |
| return new Promise (function(fulfill, reject) { |
| var uiModeChanged = remoting.testEvents.Names.uiModeChanged; |
| var timerId = null; |
| |
| if (opt_timeout === undefined) { |
| opt_timeout = browserTest.Timeout.DEFAULT; |
| } |
| |
| function onTimeout() { |
| remoting.testEvents.removeEventListener(uiModeChanged, onUIModeChanged); |
| reject('Timeout waiting for ' + expectedMode); |
| } |
| |
| function onUIModeChanged(mode) { |
| if (mode == expectedMode) { |
| remoting.testEvents.removeEventListener(uiModeChanged, onUIModeChanged); |
| window.clearTimeout(timerId); |
| timerId = null; |
| fulfill(); |
| } |
| } |
| |
| if (opt_timeout != browserTest.Timeout.NONE) { |
| timerId = window.setTimeout(onTimeout, opt_timeout); |
| } |
| remoting.testEvents.addEventListener(uiModeChanged, onUIModeChanged); |
| }); |
| }; |
| |
| browserTest.expectMe2MeError = function(errorTag) { |
| var AppMode = remoting.AppMode; |
| var Timeout = browserTest.Timeout; |
| |
| var onConnected = browserTest.onUIMode(AppMode.IN_SESSION, Timeout.None); |
| var onFailure = browserTest.onUIMode(AppMode.CLIENT_CONNECT_FAILED_ME2ME); |
| |
| onConnected = onConnected.then(function() { |
| return Promise.reject( |
| 'Expected the Me2Me connection to fail.'); |
| }); |
| |
| onFailure = onFailure.then(function() { |
| var errorDiv = document.getElementById('connect-error-message'); |
| var actual = errorDiv.innerText; |
| var expected = l10n.getTranslationOrError(errorTag); |
| |
| browserTest.clickOnControl('client-finished-me2me-button'); |
| |
| if (actual != expected) { |
| return Promise.reject('Unexpected failure. actual:' + actual + |
| ' expected:' + expected); |
| } |
| }); |
| |
| return Promise.race([onConnected, onFailure]); |
| }; |
| |
| browserTest.expectMe2MeConnected = function() { |
| var AppMode = remoting.AppMode; |
| // Timeout if the session is not connected within 30 seconds. |
| var SESSION_CONNECTION_TIMEOUT = 30000; |
| var onConnected = browserTest.onUIMode(AppMode.IN_SESSION, |
| SESSION_CONNECTION_TIMEOUT); |
| var onFailure = browserTest.onUIMode(AppMode.CLIENT_CONNECT_FAILED_ME2ME, |
| browserTest.Timeout.NONE); |
| onFailure = onFailure.then(function() { |
| var errorDiv = document.getElementById('connect-error-message'); |
| var errorMsg = errorDiv.innerText; |
| return Promise.reject('Unexpected error - ' + errorMsg); |
| }); |
| return Promise.race([onConnected, onFailure]); |
| }; |
| |
| browserTest.runTest = function(testClass, data) { |
| try { |
| var test = new testClass(); |
| browserTest.expect(typeof test.run == 'function'); |
| test.run(data); |
| } catch (e) { |
| browserTest.fail(e); |
| } |
| }; |
| |
| browserTest.init(); |