Remove browser tests - Part 1: The Tests (except for experiments)

This commit is contained in:
NTD 2018-02-03 06:00:38 -05:00 • committed by Roy Tam
commit 261374f310
1697 changed files with 0 additions and 144364 deletions

View file

@ -1,7 +0,0 @@
"use strict";
module.exports = {
"extends": [
"../../../../../testing/mochitest/browser.eslintrc.js"
]
};

View file

@ -1,4 +0,0 @@
[DEFAULT]
support-files = head.js
[browser_sidebar_syncedtabslist.js]

View file

@ -1,410 +0,0 @@
"use strict";
const FIXTURE = [
{
"id": "7cqCr77ptzX3",
"type": "client",
"name": "zcarter's Nightly on MacBook-Pro-25",
"isMobile": false,
"tabs": [
{
"type": "tab",
"title": "Firefox for Android — Mobile Web browser — More ways to customize and protect your privacy — Mozilla",
"url": "https://www.mozilla.org/en-US/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=synced-tabs-sidebar",
"icon": "chrome://mozapps/skin/places/defaultFavicon.png",
"client": "7cqCr77ptzX3",
"lastUsed": 1452124677
}
]
},
{
"id": "2xU5h-4bkWqA",
"type": "client",
"name": "laptop",
"isMobile": false,
"tabs": [
{
"type": "tab",
"title": "Firefox for iOS — Mobile Web browser for your iPhone, iPad and iPod touch — Mozilla",
"url": "https://www.mozilla.org/en-US/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=synced-tabs-sidebar",
"icon": "moz-anno:favicon:https://www.mozilla.org/media/img/firefox/favicon.dc6635050bf5.ico",
"client": "2xU5h-4bkWqA",
"lastUsed": 1451519425
},
{
"type": "tab",
"title": "Firefox Nightly First Run Page",
"url": "https://www.mozilla.org/en-US/firefox/nightly/firstrun/?oldversion=45.0a1",
"icon": "moz-anno:favicon:https://www.mozilla.org/media/img/firefox/favicon-nightly.560395bbb2e1.png",
"client": "2xU5h-4bkWqA",
"lastUsed": 1451519420
},
{
// Should appear first for this client.
"type": "tab",
"title": "Mozilla Developer Network",
"url": "https://developer.mozilla.org/en-US/",
"icon": "moz-anno:favicon:https://developer.cdn.mozilla.net/static/img/favicon32.e02854fdcf73.png",
"client": "2xU5h-4bkWqA",
"lastUsed": 1451519725
}
]
},
{
"id": "OL3EJCsdb2JD",
"type": "client",
"name": "desktop",
"isMobile": false,
"tabs": []
}
];
let originalSyncedTabsInternal = null;
function* testClean() {
let syncedTabsDeckComponent = window.SidebarUI.browser.contentWindow.syncedTabsDeckComponent;
let SyncedTabs = window.SidebarUI.browser.contentWindow.SyncedTabs;
syncedTabsDeckComponent._accountStatus.restore();
SyncedTabs._internal.getTabClients.restore();
SyncedTabs._internal = originalSyncedTabsInternal;
yield new Promise(resolve => {
window.SidebarUI.browser.contentWindow.addEventListener("unload", function listener() {
window.SidebarUI.browser.contentWindow.removeEventListener("unload", listener);
resolve();
});
SidebarUI.hide();
});
}
add_task(function* testSyncedTabsSidebarList() {
yield SidebarUI.show('viewTabsSidebar');
Assert.equal(SidebarUI.currentID, "viewTabsSidebar", "Sidebar should have SyncedTabs loaded");
let syncedTabsDeckComponent = SidebarUI.browser.contentWindow.syncedTabsDeckComponent;
let SyncedTabs = SidebarUI.browser.contentWindow.SyncedTabs;
Assert.ok(syncedTabsDeckComponent, "component exists");
originalSyncedTabsInternal = SyncedTabs._internal;
SyncedTabs._internal = {
isConfiguredToSyncTabs: true,
hasSyncedThisSession: true,
getTabClients() { return Promise.resolve([]) },
syncTabs() { return Promise.resolve(); },
};
sinon.stub(syncedTabsDeckComponent, "_accountStatus", () => Promise.resolve(true));
sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve(Cu.cloneInto(FIXTURE, {})));
yield syncedTabsDeckComponent.updatePanel();
// This is a hacky way of waiting for the view to render. The view renders
// after the following promise (a different instance of which is triggered
// in updatePanel) resolves, so we wait for it here as well
yield syncedTabsDeckComponent.tabListComponent._store.getData();
Assert.ok(SyncedTabs._internal.getTabClients.called, "get clients called");
let selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("tabs-container"),
"tabs panel is selected");
Assert.equal(selectedPanel.querySelectorAll(".tab").length, 4,
"four tabs listed");
Assert.equal(selectedPanel.querySelectorAll(".client").length, 3,
"three clients listed");
Assert.equal(selectedPanel.querySelectorAll(".client")[2].querySelectorAll(".empty").length, 1,
"third client is empty");
// Verify that the tabs are sorted by last used time.
var expectedTabIndices = [[0], [2, 0, 1]];
Array.prototype.forEach.call(selectedPanel.querySelectorAll(".client"), (clientNode, i) => {
checkItem(clientNode, FIXTURE[i]);
Array.prototype.forEach.call(clientNode.querySelectorAll(".tab"), (tabNode, j) => {
let tabIndex = expectedTabIndices[i][j];
checkItem(tabNode, FIXTURE[i].tabs[tabIndex]);
});
});
});
add_task(testClean);
add_task(function* testSyncedTabsSidebarFilteredList() {
yield SidebarUI.show('viewTabsSidebar');
let syncedTabsDeckComponent = window.SidebarUI.browser.contentWindow.syncedTabsDeckComponent;
let SyncedTabs = window.SidebarUI.browser.contentWindow.SyncedTabs;
Assert.ok(syncedTabsDeckComponent, "component exists");
originalSyncedTabsInternal = SyncedTabs._internal;
SyncedTabs._internal = {
isConfiguredToSyncTabs: true,
hasSyncedThisSession: true,
getTabClients() { return Promise.resolve([]) },
syncTabs() { return Promise.resolve(); },
};
sinon.stub(syncedTabsDeckComponent, "_accountStatus", () => Promise.resolve(true));
sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve(Cu.cloneInto(FIXTURE, {})));
yield syncedTabsDeckComponent.updatePanel();
// This is a hacky way of waiting for the view to render. The view renders
// after the following promise (a different instance of which is triggered
// in updatePanel) resolves, so we wait for it here as well
yield syncedTabsDeckComponent.tabListComponent._store.getData();
let filterInput = syncedTabsDeckComponent._window.document.querySelector(".tabsFilter");
filterInput.value = "filter text";
filterInput.blur();
yield syncedTabsDeckComponent.tabListComponent._store.getData("filter text");
let selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("tabs-container"),
"tabs panel is selected");
Assert.equal(selectedPanel.querySelectorAll(".tab").length, 4,
"four tabs listed");
Assert.equal(selectedPanel.querySelectorAll(".client").length, 0,
"no clients are listed");
Assert.equal(filterInput.value, "filter text",
"filter text box has correct value");
// Tabs should not be sorted when filter is active.
let FIXTURE_TABS = FIXTURE.reduce((prev, client) => prev.concat(client.tabs), []);
Array.prototype.forEach.call(selectedPanel.querySelectorAll(".tab"), (tabNode, i) => {
checkItem(tabNode, FIXTURE_TABS[i]);
});
// Removing the filter should resort tabs.
FIXTURE_TABS.sort((a, b) => b.lastUsed - a.lastUsed);
yield syncedTabsDeckComponent.tabListComponent._store.getData();
Array.prototype.forEach.call(selectedPanel.querySelectorAll(".tab"), (tabNode, i) => {
checkItem(tabNode, FIXTURE_TABS[i]);
});
});
add_task(testClean);
add_task(function* testSyncedTabsSidebarStatus() {
let accountExists = false;
yield SidebarUI.show('viewTabsSidebar');
let syncedTabsDeckComponent = window.SidebarUI.browser.contentWindow.syncedTabsDeckComponent;
let SyncedTabs = window.SidebarUI.browser.contentWindow.SyncedTabs;
originalSyncedTabsInternal = SyncedTabs._internal;
SyncedTabs._internal = {
isConfiguredToSyncTabs: false,
hasSyncedThisSession: false,
getTabClients() {},
syncTabs() { return Promise.resolve(); },
};
Assert.ok(syncedTabsDeckComponent, "component exists");
sinon.spy(syncedTabsDeckComponent, "updatePanel");
sinon.spy(syncedTabsDeckComponent, "observe");
sinon.stub(syncedTabsDeckComponent, "_accountStatus", () => Promise.reject("Test error"));
yield syncedTabsDeckComponent.updatePanel();
let selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("notAuthedInfo"),
"not-authed panel is selected on auth error");
syncedTabsDeckComponent._accountStatus.restore();
sinon.stub(syncedTabsDeckComponent, "_accountStatus", () => Promise.resolve(accountExists));
yield syncedTabsDeckComponent.updatePanel();
selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("notAuthedInfo"),
"not-authed panel is selected");
accountExists = true;
yield syncedTabsDeckComponent.updatePanel();
selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("tabs-disabled"),
"tabs disabled panel is selected");
SyncedTabs._internal.isConfiguredToSyncTabs = true;
yield syncedTabsDeckComponent.updatePanel();
selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("tabs-fetching"),
"tabs fetch panel is selected");
SyncedTabs._internal.hasSyncedThisSession = true;
sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve([]));
yield syncedTabsDeckComponent.updatePanel();
selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("singleDeviceInfo"),
"tabs fetch panel is selected");
SyncedTabs._internal.getTabClients.restore();
sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve([{id: "mock"}]));
yield syncedTabsDeckComponent.updatePanel();
selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
Assert.ok(selectedPanel.classList.contains("tabs-container"),
"tabs panel is selected");
});
add_task(testClean);
add_task(function* testSyncedTabsSidebarContextMenu() {
yield SidebarUI.show('viewTabsSidebar');
let syncedTabsDeckComponent = window.SidebarUI.browser.contentWindow.syncedTabsDeckComponent;
let SyncedTabs = window.SidebarUI.browser.contentWindow.SyncedTabs;
Assert.ok(syncedTabsDeckComponent, "component exists");
originalSyncedTabsInternal = SyncedTabs._internal;
SyncedTabs._internal = {
isConfiguredToSyncTabs: true,
hasSyncedThisSession: true,
getTabClients() { return Promise.resolve([]) },
syncTabs() { return Promise.resolve(); },
};
sinon.stub(syncedTabsDeckComponent, "_accountStatus", () => Promise.resolve(true));
sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve(Cu.cloneInto(FIXTURE, {})));
yield syncedTabsDeckComponent.updatePanel();
// This is a hacky way of waiting for the view to render. The view renders
// after the following promise (a different instance of which is triggered
// in updatePanel) resolves, so we wait for it here as well
yield syncedTabsDeckComponent.tabListComponent._store.getData();
info("Right-clicking the search box should show text-related actions");
let filterMenuItems = [
"menuitem[cmd=cmd_undo]",
"menuseparator",
// We don't check whether the commands are enabled due to platform
// differences. On OS X and Windows, "cut" and "copy" are always enabled
// for HTML inputs; on Linux, they're only enabled if text is selected.
"menuitem[cmd=cmd_cut]",
"menuitem[cmd=cmd_copy]",
"menuitem[cmd=cmd_paste]",
"menuitem[cmd=cmd_delete]",
"menuseparator",
"menuitem[cmd=cmd_selectAll]",
"menuseparator",
"menuitem#syncedTabsRefreshFilter",
];
yield* testContextMenu(syncedTabsDeckComponent,
"#SyncedTabsSidebarTabsFilterContext",
".tabsFilter",
filterMenuItems);
info("Right-clicking a tab should show additional actions");
let tabMenuItems = [
["menuitem#syncedTabsOpenSelected", { hidden: false }],
["menuitem#syncedTabsOpenSelectedInTab", { hidden: false }],
["menuitem#syncedTabsOpenSelectedInWindow", { hidden: false }],
["menuitem#syncedTabsOpenSelectedInPrivateWindow", { hidden: false }],
["menuseparator", { hidden: false }],
["menuitem#syncedTabsBookmarkSelected", { hidden: false }],
["menuitem#syncedTabsCopySelected", { hidden: false }],
["menuseparator", { hidden: false }],
["menuitem#syncedTabsRefresh", { hidden: false }],
];
yield* testContextMenu(syncedTabsDeckComponent,
"#SyncedTabsSidebarContext",
"#tab-7cqCr77ptzX3-0",
tabMenuItems);
info("Right-clicking a client shouldn't show any actions");
let sidebarMenuItems = [
["menuitem#syncedTabsOpenSelected", { hidden: true }],
["menuitem#syncedTabsOpenSelectedInTab", { hidden: true }],
["menuitem#syncedTabsOpenSelectedInWindow", { hidden: true }],
["menuitem#syncedTabsOpenSelectedInPrivateWindow", { hidden: true }],
["menuseparator", { hidden: true }],
["menuitem#syncedTabsBookmarkSelected", { hidden: true }],
["menuitem#syncedTabsCopySelected", { hidden: true }],
["menuseparator", { hidden: true }],
["menuitem#syncedTabsRefresh", { hidden: false }],
];
yield* testContextMenu(syncedTabsDeckComponent,
"#SyncedTabsSidebarContext",
"#item-OL3EJCsdb2JD",
sidebarMenuItems);
});
add_task(testClean);
function checkItem(node, item) {
Assert.ok(node.classList.contains("item"),
"Node should have .item class");
if (item.client) {
// tab items
Assert.equal(node.querySelector(".item-title").textContent, item.title,
"Node's title element's text should match item title");
Assert.ok(node.classList.contains("tab"),
"Node should have .tab class");
Assert.equal(node.dataset.url, item.url,
"Node's URL should match item URL");
Assert.equal(node.getAttribute("title"), item.title + "\n" + item.url,
"Tab node should have correct title attribute");
} else {
// client items
Assert.equal(node.querySelector(".item-title").textContent, item.name,
"Node's title element's text should match client name");
Assert.ok(node.classList.contains("client"),
"Node should have .client class");
Assert.equal(node.dataset.id, item.id,
"Node's ID should match item ID");
}
}
function* testContextMenu(syncedTabsDeckComponent, contextSelector, triggerSelector, menuSelectors) {
let contextMenu = document.querySelector(contextSelector);
let triggerElement = syncedTabsDeckComponent._window.document.querySelector(triggerSelector);
let isClosed = triggerElement.classList.contains("closed");
let promisePopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
let chromeWindow = triggerElement.ownerGlobal.top;
let rect = triggerElement.getBoundingClientRect();
let contentRect = chromeWindow.SidebarUI.browser.getBoundingClientRect();
// The offsets in `rect` are relative to the content window, but
// `synthesizeMouseAtPoint` calls `nsIDOMWindowUtils.sendMouseEvent`,
// which interprets the offsets relative to the containing *chrome* window.
// This means we need to account for the width and height of any elements
// outside the `browser` element, like `sidebarheader`.
let offsetX = contentRect.x + rect.x + (rect.width / 2);
let offsetY = contentRect.y + rect.y + (rect.height / 4);
yield EventUtils.synthesizeMouseAtPoint(offsetX, offsetY, {
type: "contextmenu",
button: 2,
}, chromeWindow);
yield promisePopupShown;
is(triggerElement.classList.contains("closed"), isClosed,
"Showing the context menu shouldn't toggle the tab list");
checkChildren(contextMenu, menuSelectors);
let promisePopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
contextMenu.hidePopup();
yield promisePopupHidden;
}
function checkChildren(node, selectors) {
is(node.children.length, selectors.length, "Menu item count doesn't match");
for (let index = 0; index < node.children.length; index++) {
let child = node.children[index];
let [selector, props] = [].concat(selectors[index]);
ok(selector, `Node at ${index} should have selector`);
ok(child.matches(selector), `Node ${
index} should match ${selector}`);
if (props) {
Object.keys(props).forEach(prop => {
is(child[prop], props[prop], `${prop} value at ${index} should match`);
});
}
}
}

