michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "engines": { michael@0: "Firefox": "*" michael@0: } michael@0: }; michael@0: michael@0: const { Toolbar } = require("sdk/ui/toolbar"); michael@0: const { Loader } = require("sdk/test/loader"); michael@0: const { identify } = require("sdk/ui/id"); michael@0: const { getMostRecentBrowserWindow, open, getOuterId } = require("sdk/window/utils"); michael@0: const { ready, close } = require("sdk/window/helpers"); michael@0: const { defer } = require("sdk/core/promise"); michael@0: const { send, stop, Reactor } = require("sdk/event/utils"); michael@0: const { object } = require("sdk/util/sequence"); michael@0: const { CustomizationInput } = require("sdk/input/customizable-ui"); michael@0: const { OutputPort } = require("sdk/output/system"); michael@0: const output = new OutputPort({ id: "toolbar-change" }); michael@0: michael@0: const wait = (toolbar, event) => { michael@0: let { promise, resolve } = defer(); michael@0: toolbar.once(event, resolve); michael@0: return promise; michael@0: }; michael@0: michael@0: const show = ({id}) => send(output, object([id, {collapsed: false}])); michael@0: const hide = ({id}) => send(output, object([id, {collapsed: true}])); michael@0: const retitle = ({id}, title) => send(output, object([id, {title: title}])); michael@0: michael@0: const isAttached = ({id}, window=getMostRecentBrowserWindow()) => michael@0: !!window.document.getElementById(id); michael@0: michael@0: const isCollapsed = ({id}, window=getMostRecentBrowserWindow()) => michael@0: window.document.getElementById(id).getAttribute("collapsed") === "true"; michael@0: michael@0: const closeViaButton = ({id}, window=getMostRecentBrowserWindow()) => michael@0: window.document.getElementById("close-" + id).click(); michael@0: michael@0: const readTitle = ({id}, window=getMostRecentBrowserWindow()) => michael@0: window.document.getElementById(id).getAttribute("toolbarname"); michael@0: michael@0: exports["test toolbar API"] = function*(assert) { michael@0: assert.throws(() => new Toolbar(), michael@0: /The `option.title`/, michael@0: "toolbar requires title"); michael@0: michael@0: assert.throws(() => new Toolbar({ hidden: false }), michael@0: /The `option.title`/, michael@0: "toolbar requires title"); michael@0: michael@0: const t1 = new Toolbar({ title: "foo" }); michael@0: michael@0: assert.throws(() => new Toolbar({ title: "foo" }), michael@0: /already exists/, michael@0: "can't create identical toolbars"); michael@0: michael@0: assert.ok(t1.id, "toolbar has an id"); michael@0: assert.equal(t1.id, identify(t1), "identify returns toolbar id"); michael@0: assert.deepEqual(t1.items, [], "toolbar items are empty"); michael@0: assert.equal(t1.title, void(0), "title is void until attached"); michael@0: assert.equal(t1.hidden, void(0), "hidden is void until attached"); michael@0: michael@0: yield wait(t1, "attach"); michael@0: michael@0: assert.equal(t1.title, "foo", "title is set after attach"); michael@0: assert.equal(t1.hidden, false, "by default toolbar isn't hidden"); michael@0: michael@0: assert.throws(() => new Toolbar({ title: "foo" }), michael@0: /already exists/, michael@0: "still can't create identical toolbar"); michael@0: michael@0: michael@0: const t2 = new Toolbar({ title: "bar", hidden: true }); michael@0: assert.pass("can create different toolbar though"); michael@0: michael@0: assert.ok(t2.id, "toolbar has an id"); michael@0: assert.equal(t2.id, identify(t2), "identify returns toolbar id"); michael@0: assert.notEqual(t2.id, t1.id, "each toolbar has unique id"); michael@0: michael@0: yield wait(t2, "attach"); michael@0: michael@0: assert.equal(t2.title, "bar", "title is set after attach"); michael@0: assert.equal(t2.hidden, true, "toolbar is hidden as specified"); michael@0: michael@0: t2.destroy(); michael@0: t1.destroy(); michael@0: michael@0: yield wait(t1, "detach"); michael@0: michael@0: assert.equal(t1.title, void(0), "title is voided after detach"); michael@0: assert.equal(t1.hidden, void(0), "hidden is void fater detach"); michael@0: michael@0: michael@0: const t3 = new Toolbar({ title: "foo" }); michael@0: assert.pass("Can create toolbar after identical was detached"); michael@0: michael@0: assert.equal(t3.id, t1.id, "toolbar has a same id"); michael@0: assert.equal(t3.id, identify(t3), "identify returns toolbar.id"); michael@0: assert.equal(t3.title, void(0), "title is void before attach"); michael@0: assert.equal(t3.hidden, void(0), "hidden is void before attach"); michael@0: michael@0: yield wait(t3, "attach"); michael@0: michael@0: assert.equal(t3.title, "foo", "title is set"); michael@0: assert.equal(t3.hidden, false, "toolbar is hidden"); michael@0: michael@0: t3.destroy(); michael@0: michael@0: yield wait(t3, "detach"); michael@0: }; michael@0: michael@0: exports["test show / hide toolbar"] = function*(assert) { michael@0: const t1 = new Toolbar({ title: "foo" }); michael@0: michael@0: yield wait(t1, "attach"); michael@0: michael@0: assert.equal(t1.title, "foo", "title is set after attach"); michael@0: assert.equal(t1.hidden, false, "by default toolbar isn't hidden"); michael@0: assert.ok(isAttached(t1), "toolbar was actually attarched"); michael@0: assert.ok(!isCollapsed(t1), "toolbar isn't collapsed"); michael@0: michael@0: hide(t1); michael@0: assert.equal(t1.hidden, false, "not hidden yet"); michael@0: michael@0: yield wait(t1, "hide"); michael@0: assert.equal(t1.hidden, true, "toolbar got hidden"); michael@0: assert.ok(isCollapsed(t1), "toolbar is collapsed"); michael@0: michael@0: show(t1); michael@0: michael@0: yield wait(t1, "show"); michael@0: assert.equal(t1.hidden, false, "toolbar got shown"); michael@0: assert.ok(!isCollapsed(t1), "toolbar isn't collapsed"); michael@0: michael@0: t1.destroy(); michael@0: yield wait(t1, "detach"); michael@0: assert.ok(!isAttached(t1), "toolbar is no longer attached"); michael@0: }; michael@0: michael@0: exports["test multiple windows & toolbars"] = function*(assert) { michael@0: const w1 = getMostRecentBrowserWindow(); michael@0: const t1 = new Toolbar({ title: "multi window" }); michael@0: michael@0: yield wait(t1, "attach"); michael@0: michael@0: assert.equal(t1.title, "multi window", "title is set after attach"); michael@0: assert.equal(t1.hidden, false, "by default toolbar isn't hidden"); michael@0: assert.ok(isAttached(t1, w1), "toolbar was actually attarched"); michael@0: assert.ok(!isCollapsed(t1, w1), "toolbar isn't collapsed"); michael@0: michael@0: const w2 = open(); michael@0: yield ready(w2); michael@0: michael@0: assert.ok(isAttached(t1, w2), "toolbar was attached to second window"); michael@0: assert.ok(!isCollapsed(t1, w2), "toolbar isn't collabsed"); michael@0: michael@0: hide(t1); michael@0: yield wait(t1, "hide"); michael@0: michael@0: assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2), michael@0: "toolbar is collabsed"); michael@0: michael@0: michael@0: const w3 = open(); michael@0: yield ready(w3); michael@0: michael@0: assert.ok(isAttached(t1, w1) && isAttached(t1, w2) && isAttached(t1, w3), michael@0: "toolbar is attached to all windows"); michael@0: assert.ok(isCollapsed(t1, w3) && isCollapsed(t1, w3) && isCollapsed(t1, w3), michael@0: "toolbar still collapsed in all windows"); michael@0: michael@0: michael@0: const t2 = new Toolbar({ title: "multi hidden", hidden: true }); michael@0: michael@0: yield wait(t2, "attach"); michael@0: michael@0: assert.equal(t2.title, "multi hidden", "title is set after attach"); michael@0: assert.equal(t2.hidden, true, "isn't hidden as specified"); michael@0: michael@0: assert.ok(isAttached(t1, w1) && isAttached(t1, w2) && isAttached(t1, w3), michael@0: "toolbar#1 is still attached"); michael@0: assert.ok(isAttached(t2, w1) && isAttached(t2, w2) && isAttached(t2, w3), michael@0: "toolbar#2 was attached to all windows"); michael@0: michael@0: assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2) && isCollapsed(t1, w3), michael@0: "toolbar#1 is still collapsed"); michael@0: michael@0: assert.ok(isCollapsed(t2, w1) && isCollapsed(t2, w2) && isCollapsed(t2, w3), michael@0: "toolbar#2 is collapsed"); michael@0: michael@0: t1.destroy(); michael@0: yield wait(t1, "detach"); michael@0: michael@0: assert.ok(!isAttached(t1, w1) && !isAttached(t1, w2) && !isAttached(t1, w3), michael@0: "toolbar#1 was detached from all windows"); michael@0: assert.ok(isAttached(t2, w1) && isAttached(t2, w2) && isAttached(t2, w3), michael@0: "toolbar#2 is still attached to all windows"); michael@0: michael@0: yield close(w2); michael@0: michael@0: assert.ok(isAttached(t2, w1) && isAttached(t2, w3), michael@0: "toolbar#2 is still attached to remaining windows"); michael@0: assert.ok(isCollapsed(t2, w1) && isCollapsed(t2, w3), michael@0: "toolbar#2 is still collapsed"); michael@0: michael@0: show(t2); michael@0: yield wait(t2, "show"); michael@0: michael@0: assert.ok(!isCollapsed(t2, w1) && !isCollapsed(t2, w3), michael@0: "toolbar#2 is not collapsed"); michael@0: michael@0: yield close(w3); michael@0: michael@0: assert.ok(isAttached(t2, w1), "still attached to last window"); michael@0: assert.ok(!isCollapsed(t2, w1), "still isn't collapsed"); michael@0: michael@0: t2.destroy(); michael@0: yield wait(t2, "detach"); michael@0: michael@0: assert.ok(!isAttached(t2, w1), "toolbar was removed"); michael@0: }; michael@0: michael@0: exports["test toolbar persistence"] = function*(assert) { michael@0: const t1 = new Toolbar({ title: "per sist ence" }); michael@0: michael@0: yield wait(t1, "attach"); michael@0: michael@0: assert.equal(t1.hidden, false, "toolbar is visible"); michael@0: michael@0: hide(t1); michael@0: yield wait(t1, "hide"); michael@0: michael@0: assert.equal(t1.hidden, true, "toolbar is hidden"); michael@0: assert.ok(isCollapsed(t1), "toolbar is collapsed"); michael@0: michael@0: t1.destroy(); michael@0: michael@0: yield wait(t1, "detach"); michael@0: michael@0: const t2 = new Toolbar({ title: "per sist ence" }); michael@0: michael@0: yield wait(t2, "attach"); michael@0: michael@0: assert.equal(t2.hidden, true, "toolbar persisted state"); michael@0: assert.ok(isCollapsed(t2), "toolbar is collapsed"); michael@0: michael@0: show(t2); michael@0: t2.destroy(); michael@0: michael@0: yield wait(t2, "detach"); michael@0: michael@0: const t3 = new Toolbar({ title: "per sist ence", hidden: true }); michael@0: michael@0: yield wait(t3, "attach"); michael@0: michael@0: assert.equal(t3.hidden, false, "toolbar persisted state & ignored option"); michael@0: assert.ok(!isCollapsed(t3), "toolbar isn1t collapsed"); michael@0: michael@0: t3.destroy(); michael@0: michael@0: yield wait(t3, "detach"); michael@0: }; michael@0: michael@0: michael@0: exports["test toolbar unload"] = function*(assert) { michael@0: // We override add-on id, otherwise two instances of Toolbar host (view.js) michael@0: // handling same updates, cause message port is bound to add-on id. michael@0: const loader = Loader(module, null, null, {id: "toolbar-unload-addon"}); michael@0: const { Toolbar } = loader.require("sdk/ui/toolbar"); michael@0: michael@0: const w1 = getMostRecentBrowserWindow(); michael@0: const w2 = open(); michael@0: michael@0: yield ready(w2); michael@0: michael@0: const t1 = new Toolbar({ title: "unload" }); michael@0: michael@0: yield wait(t1, "attach"); michael@0: michael@0: assert.ok(isAttached(t1, w1) && isAttached(t1, w2), michael@0: "attached to both windows"); michael@0: michael@0: michael@0: loader.unload(); michael@0: michael@0: michael@0: assert.ok(!isAttached(t1, w1) && !isAttached(t1, w2), michael@0: "detached from both windows on unload"); michael@0: michael@0: yield close(w2); michael@0: }; michael@0: michael@0: exports["test toolbar close button"] = function*(assert) { michael@0: const t1 = new Toolbar({ title: "close with button" }); michael@0: michael@0: yield wait(t1, "attach"); michael@0: const w1 = getMostRecentBrowserWindow(); michael@0: const w2 = open(); michael@0: michael@0: yield ready(w2); michael@0: michael@0: assert.ok(!isCollapsed(t1, w1) && !isCollapsed(t1, w2), michael@0: "toolbar isn't collapsed"); michael@0: michael@0: closeViaButton(t1); michael@0: michael@0: yield wait(t1, "hide"); michael@0: michael@0: assert.ok(isCollapsed(t1, w1) && isCollapsed(t1, w2), michael@0: "toolbar was collapsed"); michael@0: michael@0: t1.destroy(); michael@0: yield wait(t1, "detach"); michael@0: yield close(w2); michael@0: }; michael@0: michael@0: exports["test title change"] = function*(assert) { michael@0: const w1 = getMostRecentBrowserWindow(); michael@0: const w2 = open(); michael@0: michael@0: yield ready(w2); michael@0: michael@0: const t1 = new Toolbar({ title: "first title" }); michael@0: const id = t1.id; michael@0: michael@0: yield wait(t1, "attach"); michael@0: michael@0: michael@0: assert.equal(t1.title, "first title", michael@0: "correct title is set"); michael@0: assert.equal(readTitle(t1, w1), "first title", michael@0: "title set in the view of first window"); michael@0: assert.equal(readTitle(t1, w2), "first title", michael@0: "title set in the view of second window"); michael@0: michael@0: retitle(t1, "second title"); michael@0: michael@0: // Hide & show so to make sure changes go through a round michael@0: // loop. michael@0: hide(t1); michael@0: yield wait(t1, "hide"); michael@0: show(t1); michael@0: yield wait(t1, "show"); michael@0: michael@0: assert.equal(t1.id, id, "id remains same"); michael@0: assert.equal(t1.title, "second title", "instance title was updated"); michael@0: assert.equal(readTitle(t1, w1), "second title", michael@0: "title updated in first window"); michael@0: assert.equal(readTitle(t1, w2), "second title", michael@0: "title updated in second window"); michael@0: michael@0: t1.destroy(); michael@0: yield wait(t1, "detach"); michael@0: yield close(w2); michael@0: }; michael@0: michael@0: exports["test toolbar is not customizable"] = function*(assert, done) { michael@0: const { window, document, gCustomizeMode } = getMostRecentBrowserWindow(); michael@0: const outerId = getOuterId(window); michael@0: const input = new CustomizationInput(); michael@0: const customized = defer(); michael@0: const customizedEnd = defer(); michael@0: michael@0: new Reactor({ onStep: value => { michael@0: if (value[outerId] === true) michael@0: customized.resolve(); michael@0: if (value[outerId] === null) michael@0: customizedEnd.resolve(); michael@0: }}).run(input); michael@0: michael@0: const toolbar = new Toolbar({ title: "foo" }); michael@0: michael@0: yield wait(toolbar, "attach"); michael@0: michael@0: let view = document.getElementById(toolbar.id); michael@0: let label = view.querySelector("label"); michael@0: let inner = view.querySelector("toolbar"); michael@0: michael@0: assert.equal(view.getAttribute("customizable"), "false", michael@0: "The outer toolbar is not customizable."); michael@0: michael@0: assert.ok(label.collapsed, michael@0: "The label is not displayed.") michael@0: michael@0: assert.equal(inner.getAttribute("customizable"), "true", michael@0: "The inner toolbar is customizable."); michael@0: michael@0: assert.equal(window.getComputedStyle(inner).visibility, "visible", michael@0: "The inner toolbar is visible."); michael@0: michael@0: // Enter in customization mode michael@0: gCustomizeMode.toggle(); michael@0: michael@0: yield customized.promise; michael@0: michael@0: assert.equal(view.getAttribute("customizable"), "false", michael@0: "The outer toolbar is not customizable."); michael@0: michael@0: assert.equal(label.collapsed, false, michael@0: "The label is displayed.") michael@0: michael@0: assert.equal(inner.getAttribute("customizable"), "true", michael@0: "The inner toolbar is customizable."); michael@0: michael@0: assert.equal(window.getComputedStyle(inner).visibility, "hidden", michael@0: "The inner toolbar is hidden."); michael@0: michael@0: // Exit from customization mode michael@0: gCustomizeMode.toggle(); michael@0: michael@0: yield customizedEnd.promise; michael@0: michael@0: assert.equal(view.getAttribute("customizable"), "false", michael@0: "The outer toolbar is not customizable."); michael@0: michael@0: assert.ok(label.collapsed, michael@0: "The label is not displayed.") michael@0: michael@0: assert.equal(inner.getAttribute("customizable"), "true", michael@0: "The inner toolbar is customizable."); michael@0: michael@0: assert.equal(window.getComputedStyle(inner).visibility, "visible", michael@0: "The inner toolbar is visible."); michael@0: michael@0: toolbar.destroy(); michael@0: }; michael@0: michael@0: exports["test button are attached to toolbar"] = function*(assert) { michael@0: const { document } = getMostRecentBrowserWindow(); michael@0: const { ActionButton, ToggleButton } = require("sdk/ui"); michael@0: const { identify } = require("sdk/ui/id"); michael@0: michael@0: let action = ActionButton({ michael@0: id: "btn-1", michael@0: label: "action", michael@0: icon: "./placeholder.png" michael@0: }); michael@0: michael@0: let toggle = ToggleButton({ michael@0: id: "btn-2", michael@0: label: "toggle", michael@0: icon: "./placeholder.png" michael@0: }); michael@0: michael@0: const toolbar = new Toolbar({ michael@0: title: "foo", michael@0: items: [action, toggle] michael@0: }); michael@0: michael@0: yield wait(toolbar, "attach"); michael@0: michael@0: let actionNode = document.getElementById(identify(action)); michael@0: let toggleNode = document.getElementById(identify(toggle)); michael@0: michael@0: assert.notEqual(actionNode, null, michael@0: "action button exists in the document"); michael@0: michael@0: assert.notEqual(actionNode, null, michael@0: "action button exists in the document"); michael@0: michael@0: assert.notEqual(toggleNode, null, michael@0: "toggle button exists in the document"); michael@0: michael@0: assert.equal(actionNode.nextElementSibling, toggleNode, michael@0: "action button is placed before toggle button"); michael@0: michael@0: assert.equal(actionNode.parentNode.parentNode.id, toolbar.id, michael@0: "buttons are placed in the correct toolbar"); michael@0: michael@0: toolbar.destroy(); michael@0: }; michael@0: michael@0: require("sdk/test").run(exports);