1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/tabs/test-fennec-tabs.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,606 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +'use strict'; 1.8 + 1.9 +const { Cc, Ci } = require('chrome'); 1.10 +const { Loader, LoaderWithHookedConsole } = require('sdk/test/loader'); 1.11 +const timer = require('sdk/timers'); 1.12 +const tabs = require('sdk/tabs'); 1.13 +const windows = require('sdk/windows'); 1.14 +const { set: setPref } = require("sdk/preferences/service"); 1.15 +const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; 1.16 + 1.17 +const tabsLen = tabs.length; 1.18 +const URL = 'data:text/html;charset=utf-8,<html><head><title>#title#</title></head></html>'; 1.19 + 1.20 +// Fennec error message dispatched on all currently unimplement tab features, 1.21 +// that match LoaderWithHookedConsole messages object pattern 1.22 +const ERR_FENNEC_MSG = { 1.23 + type: "error", 1.24 + msg: "This method is not yet supported by Fennec" 1.25 +}; 1.26 + 1.27 +// TEST: tab unloader 1.28 +exports.testAutomaticDestroy = function(assert, done) { 1.29 + let called = false; 1.30 + 1.31 + let loader2 = Loader(module); 1.32 + let loader3 = Loader(module); 1.33 + let tabs2 = loader2.require('sdk/tabs'); 1.34 + let tabs3 = loader3.require('sdk/tabs'); 1.35 + let tabs2Len = tabs2.length; 1.36 + 1.37 + tabs2.on('open', function onOpen(tab) { 1.38 + assert.fail("an onOpen listener was called that should not have been"); 1.39 + called = true; 1.40 + }); 1.41 + tabs2.on('ready', function onReady(tab) { 1.42 + assert.fail("an onReady listener was called that should not have been"); 1.43 + called = true; 1.44 + }); 1.45 + tabs2.on('select', function onSelect(tab) { 1.46 + assert.fail("an onSelect listener was called that should not have been"); 1.47 + called = true; 1.48 + }); 1.49 + tabs2.on('close', function onClose(tab) { 1.50 + assert.fail("an onClose listener was called that should not have been"); 1.51 + called = true; 1.52 + }); 1.53 + loader2.unload(); 1.54 + 1.55 + tabs3.on('open', function onOpen(tab) { 1.56 + assert.pass("an onOpen listener was called for tabs3"); 1.57 + 1.58 + tab.on('ready', function onReady(tab) { 1.59 + assert.fail("an onReady listener was called that should not have been"); 1.60 + called = true; 1.61 + }); 1.62 + tab.on('select', function onSelect(tab) { 1.63 + assert.fail("an onSelect listener was called that should not have been"); 1.64 + called = true; 1.65 + }); 1.66 + tab.on('close', function onClose(tab) { 1.67 + assert.fail("an onClose listener was called that should not have been"); 1.68 + called = true; 1.69 + }); 1.70 + }); 1.71 + tabs3.open(URL.replace(/#title#/, 'tabs3')); 1.72 + loader3.unload(); 1.73 + 1.74 + // Fire a tab event and ensure that the destroyed tab is inactive 1.75 + tabs.once('open', function(tab) { 1.76 + assert.pass('tabs.once("open") works!'); 1.77 + 1.78 + assert.equal(tabs2Len, tabs2.length, "tabs2 length was not changed"); 1.79 + assert.equal(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length"); 1.80 + 1.81 + tab.once('ready', function() { 1.82 + assert.pass('tab.once("ready") works!'); 1.83 + 1.84 + tab.once('close', function() { 1.85 + assert.pass('tab.once("close") works!'); 1.86 + 1.87 + timer.setTimeout(function () { 1.88 + assert.ok(!called, "Unloaded tab module is destroyed and inactive"); 1.89 + 1.90 + // end test 1.91 + done(); 1.92 + }); 1.93 + }); 1.94 + 1.95 + tab.close(); 1.96 + }); 1.97 + }); 1.98 + 1.99 + tabs.open('data:text/html;charset=utf-8,foo'); 1.100 +}; 1.101 + 1.102 +// TEST: tab properties 1.103 +exports.testTabProperties = function(assert, done) { 1.104 + setPref(DEPRECATE_PREF, true); 1.105 + let { loader, messages } = LoaderWithHookedConsole(); 1.106 + let tabs = loader.require('sdk/tabs'); 1.107 + 1.108 + let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>"; 1.109 + let tabsLen = tabs.length; 1.110 + tabs.open({ 1.111 + url: url, 1.112 + onReady: function(tab) { 1.113 + assert.equal(tab.title, "foo", "title of the new tab matches"); 1.114 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.115 + assert.ok(tab.favicon, "favicon of the new tab is not empty"); 1.116 + // TODO: remove need for this test by implementing the favicon feature 1.117 + assert.equal(messages[0].msg, 1.118 + "tab.favicon is deprecated, and " + 1.119 + "currently favicon helpers are not yet supported " + 1.120 + "by Fennec", 1.121 + "favicon logs an error for now"); 1.122 + assert.equal(tab.style, null, "style of the new tab matches"); 1.123 + assert.equal(tab.index, tabsLen, "index of the new tab matches"); 1.124 + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); 1.125 + assert.notEqual(tab.id, null, "a tab object always has an id property"); 1.126 + 1.127 + tab.close(function() { 1.128 + loader.unload(); 1.129 + 1.130 + // end test 1.131 + done(); 1.132 + }); 1.133 + } 1.134 + }); 1.135 +}; 1.136 + 1.137 +// TEST: tabs iterator and length property 1.138 +exports.testTabsIteratorAndLength = function(assert, done) { 1.139 + let newTabs = []; 1.140 + let startCount = 0; 1.141 + for each (let t in tabs) startCount++; 1.142 + 1.143 + assert.equal(startCount, tabs.length, "length property is correct"); 1.144 + 1.145 + let url = "data:text/html;charset=utf-8,testTabsIteratorAndLength"; 1.146 + tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)}); 1.147 + tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)}); 1.148 + tabs.open({ 1.149 + url: url, 1.150 + onOpen: function(tab) { 1.151 + let count = 0; 1.152 + for each (let t in tabs) count++; 1.153 + assert.equal(count, startCount + 3, "iterated tab count matches"); 1.154 + assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); 1.155 + 1.156 + let newTabsLength = newTabs.length; 1.157 + newTabs.forEach(function(t) t.close(function() { 1.158 + if (--newTabsLength > 0) return; 1.159 + 1.160 + tab.close(done); 1.161 + })); 1.162 + } 1.163 + }); 1.164 +}; 1.165 + 1.166 +// TEST: tab.url setter 1.167 +exports.testTabLocation = function(assert, done) { 1.168 + let url1 = "data:text/html;charset=utf-8,foo"; 1.169 + let url2 = "data:text/html;charset=utf-8,bar"; 1.170 + 1.171 + tabs.on('ready', function onReady(tab) { 1.172 + if (tab.url != url2) 1.173 + return; 1.174 + 1.175 + tabs.removeListener('ready', onReady); 1.176 + assert.pass("tab loaded the correct url"); 1.177 + 1.178 + tab.close(done); 1.179 + }); 1.180 + 1.181 + tabs.open({ 1.182 + url: url1, 1.183 + onOpen: function(tab) { 1.184 + tab.url = url2; 1.185 + } 1.186 + }); 1.187 +}; 1.188 + 1.189 +// TEST: tab.move() 1.190 +exports.testTabMove = function(assert, done) { 1.191 + let { loader, messages } = LoaderWithHookedConsole(); 1.192 + let tabs = loader.require('sdk/tabs'); 1.193 + 1.194 + let url = "data:text/html;charset=utf-8,testTabMove"; 1.195 + 1.196 + tabs.open({ 1.197 + url: url, 1.198 + onOpen: function(tab1) { 1.199 + assert.ok(tab1.index >= 0, "opening a tab returns a tab w/ valid index"); 1.200 + 1.201 + tabs.open({ 1.202 + url: url, 1.203 + onOpen: function(tab) { 1.204 + let i = tab.index; 1.205 + assert.ok(tab.index > tab1.index, "2nd tab has valid index"); 1.206 + tab.index = 0; 1.207 + assert.equal(tab.index, i, "tab index after move matches"); 1.208 + assert.equal(JSON.stringify(messages), 1.209 + JSON.stringify([ERR_FENNEC_MSG]), 1.210 + "setting tab.index logs error"); 1.211 + // end test 1.212 + tab1.close(function() tab.close(function() { 1.213 + loader.unload(); 1.214 + done(); 1.215 + })); 1.216 + } 1.217 + }); 1.218 + } 1.219 + }); 1.220 +}; 1.221 + 1.222 +// TEST: open tab with default options 1.223 +exports.testTabsOpen_alt = function(assert, done) { 1.224 + let { loader, messages } = LoaderWithHookedConsole(); 1.225 + let tabs = loader.require('sdk/tabs'); 1.226 + let url = "data:text/html;charset=utf-8,default"; 1.227 + 1.228 + tabs.open({ 1.229 + url: url, 1.230 + onReady: function(tab) { 1.231 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.232 + assert.equal(tabs.activeTab, tab, "URL of active tab in the current window matches"); 1.233 + assert.equal(tab.isPinned, false, "The new tab is not pinned"); 1.234 + assert.equal(messages.length, 1, "isPinned logs error"); 1.235 + 1.236 + // end test 1.237 + tab.close(function() { 1.238 + loader.unload(); 1.239 + done(); 1.240 + }); 1.241 + } 1.242 + }); 1.243 +}; 1.244 + 1.245 +// TEST: open pinned tab 1.246 +exports.testOpenPinned_alt = function(assert, done) { 1.247 + let { loader, messages } = LoaderWithHookedConsole(); 1.248 + let tabs = loader.require('sdk/tabs'); 1.249 + let url = "about:blank"; 1.250 + 1.251 + tabs.open({ 1.252 + url: url, 1.253 + isPinned: true, 1.254 + onOpen: function(tab) { 1.255 + assert.equal(tab.isPinned, false, "The new tab is pinned"); 1.256 + // We get two error message: one for tabs.open's isPinned argument 1.257 + // and another one for tab.isPinned 1.258 + assert.equal(JSON.stringify(messages), 1.259 + JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), 1.260 + "isPinned logs error"); 1.261 + 1.262 + // end test 1.263 + tab.close(function() { 1.264 + loader.unload(); 1.265 + done(); 1.266 + }); 1.267 + } 1.268 + }); 1.269 +}; 1.270 + 1.271 +// TEST: pin/unpin opened tab 1.272 +exports.testPinUnpin_alt = function(assert, done) { 1.273 + let { loader, messages } = LoaderWithHookedConsole(); 1.274 + let tabs = loader.require('sdk/tabs'); 1.275 + let url = "data:text/html;charset=utf-8,default"; 1.276 + 1.277 + tabs.open({ 1.278 + url: url, 1.279 + onOpen: function(tab) { 1.280 + tab.pin(); 1.281 + assert.equal(tab.isPinned, false, "The tab was pinned correctly"); 1.282 + assert.equal(JSON.stringify(messages), 1.283 + JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), 1.284 + "tab.pin() logs error"); 1.285 + 1.286 + // Clear console messages for the following test 1.287 + messages.length = 0; 1.288 + 1.289 + tab.unpin(); 1.290 + assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); 1.291 + assert.equal(JSON.stringify(messages), 1.292 + JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), 1.293 + "tab.unpin() logs error"); 1.294 + 1.295 + // end test 1.296 + tab.close(function() { 1.297 + loader.unload(); 1.298 + done(); 1.299 + }); 1.300 + } 1.301 + }); 1.302 +}; 1.303 + 1.304 +// TEST: open tab in background 1.305 +exports.testInBackground = function(assert, done) { 1.306 + let activeUrl = tabs.activeTab.url; 1.307 + let url = "data:text/html;charset=utf-8,background"; 1.308 + let window = windows.browserWindows.activeWindow; 1.309 + tabs.once('ready', function onReady(tab) { 1.310 + assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); 1.311 + assert.equal(tab.url, url, "URL of the new background tab matches"); 1.312 + assert.equal(windows.browserWindows.activeWindow, window, "a new window was not opened"); 1.313 + assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); 1.314 + 1.315 + // end test 1.316 + tab.close(done); 1.317 + }); 1.318 + 1.319 + tabs.open({ 1.320 + url: url, 1.321 + inBackground: true 1.322 + }); 1.323 +}; 1.324 + 1.325 +// TEST: open tab in new window 1.326 +exports.testOpenInNewWindow = function(assert, done) { 1.327 + let url = "data:text/html;charset=utf-8,newwindow"; 1.328 + let window = windows.browserWindows.activeWindow; 1.329 + 1.330 + tabs.open({ 1.331 + url: url, 1.332 + inNewWindow: true, 1.333 + onReady: function(tab) { 1.334 + assert.equal(windows.browserWindows.length, 1, "a new window was not opened"); 1.335 + assert.equal(windows.browserWindows.activeWindow, window, "old window is active"); 1.336 + assert.equal(tab.url, url, "URL of the new tab matches"); 1.337 + assert.equal(tabs.activeTab, tab, "tab is the activeTab"); 1.338 + 1.339 + tab.close(done); 1.340 + } 1.341 + }); 1.342 +}; 1.343 + 1.344 +// TEST: onOpen event handler 1.345 +exports.testTabsEvent_onOpen = function(assert, done) { 1.346 + let url = URL.replace('#title#', 'testTabsEvent_onOpen'); 1.347 + let eventCount = 0; 1.348 + 1.349 + // add listener via property assignment 1.350 + function listener1(tab) { 1.351 + eventCount++; 1.352 + }; 1.353 + tabs.on('open', listener1); 1.354 + 1.355 + // add listener via collection add 1.356 + tabs.on('open', function listener2(tab) { 1.357 + assert.equal(++eventCount, 2, "both listeners notified"); 1.358 + tabs.removeListener('open', listener1); 1.359 + tabs.removeListener('open', listener2); 1.360 + 1.361 + // ends test 1.362 + tab.close(done); 1.363 + }); 1.364 + 1.365 + tabs.open(url); 1.366 +}; 1.367 + 1.368 +// TEST: onClose event handler 1.369 +exports.testTabsEvent_onClose = function(assert, done) { 1.370 + let url = "data:text/html;charset=utf-8,onclose"; 1.371 + let eventCount = 0; 1.372 + 1.373 + // add listener via property assignment 1.374 + function listener1(tab) { 1.375 + eventCount++; 1.376 + } 1.377 + tabs.on('close', listener1); 1.378 + 1.379 + // add listener via collection add 1.380 + tabs.on('close', function listener2(tab) { 1.381 + assert.equal(++eventCount, 2, "both listeners notified"); 1.382 + tabs.removeListener('close', listener1); 1.383 + tabs.removeListener('close', listener2); 1.384 + 1.385 + // end test 1.386 + done(); 1.387 + }); 1.388 + 1.389 + tabs.on('ready', function onReady(tab) { 1.390 + tabs.removeListener('ready', onReady); 1.391 + tab.close(); 1.392 + }); 1.393 + 1.394 + tabs.open(url); 1.395 +}; 1.396 + 1.397 +// TEST: onClose event handler when a window is closed 1.398 +exports.testTabsEvent_onCloseWindow = function(assert, done) { 1.399 + let closeCount = 0, individualCloseCount = 0; 1.400 + function listener() { 1.401 + closeCount++; 1.402 + } 1.403 + tabs.on('close', listener); 1.404 + 1.405 + // One tab is already open with the window 1.406 + let openTabs = 0; 1.407 + function testCasePossiblyLoaded(tab) { 1.408 + tab.close(function() { 1.409 + if (++openTabs == 3) { 1.410 + tabs.removeListener("close", listener); 1.411 + 1.412 + assert.equal(closeCount, 3, "Correct number of close events received"); 1.413 + assert.equal(individualCloseCount, 3, 1.414 + "Each tab with an attached onClose listener received a close " + 1.415 + "event when the window was closed"); 1.416 + 1.417 + done(); 1.418 + } 1.419 + }); 1.420 + } 1.421 + 1.422 + tabs.open({ 1.423 + url: "data:text/html;charset=utf-8,tab2", 1.424 + onOpen: testCasePossiblyLoaded, 1.425 + onClose: function() individualCloseCount++ 1.426 + }); 1.427 + 1.428 + tabs.open({ 1.429 + url: "data:text/html;charset=utf-8,tab3", 1.430 + onOpen: testCasePossiblyLoaded, 1.431 + onClose: function() individualCloseCount++ 1.432 + }); 1.433 + 1.434 + tabs.open({ 1.435 + url: "data:text/html;charset=utf-8,tab4", 1.436 + onOpen: testCasePossiblyLoaded, 1.437 + onClose: function() individualCloseCount++ 1.438 + }); 1.439 +}; 1.440 + 1.441 +// TEST: onReady event handler 1.442 +exports.testTabsEvent_onReady = function(assert, done) { 1.443 + let url = "data:text/html;charset=utf-8,onready"; 1.444 + let eventCount = 0; 1.445 + 1.446 + // add listener via property assignment 1.447 + function listener1(tab) { 1.448 + eventCount++; 1.449 + }; 1.450 + tabs.on('ready', listener1); 1.451 + 1.452 + // add listener via collection add 1.453 + tabs.on('ready', function listener2(tab) { 1.454 + assert.equal(++eventCount, 2, "both listeners notified"); 1.455 + tabs.removeListener('ready', listener1); 1.456 + tabs.removeListener('ready', listener2); 1.457 + 1.458 + // end test 1.459 + tab.close(done); 1.460 + }); 1.461 + 1.462 + tabs.open(url); 1.463 +}; 1.464 + 1.465 +// TEST: onActivate event handler 1.466 +exports.testTabsEvent_onActivate = function(assert, done) { 1.467 + let url = "data:text/html;charset=utf-8,onactivate"; 1.468 + let eventCount = 0; 1.469 + 1.470 + // add listener via property assignment 1.471 + function listener1(tab) { 1.472 + eventCount++; 1.473 + }; 1.474 + tabs.on('activate', listener1); 1.475 + 1.476 + // add listener via collection add 1.477 + tabs.on('activate', function listener2(tab) { 1.478 + assert.equal(++eventCount, 2, "both listeners notified"); 1.479 + assert.equal(tab, tabs.activeTab, 'the active tab is correct'); 1.480 + tabs.removeListener('activate', listener1); 1.481 + tabs.removeListener('activate', listener2); 1.482 + 1.483 + // end test 1.484 + tab.close(done); 1.485 + }); 1.486 + 1.487 + tabs.open(url); 1.488 +}; 1.489 + 1.490 +// TEST: onDeactivate event handler 1.491 +exports.testTabsEvent_onDeactivate = function(assert, done) { 1.492 + let url = "data:text/html;charset=utf-8,ondeactivate"; 1.493 + let eventCount = 0; 1.494 + 1.495 + // add listener via property assignment 1.496 + function listener1(tab) { 1.497 + eventCount++; 1.498 + }; 1.499 + tabs.on('deactivate', listener1); 1.500 + 1.501 + // add listener via collection add 1.502 + tabs.on('deactivate', function listener2(tab) { 1.503 + assert.equal(++eventCount, 2, 'both listeners notified'); 1.504 + assert.notEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab'); 1.505 + tabs.removeListener('deactivate', listener1); 1.506 + tabs.removeListener('deactivate', listener2); 1.507 + 1.508 + // end test 1.509 + tab.close(done); 1.510 + }); 1.511 + 1.512 + tabs.on('activate', function onActivate(tab) { 1.513 + tabs.removeListener('activate', onActivate); 1.514 + tabs.open("data:text/html;charset=utf-8,foo"); 1.515 + tab.close(); 1.516 + }); 1.517 + 1.518 + tabs.open(url); 1.519 +}; 1.520 + 1.521 +// TEST: per-tab event handlers 1.522 +exports.testPerTabEvents = function(assert, done) { 1.523 + let eventCount = 0; 1.524 + 1.525 + tabs.open({ 1.526 + url: "data:text/html;charset=utf-8,foo", 1.527 + onOpen: function(tab) { 1.528 + // add listener via property assignment 1.529 + function listener1() { 1.530 + eventCount++; 1.531 + }; 1.532 + tab.on('ready', listener1); 1.533 + 1.534 + // add listener via collection add 1.535 + tab.on('ready', function listener2() { 1.536 + assert.equal(eventCount, 1, "both listeners notified"); 1.537 + tab.removeListener('ready', listener1); 1.538 + tab.removeListener('ready', listener2); 1.539 + 1.540 + // end test 1.541 + tab.close(done); 1.542 + }); 1.543 + } 1.544 + }); 1.545 +}; 1.546 + 1.547 +exports.testUniqueTabIds = function(assert, done) { 1.548 + var tabs = require('sdk/tabs'); 1.549 + var tabIds = {}; 1.550 + var steps = [ 1.551 + function (index) { 1.552 + tabs.open({ 1.553 + url: "data:text/html;charset=utf-8,foo", 1.554 + onOpen: function(tab) { 1.555 + tabIds['tab1'] = tab.id; 1.556 + next(index); 1.557 + } 1.558 + }); 1.559 + }, 1.560 + function (index) { 1.561 + tabs.open({ 1.562 + url: "data:text/html;charset=utf-8,bar", 1.563 + onOpen: function(tab) { 1.564 + tabIds['tab2'] = tab.id; 1.565 + next(index); 1.566 + } 1.567 + }); 1.568 + }, 1.569 + function (index) { 1.570 + assert.notEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique."); 1.571 + done(); 1.572 + } 1.573 + ]; 1.574 + 1.575 + function next(index) { 1.576 + if (index === steps.length) { 1.577 + return; 1.578 + } 1.579 + let fn = steps[index]; 1.580 + index++; 1.581 + fn(index); 1.582 + } 1.583 + 1.584 + next(0); 1.585 +} 1.586 + 1.587 +exports.testOnLoadEventWithDOM = function(assert, done) { 1.588 + let count = 0; 1.589 + let title = 'testOnLoadEventWithDOM'; 1.590 + 1.591 + tabs.open({ 1.592 + url: 'data:text/html;charset=utf-8,<title>' + title + '</title>', 1.593 + inBackground: true, 1.594 + onLoad: function(tab) { 1.595 + assert.equal(tab.title, title, 'tab passed in as arg, load called'); 1.596 + 1.597 + if (++count > 1) { 1.598 + assert.pass('onLoad event called on reload'); 1.599 + tab.close(done); 1.600 + } 1.601 + else { 1.602 + assert.pass('first onLoad event occured'); 1.603 + tab.reload(); 1.604 + } 1.605 + } 1.606 + }); 1.607 +}; 1.608 + 1.609 +require('sdk/test').run(exports);