View file

@ -1,19 +0,0 @@
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
// Load mocking/stubbing library, sinon
// docs: http://sinonjs.org/docs/
/* global sinon */
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
registerCleanupFunction(function*() {
// Cleanup window or the test runner will throw an error
delete window.sinon;
delete window.setImmediate;
delete window.clearImmediate;
});

View file

@ -1,7 +0,0 @@
"use strict";
module.exports = {
"extends": [
"../../../../../testing/xpcshell/xpcshell.eslintrc.js"
]
};

View file

@ -1,29 +0,0 @@
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
return Components.utils.import("resource://gre/modules/FxAccountsCommon.js", {});
});
Cu.import("resource://gre/modules/Timer.jsm");
do_get_profile(); // fxa needs a profile directory for storage.
// Create a window polyfill so sinon can load
let window = {
document: {},
location: {},
setTimeout: setTimeout,
setInterval: setInterval,
clearTimeout: clearTimeout,
clearinterval: clearInterval
};
let self = window;
// Load mocking/stubbing library, sinon
// docs: http://sinonjs.org/docs/
/* global sinon */
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("resource://testing-common/sinon-1.16.1.js");

View file

@ -1,35 +0,0 @@
"use strict";
let { EventEmitter } = Cu.import("resource:///modules/syncedtabs/EventEmitter.jsm", {});
add_task(function* testSingleListener() {
let eventEmitter = new EventEmitter();
let spy = sinon.spy();
eventEmitter.on("click", spy);
eventEmitter.emit("click", "foo", "bar");
Assert.ok(spy.calledOnce);
Assert.ok(spy.calledWith("foo", "bar"));
eventEmitter.off("click", spy);
eventEmitter.emit("click");
Assert.ok(spy.calledOnce);
});
add_task(function* testMultipleListeners() {
let eventEmitter = new EventEmitter();
let spy1 = sinon.spy();
let spy2 = sinon.spy();
eventEmitter.on("some_event", spy1);
eventEmitter.on("some_event", spy2);
eventEmitter.emit("some_event");
Assert.ok(spy1.calledOnce);
Assert.ok(spy2.calledOnce);
eventEmitter.off("some_event", spy1);
eventEmitter.emit("some_event");
Assert.ok(spy1.calledOnce);
Assert.ok(spy2.calledTwice);
});

