/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { Cc, Ci } = require('chrome');
const { Loader, LoaderWithHookedConsole } = require('sdk/test/loader');
const timer = require('sdk/timers');
const tabs = require('sdk/tabs');
const windows = require('sdk/windows');
const { set: setPref } = require("sdk/preferences/service");
const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
const tabsLen = tabs.length;
const URL = 'data:text/html;charset=utf-8,
#title#';
// Fennec error message dispatched on all currently unimplement tab features,
// that match LoaderWithHookedConsole messages object pattern
const ERR_FENNEC_MSG = {
type: "error",
msg: "This method is not yet supported by Fennec"
};
// TEST: tab unloader
exports.testAutomaticDestroy = function(assert, done) {
let called = false;
let loader2 = Loader(module);
let loader3 = Loader(module);
let tabs2 = loader2.require('sdk/tabs');
let tabs3 = loader3.require('sdk/tabs');
let tabs2Len = tabs2.length;
tabs2.on('open', function onOpen(tab) {
assert.fail("an onOpen listener was called that should not have been");
called = true;
});
tabs2.on('ready', function onReady(tab) {
assert.fail("an onReady listener was called that should not have been");
called = true;
});
tabs2.on('select', function onSelect(tab) {
assert.fail("an onSelect listener was called that should not have been");
called = true;
});
tabs2.on('close', function onClose(tab) {
assert.fail("an onClose listener was called that should not have been");
called = true;
});
loader2.unload();
tabs3.on('open', function onOpen(tab) {
assert.pass("an onOpen listener was called for tabs3");
tab.on('ready', function onReady(tab) {
assert.fail("an onReady listener was called that should not have been");
called = true;
});
tab.on('select', function onSelect(tab) {
assert.fail("an onSelect listener was called that should not have been");
called = true;
});
tab.on('close', function onClose(tab) {
assert.fail("an onClose listener was called that should not have been");
called = true;
});
});
tabs3.open(URL.replace(/#title#/, 'tabs3'));
loader3.unload();
// Fire a tab event and ensure that the destroyed tab is inactive
tabs.once('open', function(tab) {
assert.pass('tabs.once("open") works!');
assert.equal(tabs2Len, tabs2.length, "tabs2 length was not changed");
assert.equal(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length");
tab.once('ready', function() {
assert.pass('tab.once("ready") works!');
tab.once('close', function() {
assert.pass('tab.once("close") works!');
timer.setTimeout(function () {
assert.ok(!called, "Unloaded tab module is destroyed and inactive");
// end test
done();
});
});
tab.close();
});
});
tabs.open('data:text/html;charset=utf-8,foo');
};
// TEST: tab properties
exports.testTabProperties = function(assert, done) {
setPref(DEPRECATE_PREF, true);
let { loader, messages } = LoaderWithHookedConsole();
let tabs = loader.require('sdk/tabs');
let url = "data:text/html;charset=utf-8,foofoo";
let tabsLen = tabs.length;
tabs.open({
url: url,
onReady: function(tab) {
assert.equal(tab.title, "foo", "title of the new tab matches");
assert.equal(tab.url, url, "URL of the new tab matches");
assert.ok(tab.favicon, "favicon of the new tab is not empty");
// TODO: remove need for this test by implementing the favicon feature
assert.equal(messages[0].msg,
"tab.favicon is deprecated, and " +
"currently favicon helpers are not yet supported " +
"by Fennec",
"favicon logs an error for now");
assert.equal(tab.style, null, "style of the new tab matches");
assert.equal(tab.index, tabsLen, "index of the new tab matches");
assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
assert.notEqual(tab.id, null, "a tab object always has an id property");
tab.close(function() {
loader.unload();
// end test
done();
});
}
});
};
// TEST: tabs iterator and length property
exports.testTabsIteratorAndLength = function(assert, done) {
let newTabs = [];
let startCount = 0;
for each (let t in tabs) startCount++;
assert.equal(startCount, tabs.length, "length property is correct");
let url = "data:text/html;charset=utf-8,testTabsIteratorAndLength";
tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)});
tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)});
tabs.open({
url: url,
onOpen: function(tab) {
let count = 0;
for each (let t in tabs) count++;
assert.equal(count, startCount + 3, "iterated tab count matches");
assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property");
let newTabsLength = newTabs.length;
newTabs.forEach(function(t) t.close(function() {
if (--newTabsLength > 0) return;
tab.close(done);
}));
}
});
};
// TEST: tab.url setter
exports.testTabLocation = function(assert, done) {
let url1 = "data:text/html;charset=utf-8,foo";
let url2 = "data:text/html;charset=utf-8,bar";
tabs.on('ready', function onReady(tab) {
if (tab.url != url2)
return;
tabs.removeListener('ready', onReady);
assert.pass("tab loaded the correct url");
tab.close(done);
});
tabs.open({
url: url1,
onOpen: function(tab) {
tab.url = url2;
}
});
};
// TEST: tab.move()
exports.testTabMove = function(assert, done) {
let { loader, messages } = LoaderWithHookedConsole();
let tabs = loader.require('sdk/tabs');
let url = "data:text/html;charset=utf-8,testTabMove";
tabs.open({
url: url,
onOpen: function(tab1) {
assert.ok(tab1.index >= 0, "opening a tab returns a tab w/ valid index");
tabs.open({
url: url,
onOpen: function(tab) {
let i = tab.index;
assert.ok(tab.index > tab1.index, "2nd tab has valid index");
tab.index = 0;
assert.equal(tab.index, i, "tab index after move matches");
assert.equal(JSON.stringify(messages),
JSON.stringify([ERR_FENNEC_MSG]),
"setting tab.index logs error");
// end test
tab1.close(function() tab.close(function() {
loader.unload();
done();
}));
}
});
}
});
};
// TEST: open tab with default options
exports.testTabsOpen_alt = function(assert, done) {
let { loader, messages } = LoaderWithHookedConsole();
let tabs = loader.require('sdk/tabs');
let url = "data:text/html;charset=utf-8,default";
tabs.open({
url: url,
onReady: function(tab) {
assert.equal(tab.url, url, "URL of the new tab matches");
assert.equal(tabs.activeTab, tab, "URL of active tab in the current window matches");
assert.equal(tab.isPinned, false, "The new tab is not pinned");
assert.equal(messages.length, 1, "isPinned logs error");
// end test
tab.close(function() {
loader.unload();
done();
});
}
});
};
// TEST: open pinned tab
exports.testOpenPinned_alt = function(assert, done) {
let { loader, messages } = LoaderWithHookedConsole();
let tabs = loader.require('sdk/tabs');
let url = "about:blank";
tabs.open({
url: url,
isPinned: true,
onOpen: function(tab) {
assert.equal(tab.isPinned, false, "The new tab is pinned");
// We get two error message: one for tabs.open's isPinned argument
// and another one for tab.isPinned
assert.equal(JSON.stringify(messages),
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
"isPinned logs error");
// end test
tab.close(function() {
loader.unload();
done();
});
}
});
};
// TEST: pin/unpin opened tab
exports.testPinUnpin_alt = function(assert, done) {
let { loader, messages } = LoaderWithHookedConsole();
let tabs = loader.require('sdk/tabs');
let url = "data:text/html;charset=utf-8,default";
tabs.open({
url: url,
onOpen: function(tab) {
tab.pin();
assert.equal(tab.isPinned, false, "The tab was pinned correctly");
assert.equal(JSON.stringify(messages),
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
"tab.pin() logs error");
// Clear console messages for the following test
messages.length = 0;
tab.unpin();
assert.equal(tab.isPinned, false, "The tab was unpinned correctly");
assert.equal(JSON.stringify(messages),
JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]),
"tab.unpin() logs error");
// end test
tab.close(function() {
loader.unload();
done();
});
}
});
};
// TEST: open tab in background
exports.testInBackground = function(assert, done) {
let activeUrl = tabs.activeTab.url;
let url = "data:text/html;charset=utf-8,background";
let window = windows.browserWindows.activeWindow;
tabs.once('ready', function onReady(tab) {
assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed");
assert.equal(tab.url, url, "URL of the new background tab matches");
assert.equal(windows.browserWindows.activeWindow, window, "a new window was not opened");
assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL");
// end test
tab.close(done);
});
tabs.open({
url: url,
inBackground: true
});
};
// TEST: open tab in new window
exports.testOpenInNewWindow = function(assert, done) {
let url = "data:text/html;charset=utf-8,newwindow";
let window = windows.browserWindows.activeWindow;
tabs.open({
url: url,
inNewWindow: true,
onReady: function(tab) {
assert.equal(windows.browserWindows.length, 1, "a new window was not opened");
assert.equal(windows.browserWindows.activeWindow, window, "old window is active");
assert.equal(tab.url, url, "URL of the new tab matches");
assert.equal(tabs.activeTab, tab, "tab is the activeTab");
tab.close(done);
}
});
};
// TEST: onOpen event handler
exports.testTabsEvent_onOpen = function(assert, done) {
let url = URL.replace('#title#', 'testTabsEvent_onOpen');
let eventCount = 0;
// add listener via property assignment
function listener1(tab) {
eventCount++;
};
tabs.on('open', listener1);
// add listener via collection add
tabs.on('open', function listener2(tab) {
assert.equal(++eventCount, 2, "both listeners notified");
tabs.removeListener('open', listener1);
tabs.removeListener('open', listener2);
// ends test
tab.close(done);
});
tabs.open(url);
};
// TEST: onClose event handler
exports.testTabsEvent_onClose = function(assert, done) {
let url = "data:text/html;charset=utf-8,onclose";
let eventCount = 0;
// add listener via property assignment
function listener1(tab) {
eventCount++;
}
tabs.on('close', listener1);
// add listener via collection add
tabs.on('close', function listener2(tab) {
assert.equal(++eventCount, 2, "both listeners notified");
tabs.removeListener('close', listener1);
tabs.removeListener('close', listener2);
// end test
done();
});
tabs.on('ready', function onReady(tab) {
tabs.removeListener('ready', onReady);
tab.close();
});
tabs.open(url);
};
// TEST: onClose event handler when a window is closed
exports.testTabsEvent_onCloseWindow = function(assert, done) {
let closeCount = 0, individualCloseCount = 0;
function listener() {
closeCount++;
}
tabs.on('close', listener);
// One tab is already open with the window
let openTabs = 0;
function testCasePossiblyLoaded(tab) {
tab.close(function() {
if (++openTabs == 3) {
tabs.removeListener("close", listener);
assert.equal(closeCount, 3, "Correct number of close events received");
assert.equal(individualCloseCount, 3,
"Each tab with an attached onClose listener received a close " +
"event when the window was closed");
done();
}
});
}
tabs.open({
url: "data:text/html;charset=utf-8,tab2",
onOpen: testCasePossiblyLoaded,
onClose: function() individualCloseCount++
});
tabs.open({
url: "data:text/html;charset=utf-8,tab3",
onOpen: testCasePossiblyLoaded,
onClose: function() individualCloseCount++
});
tabs.open({
url: "data:text/html;charset=utf-8,tab4",
onOpen: testCasePossiblyLoaded,
onClose: function() individualCloseCount++
});
};
// TEST: onReady event handler
exports.testTabsEvent_onReady = function(assert, done) {
let url = "data:text/html;charset=utf-8,onready";
let eventCount = 0;
// add listener via property assignment
function listener1(tab) {
eventCount++;
};
tabs.on('ready', listener1);
// add listener via collection add
tabs.on('ready', function listener2(tab) {
assert.equal(++eventCount, 2, "both listeners notified");
tabs.removeListener('ready', listener1);
tabs.removeListener('ready', listener2);
// end test
tab.close(done);
});
tabs.open(url);
};
// TEST: onActivate event handler
exports.testTabsEvent_onActivate = function(assert, done) {
let url = "data:text/html;charset=utf-8,onactivate";
let eventCount = 0;
// add listener via property assignment
function listener1(tab) {
eventCount++;
};
tabs.on('activate', listener1);
// add listener via collection add
tabs.on('activate', function listener2(tab) {
assert.equal(++eventCount, 2, "both listeners notified");
assert.equal(tab, tabs.activeTab, 'the active tab is correct');
tabs.removeListener('activate', listener1);
tabs.removeListener('activate', listener2);
// end test
tab.close(done);
});
tabs.open(url);
};
// TEST: onDeactivate event handler
exports.testTabsEvent_onDeactivate = function(assert, done) {
let url = "data:text/html;charset=utf-8,ondeactivate";
let eventCount = 0;
// add listener via property assignment
function listener1(tab) {
eventCount++;
};
tabs.on('deactivate', listener1);
// add listener via collection add
tabs.on('deactivate', function listener2(tab) {
assert.equal(++eventCount, 2, 'both listeners notified');
assert.notEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab');
tabs.removeListener('deactivate', listener1);
tabs.removeListener('deactivate', listener2);
// end test
tab.close(done);
});
tabs.on('activate', function onActivate(tab) {
tabs.removeListener('activate', onActivate);
tabs.open("data:text/html;charset=utf-8,foo");
tab.close();
});
tabs.open(url);
};
// TEST: per-tab event handlers
exports.testPerTabEvents = function(assert, done) {
let eventCount = 0;
tabs.open({
url: "data:text/html;charset=utf-8,foo",
onOpen: function(tab) {
// add listener via property assignment
function listener1() {
eventCount++;
};
tab.on('ready', listener1);
// add listener via collection add
tab.on('ready', function listener2() {
assert.equal(eventCount, 1, "both listeners notified");
tab.removeListener('ready', listener1);
tab.removeListener('ready', listener2);
// end test
tab.close(done);
});
}
});
};
exports.testUniqueTabIds = function(assert, done) {
var tabs = require('sdk/tabs');
var tabIds = {};
var steps = [
function (index) {
tabs.open({
url: "data:text/html;charset=utf-8,foo",
onOpen: function(tab) {
tabIds['tab1'] = tab.id;
next(index);
}
});
},
function (index) {
tabs.open({
url: "data:text/html;charset=utf-8,bar",
onOpen: function(tab) {
tabIds['tab2'] = tab.id;
next(index);
}
});
},
function (index) {
assert.notEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
done();
}
];
function next(index) {
if (index === steps.length) {
return;
}
let fn = steps[index];
index++;
fn(index);
}
next(0);
}
require('sdk/test').run(exports);