1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // JavaScript for invoking methods on APIs used by Hangouts via the
6 // Hangout Services extension, and a JavaScript-based end-to-end test
9 // ID of the Hangout Services component extension.
10 var EXTENSION_ID = "nkeimhogjdpnpccoofpliimaahmaaome";
12 // Sends a message to the Hangout Services extension, expecting
13 // success, and unwraps the value returned.
14 function sendMessage(message, callback) {
15 function unwrapValue(result) {
17 callback(result.value);
19 window.top.chrome.runtime.sendMessage(EXTENSION_ID, message, unwrapValue);
22 // If connected, this port will receive events from the extension.
25 // Connects callbackPort to the extension, with a connectInfo.name that
26 // indicates we want to receive the onSinksChanged event, and sets its
27 // onMessage to |callback|.
28 function listenForSinksChangedEvent(callback) {
29 callbackPort = window.top.chrome.runtime.connect(
30 EXTENSION_ID, {'name': 'onSinksChangedListener'});
31 callbackPort.onMessage.addListener(callback);
35 // Helpers to invoke functions on the extension.
38 // Will call |callback({'cancelId': ..., 'streamId': ...})| on completion.
39 function chooseDesktopMedia(callback) {
40 sendMessage({'method': 'chooseDesktopMedia'}, callback);
43 // Will call |callback()| when API method has been called (it will
45 function cancelChooseDesktopMedia(cancelId, callback) {
46 sendMessage({'method': 'cancelChooseDesktopMedia', 'cancelId': cancelId},
50 // Will call |callback(cpuInfo)| on completion.
51 function cpuGetInfo(callback) {
52 sendMessage({'method': 'cpu.getInfo'}, callback);
55 // Will call |callback()| on completion.
56 function loggingSetMetadata(metaData, callback) {
57 sendMessage({'method': 'logging.setMetadata', 'metaData': metaData},
61 // Will call |callback()| on completion.
62 function loggingStart(callback) {
63 sendMessage({'method': 'logging.start'}, callback);
66 // Will call |callback()| when API method has been called (it will
68 function loggingUploadOnRenderClose() {
69 sendMessage({'method': 'logging.uploadOnRenderClose'});
72 // Will call |callback()| when API method has been called (it will
74 function loggingNoUploadOnRenderClose() {
75 sendMessage({'method': 'logging.noUploadOnRenderClose'});
78 // Will call |callback()| on completion.
79 function loggingStop(callback) {
80 sendMessage({'method': 'logging.stop'}, callback);
83 // Will call |callback(uploadResult)| on completion.
84 function loggingUpload(callback) {
85 sendMessage({'method': 'logging.upload'}, callback);
88 // Will call |callback()| on completion.
89 function loggingDiscard(callback) {
90 sendMessage({'method': 'logging.discard'}, callback);
93 // Will call |callback(sinkList)| on completion.
94 function getSinks(callback) {
95 sendMessage({'method': 'getSinks'}, callback);
98 // Will call |callback(activeSink)| on completion.
99 function getActiveSink(callback) {
100 sendMessage({'method': 'getActiveSink'}, callback);
103 // Will call |callback()| on completion.
104 function setActiveSink(sinkId, callback) {
105 sendMessage({'method': 'setActiveSink', 'sinkId': sinkId}, callback);
108 // Will call |callback(sinkId)| on completion.
109 function getAssociatedSink(sourceId, callback) {
110 sendMessage({'method': 'getAssociatedSink', 'sourceId': sourceId},
114 // Will call |callback()| on completion. If the extension you send to
115 // is not loaded, the extension system will still call |callback()|
116 // but will set lastError.
117 function isExtensionEnabled(callback) {
118 sendMessage({'method': 'isExtensionEnabled'}, callback);
125 function manualTestChooseDesktopMedia() {
126 chooseDesktopMedia(function(results) {
127 alert('Cancel ID: ' + results.cancelId +
128 ', stream ID: ' + results.streamId);
132 function manualTestListenForSinksChangedEvent() {
133 listenForSinksChangedEvent(function(msg) {
134 if (msg['eventName'] && msg['eventName'] == 'onSinksChanged')
135 alert('Got onSinksChanged event.');
143 // Very micro test framework. Add all tests to |TESTS|. Each test must
144 // call the passed-in callback eventually with a string indicating
145 // results. Empty results indicate success.
150 testDisabledLoggingButUpload,
151 testEnabledLoggingButDiscard,
155 testGetAssociatedSink,
156 testIsExtensionEnabled,
157 testSendingToInvalidExtension,
159 // Uncomment to manually test timeout logic.
163 function runAllTests(callback) {
166 // Run one test at a time, running the next only on completion.
167 // This makes it easier to deal with timing out tests that do not
168 // complete successfully.
170 // It also seems necessary (instead of starting all the tests in
171 // parallel) as the webrtcLoggingPrivate API does not seem to like
172 // certain sequences of interleaved requests (it may DCHECK in
173 // WebRtcLoggingHandlerHost::NotifyLoggingStarted() when firing the
176 // TODO(grunell): Fix webrtcLoggingPrivate to be stateless.
178 // Index of the test currently executing.
181 function startTest(test) {
182 console.log('Starting ' + test.name);
184 // Start the test function...
185 test(function(currentResults) {
186 nextTest(test.name, currentResults, false);
189 // ...and also start a timeout.
190 function onTimeout() {
191 nextTest(test.name, '', true);
193 setTimeout(onTimeout, 3000);
196 function nextTest(testName, currentResults, timedOut) {
197 // The check for testIndex is necessary for timeouts arriving
198 // after testIndex is already past the end of the TESTS array.
199 if (testIndex >= TESTS.length ||
200 testName != TESTS[testIndex].name) {
201 // Either a timeout of a function that already completed, or a
202 // function completing after a timeout. Either way we ignore.
203 console.log('Ignoring results for ' + testName +
204 ' (timedout: ' + timedOut + ')');
209 console.log('Timed out: ' + testName);
210 results = results + 'Timed out: ' + testName + '\n';
212 console.log('Got results for ' + testName + ': ' + currentResults);
213 if (currentResults != '') {
214 results = results + 'Failure in ' + testName + ':\n';
215 results = results + currentResults;
220 if (testIndex == TESTS.length)
223 startTest(TESTS[testIndex]);
226 startTest(TESTS[testIndex]);
229 function testCpuGetInfo(callback) {
230 cpuGetInfo(function(info) {
231 if (info.numOfProcessors != 0 &&
232 info.archName != '' &&
233 info.modelName != '')
236 callback('Missing information in CpuInfo');
240 // Tests setting metadata, turning on upload, starting and then
242 function testLogging(callback) {
243 loggingSetMetadata([{'bingo': 'bongo', 'smurf': 'geburf'}], function() {
244 loggingStart(function() {
245 loggingUploadOnRenderClose();
246 loggingStop(function() {
253 // Starts and stops logging while auto-upload is disabled.
254 function testDisabledLogging(callback) {
255 loggingNoUploadOnRenderClose();
256 loggingStart(function() {
257 loggingStop(function() {
263 // Starts and stops logging while auto-upload is disabled, but
264 // requests logs upload after stopping logging.
265 function testDisabledLoggingButUpload(callback) {
266 loggingNoUploadOnRenderClose();
267 loggingStart(function() {
268 loggingStop(function() {
269 loggingUpload(function(loggingResult) {
270 if (loggingResult != '')
273 callback('Got empty upload result.');
279 // Starts and stops logging while auto-upload is enabled, but
280 // requests logs be discarded after stopping logging.
281 function testEnabledLoggingButDiscard(callback) {
282 loggingUploadOnRenderClose();
283 loggingStart(function() {
284 loggingStop(function() {
285 loggingDiscard(function() {
292 function testGetSinks(callback) {
293 getSinks(function(sinks) {
294 if (sinks.length == 0)
295 callback('Got no sinks.');
301 function testGetActiveSink(callback) {
302 getActiveSink(function(sinkId) {
304 callback('Got empty sink ID.');
310 function testSetActiveSink(callback) {
311 getSinks(function(sinks) {
312 for (var i = 0; i < sinks.length; ++i) {
313 setActiveSink(sinks[i].sinkId);
319 function testGetAssociatedSink(callback) {
320 getAssociatedSink('noSuchSourceId', function(sinkId) {
322 callback('Got non-empty sink ID for nonexistent source ID.');
329 function testIsExtensionEnabled(callback) {
330 isExtensionEnabled(function() {
335 function testSendingToInvalidExtension(callback) {
336 // This test verifies that client code can always determine whether
337 // or not the component extension is available without resorting to
338 // timeouts, by sending it the 'isExtensionEnabled' message. If the
339 // extension is there, it will respond (see testIsExtensionEnabled)
340 // and if it's not, the extension framework will send a callback and
342 var NON_EXISTENT_EXTENSION_ID = "aaaaaaagjdpnpccoofpliimaaeeeeeee";
343 window.top.chrome.runtime.sendMessage(
344 NON_EXISTENT_EXTENSION_ID, {'method': 'isExtensionEnabled'},
346 if (window.top.chrome.runtime.lastError.message.indexOf(
347 'Could not establish connection') == -1) {
348 callback('lastError is not set correctly.');
355 function testTimeout(callback) {
356 // Never call the callback. Used for manually testing that the
357 // timeout logic of the test framework is correct.