View file

@ -1,218 +0,0 @@
"use strict";
let { SyncedTabs } = Cu.import("resource://services-sync/SyncedTabs.jsm", {});
let { SyncedTabsDeckComponent } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckComponent.js", {});
let { TabListComponent } = Cu.import("resource:///modules/syncedtabs/TabListComponent.js", {});
let { SyncedTabsListStore } = Cu.import("resource:///modules/syncedtabs/SyncedTabsListStore.js", {});
let { SyncedTabsDeckStore } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckStore.js", {});
let { TabListView } = Cu.import("resource:///modules/syncedtabs/TabListView.js", {});
let { DeckView } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckView.js", {});
add_task(function* testInitUninit() {
let deckStore = new SyncedTabsDeckStore();
let listComponent = {};
let ViewMock = sinon.stub();
let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}};
ViewMock.returns(view);
sinon.stub(SyncedTabs, "syncTabs", () => Promise.resolve());
sinon.spy(deckStore, "on");
sinon.stub(deckStore, "setPanels");
let component = new SyncedTabsDeckComponent({
window,
deckStore,
listComponent,
SyncedTabs,
DeckView: ViewMock,
});
sinon.stub(component, "updatePanel");
component.init();
Assert.ok(SyncedTabs.syncTabs.called);
SyncedTabs.syncTabs.restore();
Assert.ok(ViewMock.calledWithNew(), "view is instantiated");
Assert.equal(ViewMock.args[0][0], window);
Assert.equal(ViewMock.args[0][1], listComponent);
Assert.ok(ViewMock.args[0][2].onAndroidClick,
"view is passed onAndroidClick prop");
Assert.ok(ViewMock.args[0][2].oniOSClick,
"view is passed oniOSClick prop");
Assert.ok(ViewMock.args[0][2].onSyncPrefClick,
"view is passed onSyncPrefClick prop");
Assert.equal(component.container, view.container,
"component returns view's container");
Assert.ok(deckStore.on.calledOnce, "listener is added to store");
Assert.equal(deckStore.on.args[0][0], "change");
// Object.values only in nightly
let values = Object.keys(component.PANELS).map(k => component.PANELS[k]);
Assert.ok(deckStore.setPanels.calledWith(values),
"panels are set on deck store");
Assert.ok(component.updatePanel.called);
deckStore.emit("change", "mock state");
Assert.ok(view.render.calledWith("mock state"),
"view.render is called on state change");
component.uninit();
Assert.ok(view.destroy.calledOnce, "view is destroyed on uninit");
});
function waitForObserver() {
return new Promise((resolve, reject) => {
Services.obs.addObserver((subject, topic) => {
resolve();
}, SyncedTabs.TOPIC_TABS_CHANGED, false);
});
}
add_task(function* testObserver() {
let deckStore = new SyncedTabsDeckStore();
let listStore = new SyncedTabsListStore(SyncedTabs);
let listComponent = {};
let ViewMock = sinon.stub();
let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}};
ViewMock.returns(view);
sinon.stub(SyncedTabs, "syncTabs", () => Promise.resolve());
sinon.spy(deckStore, "on");
sinon.stub(deckStore, "setPanels");
sinon.stub(listStore, "getData");
let component = new SyncedTabsDeckComponent({
window,
deckStore,
listStore,
listComponent,
SyncedTabs,
DeckView: ViewMock,
});
sinon.spy(component, "observe");
sinon.stub(component, "updatePanel");
component.init();
SyncedTabs.syncTabs.restore();
Assert.ok(component.updatePanel.called, "triggers panel update during init");
Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED, "");
Assert.ok(component.observe.calledWith(null, SyncedTabs.TOPIC_TABS_CHANGED, ""),
"component is notified");
Assert.ok(listStore.getData.called, "gets list data");
Assert.ok(component.updatePanel.calledTwice, "triggers panel update");
Services.obs.notifyObservers(null, FxAccountsCommon.ONLOGIN_NOTIFICATION, "");
Assert.ok(component.observe.calledWith(null, FxAccountsCommon.ONLOGIN_NOTIFICATION, ""),
"component is notified of login");
Assert.equal(component.updatePanel.callCount, 3, "triggers panel update again");
});
add_task(function* testPanelStatus() {
let deckStore = new SyncedTabsDeckStore();
let listStore = new SyncedTabsListStore();
let listComponent = {};
let fxAccounts = {
accountStatus() {}
};
let SyncedTabsMock = {
getTabClients() {}
};
sinon.stub(listStore, "getData");
let component = new SyncedTabsDeckComponent({
fxAccounts,
deckStore,
listComponent,
SyncedTabs: SyncedTabsMock,
});
let isAuthed = false;
sinon.stub(fxAccounts, "accountStatus", () => Promise.resolve(isAuthed));
let result = yield component.getPanelStatus();
Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
isAuthed = true;
SyncedTabsMock.isConfiguredToSyncTabs = false;
result = yield component.getPanelStatus();
Assert.equal(result, component.PANELS.TABS_DISABLED);
SyncedTabsMock.isConfiguredToSyncTabs = true;
SyncedTabsMock.hasSyncedThisSession = false;
result = yield component.getPanelStatus();
Assert.equal(result, component.PANELS.TABS_FETCHING);
SyncedTabsMock.hasSyncedThisSession = true;
let clients = [];
sinon.stub(SyncedTabsMock, "getTabClients", () => Promise.resolve(clients));
result = yield component.getPanelStatus();
Assert.equal(result, component.PANELS.SINGLE_DEVICE_INFO);
clients = ["mock-client"];
result = yield component.getPanelStatus();
Assert.equal(result, component.PANELS.TABS_CONTAINER);
fxAccounts.accountStatus.restore();
sinon.stub(fxAccounts, "accountStatus", () => Promise.reject("err"));
result = yield component.getPanelStatus();
Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
sinon.stub(component, "getPanelStatus", () => Promise.resolve("mock-panelId"));
sinon.spy(deckStore, "selectPanel");
yield component.updatePanel();
Assert.ok(deckStore.selectPanel.calledWith("mock-panelId"));
});
add_task(function* testActions() {
let windowMock = {
openUILink() {},
};
let chromeWindowMock = {
gSyncUI: {
openSetup() {}
}
};
sinon.spy(windowMock, "openUILink");
sinon.spy(chromeWindowMock.gSyncUI, "openSetup");
let getChromeWindowMock = sinon.stub();
getChromeWindowMock.returns(chromeWindowMock);
let component = new SyncedTabsDeckComponent({
window: windowMock,
getChromeWindowMock
});
let href = Services.prefs.getCharPref("identity.mobilepromo.android") + "synced-tabs-sidebar";
component.openAndroidLink("mock-event");
Assert.ok(windowMock.openUILink.calledWith(href, "mock-event"));
href = Services.prefs.getCharPref("identity.mobilepromo.ios") + "synced-tabs-sidebar";
component.openiOSLink("mock-event");
Assert.ok(windowMock.openUILink.calledWith(href, "mock-event"));
component.openSyncPrefs();
Assert.ok(getChromeWindowMock.calledWith(windowMock));
Assert.ok(chromeWindowMock.gSyncUI.openSetup.called);
});

