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