View file

@ -1,64 +0,0 @@
"use strict";
let { SyncedTabsDeckStore } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckStore.js", {});
add_task(function* testSelectUnkownPanel() {
let deckStore = new SyncedTabsDeckStore();
let spy = sinon.spy();
deckStore.on("change", spy);
deckStore.selectPanel("foo");
Assert.ok(!spy.called);
});
add_task(function* testSetPanels() {
let deckStore = new SyncedTabsDeckStore();
let spy = sinon.spy();
deckStore.on("change", spy);
deckStore.setPanels(["panel1", "panel2"]);
Assert.ok(spy.calledWith({
panels: [
{ id: "panel1", selected: false },
{ id: "panel2", selected: false },
],
isUpdatable: false
}));
});
add_task(function* testSelectPanel() {
let deckStore = new SyncedTabsDeckStore();
let spy = sinon.spy();
deckStore.setPanels(["panel1", "panel2"]);
deckStore.on("change", spy);
deckStore.selectPanel("panel2");
Assert.ok(spy.calledWith({
panels: [
{ id: "panel1", selected: false },
{ id: "panel2", selected: true },
],
isUpdatable: true
}));
deckStore.selectPanel("panel2");
Assert.ok(spy.calledOnce, "doesn't trigger unless panel changes");
});
add_task(function* testSetPanelsSameArray() {
let deckStore = new SyncedTabsDeckStore();
let spy = sinon.spy();
deckStore.on("change", spy);
let panels = ["panel1", "panel2"];
deckStore.setPanels(panels);
deckStore.setPanels(panels);
Assert.ok(spy.calledOnce, "doesn't trigger unless set of panels changes");
});

View file

@ -1,266 +0,0 @@
"use strict";
let { SyncedTabs } = Cu.import("resource://services-sync/SyncedTabs.jsm", {});
let { SyncedTabsListStore } = Cu.import("resource:///modules/syncedtabs/SyncedTabsListStore.js", {});
const FIXTURE = [
{
"id": "2xU5h-4bkWqA",
"type": "client",
"name": "laptop",
"isMobile": false,
"tabs": [
{
"type": "tab",
"title": "Firefox for iOS — Mobile Web browser for your iPhone, iPad and iPod touch — Mozilla",
"url": "https://www.mozilla.org/en-US/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=synced-tabs-sidebar",
"icon": "moz-anno:favicon:https://www.mozilla.org/media/img/firefox/favicon.dc6635050bf5.ico",
"client": "2xU5h-4bkWqA",
"lastUsed": 1451519425
},
{
"type": "tab",
"title": "Firefox Nightly First Run Page",
"url": "https://www.mozilla.org/en-US/firefox/nightly/firstrun/?oldversion=45.0a1",
"icon": "moz-anno:favicon:https://www.mozilla.org/media/img/firefox/favicon-nightly.560395bbb2e1.png",
"client": "2xU5h-4bkWqA",
"lastUsed": 1451519420
}
]
},
{
"id": "OL3EJCsdb2JD",
"type": "client",
"name": "desktop",
"isMobile": false,
"tabs": []
}
];
add_task(function* testGetDataEmpty() {
let store = new SyncedTabsListStore(SyncedTabs);
let spy = sinon.spy();
sinon.stub(SyncedTabs, "getTabClients", () => {
return Promise.resolve([]);
});
store.on("change", spy);
yield store.getData();
Assert.ok(SyncedTabs.getTabClients.calledWith(""));
Assert.ok(spy.calledWith({
clients: [],
canUpdateAll: false,
canUpdateInput: false,
filter: "",
inputFocused: false
}));
yield store.getData("filter");
Assert.ok(SyncedTabs.getTabClients.calledWith("filter"));
Assert.ok(spy.calledWith({
clients: [],
canUpdateAll: false,
canUpdateInput: true,
filter: "filter",
inputFocused: false
}));
SyncedTabs.getTabClients.restore();
});
add_task(function* testRowSelectionWithoutFilter() {
let store = new SyncedTabsListStore(SyncedTabs);
let spy = sinon.spy();
sinon.stub(SyncedTabs, "getTabClients", () => {
return Promise.resolve(FIXTURE);
});
yield store.getData();
SyncedTabs.getTabClients.restore();
store.on("change", spy);
store.selectRow(0, -1);
Assert.ok(spy.args[0][0].canUpdateAll, "can update the whole view");
Assert.ok(spy.args[0][0].clients[0].selected, "first client is selected");
store.moveSelectionUp();
Assert.ok(spy.calledOnce,
"can't move up past first client, no change triggered");
store.selectRow(0, 0);
Assert.ok(spy.args[1][0].clients[0].tabs[0].selected,
"first tab of first client is selected");
store.selectRow(0, 0);
Assert.ok(spy.calledTwice, "selecting same row doesn't trigger change");
store.selectRow(0, 1);
Assert.ok(spy.args[2][0].clients[0].tabs[1].selected,
"second tab of first client is selected");
store.selectRow(1);
Assert.ok(spy.args[3][0].clients[1].selected, "second client is selected");
store.moveSelectionDown();
Assert.equal(spy.callCount, 4,
"can't move selection down past last client, no change triggered");
store.moveSelectionUp();
Assert.equal(spy.callCount, 5,
"changed");
Assert.ok(spy.args[4][0].clients[0].tabs[FIXTURE[0].tabs.length - 1].selected,
"move selection up from client selects last tab of previous client");
store.moveSelectionUp();
Assert.ok(spy.args[5][0].clients[0].tabs[FIXTURE[0].tabs.length - 2].selected,
"move selection up from tab selects previous tab of client");
});
add_task(function* testToggleBranches() {
let store = new SyncedTabsListStore(SyncedTabs);
let spy = sinon.spy();
sinon.stub(SyncedTabs, "getTabClients", () => {
return Promise.resolve(FIXTURE);
});
yield store.getData();
SyncedTabs.getTabClients.restore();
store.selectRow(0);
store.on("change", spy);
let clientId = FIXTURE[0].id;
store.closeBranch(clientId);
Assert.ok(spy.args[0][0].clients[0].closed, "first client is closed");
store.openBranch(clientId);
Assert.ok(!spy.args[1][0].clients[0].closed, "first client is open");
store.toggleBranch(clientId);
Assert.ok(spy.args[2][0].clients[0].closed, "first client is toggled closed");
store.moveSelectionDown();
Assert.ok(spy.args[3][0].clients[1].selected,
"selection skips tabs if client is closed");
store.moveSelectionUp();
Assert.ok(spy.args[4][0].clients[0].selected,
"selection skips tabs if client is closed");
});
add_task(function* testRowSelectionWithFilter() {
let store = new SyncedTabsListStore(SyncedTabs);
let spy = sinon.spy();
sinon.stub(SyncedTabs, "getTabClients", () => {
return Promise.resolve(FIXTURE);
});
yield store.getData("filter");
SyncedTabs.getTabClients.restore();
store.on("change", spy);
store.selectRow(0);
Assert.ok(spy.args[0][0].clients[0].tabs[0].selected, "first tab is selected");
store.moveSelectionUp();
Assert.ok(spy.calledOnce,
"can't move up past first tab, no change triggered");
store.moveSelectionDown();
Assert.ok(spy.args[1][0].clients[0].tabs[1].selected,
"selection skips tabs if client is closed");
store.moveSelectionDown();
Assert.equal(spy.callCount, 2,
"can't move selection down past last tab, no change triggered");
store.selectRow(1);
Assert.equal(spy.callCount, 2,
"doesn't trigger change if same row selected");
});
add_task(function* testFilterAndClearFilter() {
let store = new SyncedTabsListStore(SyncedTabs);
let spy = sinon.spy();
sinon.stub(SyncedTabs, "getTabClients", () => {
return Promise.resolve(FIXTURE);
});
store.on("change", spy);
yield store.getData("filter");
Assert.ok(SyncedTabs.getTabClients.calledWith("filter"));
Assert.ok(!spy.args[0][0].canUpdateAll, "can't update all");
Assert.ok(spy.args[0][0].canUpdateInput, "can update just input");
store.selectRow(0);
Assert.equal(spy.args[1][0].filter, "filter");
Assert.ok(spy.args[1][0].clients[0].tabs[0].selected,
"tab is selected");
yield store.clearFilter();
Assert.ok(SyncedTabs.getTabClients.calledWith(""));
Assert.ok(!spy.args[2][0].canUpdateAll, "can't update all");
Assert.ok(!spy.args[2][0].canUpdateInput, "can't just update input");
Assert.equal(spy.args[2][0].filter, "");
Assert.ok(!spy.args[2][0].clients[0].tabs[0].selected,
"tab is no longer selected");
SyncedTabs.getTabClients.restore();
});
add_task(function* testFocusBlurInput() {
let store = new SyncedTabsListStore(SyncedTabs);
let spy = sinon.spy();
sinon.stub(SyncedTabs, "getTabClients", () => {
return Promise.resolve(FIXTURE);
});
store.on("change", spy);
yield store.getData();
SyncedTabs.getTabClients.restore();
Assert.ok(!spy.args[0][0].canUpdateAll, "must rerender all");
store.selectRow(0);
Assert.ok(!spy.args[1][0].inputFocused,
"input is not focused");
Assert.ok(spy.args[1][0].clients[0].selected,
"client is selected");
Assert.ok(spy.args[1][0].clients[0].focused,
"client is focused");
store.focusInput();
Assert.ok(spy.args[2][0].inputFocused,
"input is focused");
Assert.ok(spy.args[2][0].clients[0].selected,
"client is still selected");
Assert.ok(!spy.args[2][0].clients[0].focused,
"client is no longer focused");
store.blurInput();
Assert.ok(!spy.args[3][0].inputFocused,
"input is not focused");
Assert.ok(spy.args[3][0].clients[0].selected,
"client is selected");
Assert.ok(spy.args[3][0].clients[0].focused,
"client is focused");
});

View file

@ -1,155 +0,0 @@
"use strict";
let { SyncedTabs } = Cu.import("resource://services-sync/SyncedTabs.jsm", {});
let { TabListComponent } = Cu.import("resource:///modules/syncedtabs/TabListComponent.js", {});
let { SyncedTabsListStore } = Cu.import("resource:///modules/syncedtabs/SyncedTabsListStore.js", {});
let { View } = Cu.import("resource:///modules/syncedtabs/TabListView.js", {});
const ACTION_METHODS = [
"onSelectRow",
"onOpenTab",
"onOpenTabs",
"onMoveSelectionDown",
"onMoveSelectionUp",
"onToggleBranch",
"onBookmarkTab",
"onSyncRefresh",
"onFilter",
"onClearFilter",
"onFilterFocus",
"onFilterBlur",
];
add_task(function* testInitUninit() {
let store = new SyncedTabsListStore();
let ViewMock = sinon.stub();
let view = {render() {}, destroy() {}};
ViewMock.returns(view);
sinon.spy(view, 'render');
sinon.spy(view, 'destroy');
sinon.spy(store, "on");
sinon.stub(store, "getData");
sinon.stub(store, "focusInput");
let component = new TabListComponent({window, store, View: ViewMock, SyncedTabs});
for (let action of ACTION_METHODS) {
sinon.stub(component, action);
}
component.init();
Assert.ok(ViewMock.calledWithNew(), "view is instantiated");
Assert.ok(store.on.calledOnce, "listener is added to store");
Assert.equal(store.on.args[0][0], "change");
Assert.ok(view.render.calledWith({clients: []}),
"render is called on view instance");
Assert.ok(store.getData.calledOnce, "store gets initial data");
Assert.ok(store.focusInput.calledOnce, "input field is focused");
for (let method of ACTION_METHODS) {
let action = ViewMock.args[0][1][method];
Assert.ok(action, method + " action is passed to View");
action("foo", "bar");
Assert.ok(component[method].calledWith("foo", "bar"),
method + " action passed to View triggers the component method with args");
}
store.emit("change", "mock state");
Assert.ok(view.render.secondCall.calledWith("mock state"),
"view.render is called on state change");
component.uninit();
Assert.ok(view.destroy.calledOnce, "view is destroyed on uninit");
});
add_task(function* testActions() {
let store = new SyncedTabsListStore();
let chromeWindowMock = {
gBrowser: {
loadTabs() {},
},
};
let getChromeWindowMock = sinon.stub();
getChromeWindowMock.returns(chromeWindowMock);
let clipboardHelperMock = {
copyString() {},
};
let windowMock = {
top: {
PlacesCommandHook: {
bookmarkLink() { return Promise.resolve(); }
},
PlacesUtils: { bookmarksMenuFolderId: "id" }
},
getBrowserURL() {},
openDialog() {},
openUILinkIn() {}
};
let component = new TabListComponent({
window: windowMock, store, View: null, SyncedTabs,
clipboardHelper: clipboardHelperMock,
getChromeWindow: getChromeWindowMock });
sinon.stub(store, "getData");
component.onFilter("query");
Assert.ok(store.getData.calledWith("query"));
sinon.stub(store, "clearFilter");
component.onClearFilter();
Assert.ok(store.clearFilter.called);
sinon.stub(store, "focusInput");
component.onFilterFocus();
Assert.ok(store.focusInput.called);
sinon.stub(store, "blurInput");
component.onFilterBlur();
Assert.ok(store.blurInput.called);
sinon.stub(store, "selectRow");
component.onSelectRow([-1, -1]);
Assert.ok(store.selectRow.calledWith(-1, -1));
sinon.stub(store, "moveSelectionDown");
component.onMoveSelectionDown();
Assert.ok(store.moveSelectionDown.called);
sinon.stub(store, "moveSelectionUp");
component.onMoveSelectionUp();
Assert.ok(store.moveSelectionUp.called);
sinon.stub(store, "toggleBranch");
component.onToggleBranch("foo-id");
Assert.ok(store.toggleBranch.calledWith("foo-id"));
sinon.spy(windowMock.top.PlacesCommandHook, "bookmarkLink");
component.onBookmarkTab("uri", "title");
Assert.equal(windowMock.top.PlacesCommandHook.bookmarkLink.args[0][1], "uri");
Assert.equal(windowMock.top.PlacesCommandHook.bookmarkLink.args[0][2], "title");
sinon.spy(windowMock, "openUILinkIn");
component.onOpenTab("uri", "where", "params");
Assert.ok(windowMock.openUILinkIn.calledWith("uri", "where", "params"));
sinon.spy(chromeWindowMock.gBrowser, "loadTabs");
let tabsToOpen = ["uri1", "uri2"];
component.onOpenTabs(tabsToOpen, "where");
Assert.ok(getChromeWindowMock.calledWith(windowMock));
Assert.ok(chromeWindowMock.gBrowser.loadTabs.calledWith(tabsToOpen, false, false));
component.onOpenTabs(tabsToOpen, "tabshifted");
Assert.ok(chromeWindowMock.gBrowser.loadTabs.calledWith(tabsToOpen, true, false));
sinon.spy(clipboardHelperMock, "copyString");
component.onCopyTabLocation("uri");
Assert.ok(clipboardHelperMock.copyString.calledWith("uri"));
sinon.stub(SyncedTabs, "syncTabs");
component.onSyncRefresh();
Assert.ok(SyncedTabs.syncTabs.calledWith(true));
SyncedTabs.syncTabs.restore();
});

View file

@ -1,10 +0,0 @@
[DEFAULT]
head = head.js
tail =
firefox-appdir = browser
[test_EventEmitter.js]
[test_SyncedTabsDeckStore.js]
[test_SyncedTabsListStore.js]
[test_SyncedTabsDeckComponent.js]
[test_TabListComponent.js]