diff options
Diffstat (limited to 'toolkit/devtools/app-manager/test')
21 files changed, 1276 insertions, 0 deletions
diff --git a/toolkit/devtools/app-manager/test/browser.ini b/toolkit/devtools/app-manager/test/browser.ini new file mode 100644 index 000000000..f88707390 --- /dev/null +++ b/toolkit/devtools/app-manager/test/browser.ini @@ -0,0 +1,10 @@ +[DEFAULT] +skip-if = e10s # Bug ?????? - devtools tests disabled with e10s +subsuite = devtools +support-files = + head.js + hosted_app.manifest + manifest.webapp + +[browser_manifest_editor.js] +skip-if = true # Bug 989169 - Very intermittent, but App Manager about to be removed diff --git a/toolkit/devtools/app-manager/test/browser_manifest_editor.js b/toolkit/devtools/app-manager/test/browser_manifest_editor.js new file mode 100644 index 000000000..676eff6f1 --- /dev/null +++ b/toolkit/devtools/app-manager/test/browser_manifest_editor.js @@ -0,0 +1,197 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const {Services} = Cu.import("resource://gre/modules/Services.jsm"); + +const MANIFEST_EDITOR_ENABLED = "devtools.appmanager.manifestEditor.enabled"; + +let gManifestWindow, gManifestEditor; + +function test() { + waitForExplicitFinish(); + + Task.spawn(function() { + Services.prefs.setBoolPref(MANIFEST_EDITOR_ENABLED, true); + let tab = yield openAppManager(); + yield selectProjectsPanel(); + yield addSamplePackagedApp(); + yield showSampleProjectDetails(); + + gManifestWindow = getManifestWindow(); + gManifestEditor = getProjectsWindow().UI.manifestEditor; + yield changeManifestValue("name", "the best app"); + yield changeManifestValueBad("name", "the worst app"); + yield addNewManifestProperty("developer", "foo", "bar"); + + // add duplicate property in the same parent doesn't create duplicates + yield addNewManifestProperty("developer", "foo", "bar2"); + + // add propery with same key in other parent is allowed + yield addNewManifestProperty("tester", "foo", "new"); + + yield addNewManifestPropertyBad("developer", "blob", "bob"); + yield removeManifestProperty("developer", "foo"); + gManifestWindow = null; + gManifestEditor = null; + + yield removeSamplePackagedApp(); + yield removeTab(tab); + Services.prefs.setBoolPref(MANIFEST_EDITOR_ENABLED, false); + finish(); + }); +} + +// Wait until the animation from commitHierarchy has completed +function waitForUpdate() { + return waitForTime(gManifestEditor.editor.lazyEmptyDelay + 1); +} + +function changeManifestValue(key, value) { + return Task.spawn(function() { + let propElem = gManifestWindow.document + .querySelector("[id ^= '" + key + "']"); + is(propElem.querySelector(".name").value, key, + "Key doesn't match expected value"); + + let valueElem = propElem.querySelector(".value"); + EventUtils.sendMouseEvent({ type: "mousedown" }, valueElem, gManifestWindow); + + let valueInput = propElem.querySelector(".element-value-input"); + valueInput.value = '"' + value + '"'; + EventUtils.sendKey("RETURN", gManifestWindow); + + yield waitForUpdate(); + // Elements have all been replaced, re-select them + propElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']"); + valueElem = propElem.querySelector(".value"); + is(valueElem.value, '"' + value + '"', + "Value doesn't match expected value"); + + is(gManifestEditor.manifest[key], value, + "Manifest doesn't contain expected value"); + }); +} + +function changeManifestValueBad(key, value) { + return Task.spawn(function() { + let propElem = gManifestWindow.document + .querySelector("[id ^= '" + key + "']"); + is(propElem.querySelector(".name").value, key, + "Key doesn't match expected value"); + + let valueElem = propElem.querySelector(".value"); + EventUtils.sendMouseEvent({ type: "mousedown" }, valueElem, gManifestWindow); + + let valueInput = propElem.querySelector(".element-value-input"); + // Leaving out quotes will result in an error, so no change should be made. + valueInput.value = value; + EventUtils.sendKey("RETURN", gManifestWindow); + + yield waitForUpdate(); + // Elements have all been replaced, re-select them + propElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']"); + valueElem = propElem.querySelector(".value"); + isnot(valueElem.value, '"' + value + '"', + "Value was changed, but it should not have been"); + + isnot(gManifestEditor.manifest[key], value, + "Manifest was changed, but it should not have been"); + }); +} + +function addNewManifestProperty(parent, key, value) { + info("Adding new property - parent: " + parent + "; key: " + key + "; value: " + value + "\n\n"); + return Task.spawn(function() { + let parentElem = gManifestWindow.document + .querySelector("[id ^= '" + parent + "']"); + ok(parentElem, "Found parent element: " + parentElem.id); + + let addPropertyElem = parentElem.querySelector(".variables-view-add-property"); + ok(addPropertyElem, "Found add-property button"); + + EventUtils.sendMouseEvent({ type: "mousedown" }, addPropertyElem, gManifestWindow); + + let nameInput = parentElem.querySelector(".element-name-input"); + nameInput.value = key; + EventUtils.sendKey("TAB", gManifestWindow); + + let valueInput = parentElem.querySelector(".element-value-input"); + valueInput.value = '"' + value + '"'; + EventUtils.sendKey("RETURN", gManifestWindow); + + yield waitForUpdate(); + + parentElem = gManifestWindow.document.querySelector("[id ^= '" + parent + "']"); + let elems = parentElem.querySelectorAll("[id ^= '" + key + "']"); + is(elems.length, 1, "No duplicate property is added"); + + let newElem = elems[0]; + let nameElem = newElem.querySelector(".name"); + is(nameElem.value, key, "Key doesn't match expected Key"); + + ok(key in gManifestEditor.manifest[parent], + "Manifest doesn't contain expected key"); + + let valueElem = newElem.querySelector(".value"); + is(valueElem.value, '"' + value + '"', + "Value doesn't match expected value"); + + is(gManifestEditor.manifest[parent][key], value, + "Manifest doesn't contain expected value"); + }); +} + +function addNewManifestPropertyBad(parent, key, value) { + return Task.spawn(function() { + let parentElem = gManifestWindow.document + .querySelector("[id ^= '" + parent + "']"); + ok(parentElem, + "Found parent element"); + let addPropertyElem = parentElem + .querySelector(".variables-view-add-property"); + ok(addPropertyElem, + "Found add-property button"); + + EventUtils.sendMouseEvent({ type: "mousedown" }, addPropertyElem, gManifestWindow); + + let nameInput = parentElem.querySelector(".element-name-input"); + nameInput.value = key; + EventUtils.sendKey("TAB", gManifestWindow); + + let valueInput = parentElem.querySelector(".element-value-input"); + // Leaving out quotes will result in an error, so no change should be made. + valueInput.value = value; + EventUtils.sendKey("RETURN", gManifestWindow); + + yield waitForUpdate(); + + let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']"); + ok(!newElem, "Key was added, but it should not have been"); + ok(!(key in gManifestEditor.manifest[parent]), + "Manifest contains key, but it should not"); + }); +} + +function removeManifestProperty(parent, key) { + info("*** Remove property test ***"); + + return Task.spawn(function() { + let parentElem = gManifestWindow.document + .querySelector("[id ^= '" + parent + "']"); + ok(parentElem, "Found parent element"); + + let keyExists = key in gManifestEditor.manifest[parent]; + ok(keyExists, + "The manifest contains the key under the expected parent"); + + let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']"); + let removePropertyButton = newElem.querySelector(".variables-view-delete"); + ok(removePropertyButton, "The remove property button was found"); + removePropertyButton.click(); + + yield waitForUpdate(); + + ok(!(key in gManifestEditor.manifest[parent]), "Property was successfully removed"); + }); +} diff --git a/toolkit/devtools/app-manager/test/chrome.ini b/toolkit/devtools/app-manager/test/chrome.ini new file mode 100644 index 000000000..803059f45 --- /dev/null +++ b/toolkit/devtools/app-manager/test/chrome.ini @@ -0,0 +1,11 @@ +[DEFAULT] +support-files = + hosted_app.manifest + validator/* + +[test_connection_store.html] +[test_device_store.html] +[test_projects_store.html] +[test_remain_connected.html] +[test_template.html] +[test_app_validator.html] diff --git a/toolkit/devtools/app-manager/test/head.js b/toolkit/devtools/app-manager/test/head.js new file mode 100644 index 000000000..a8cb42214 --- /dev/null +++ b/toolkit/devtools/app-manager/test/head.js @@ -0,0 +1,175 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const {utils: Cu, classes: Cc, interfaces: Ci} = Components; + +const {Promise: promise} = + Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}); +const {devtools} = + Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); +const {require} = devtools; + +const {AppProjects} = require("devtools/app-manager/app-projects"); + +const APP_MANAGER_URL = "about:app-manager"; +const TEST_BASE = + "chrome://mochitests/content/browser/browser/devtools/app-manager/test/"; +const HOSTED_APP_MANIFEST = TEST_BASE + "hosted_app.manifest"; + +const PACKAGED_APP_DIR_PATH = getTestFilePath("."); + +gDevTools.testing = true; +SimpleTest.registerCleanupFunction(() => { + gDevTools.testing = false; +}); + +function addTab(url, targetWindow = window) { + info("Adding tab: " + url); + + let deferred = promise.defer(); + let targetBrowser = targetWindow.gBrowser; + + targetWindow.focus(); + let tab = targetBrowser.selectedTab = targetBrowser.addTab(url); + let linkedBrowser = tab.linkedBrowser; + + linkedBrowser.addEventListener("load", function onLoad() { + linkedBrowser.removeEventListener("load", onLoad, true); + info("Tab added and finished loading: " + url); + deferred.resolve(tab); + }, true); + + return deferred.promise; +} + +function removeTab(tab, targetWindow = window) { + info("Removing tab."); + + let deferred = promise.defer(); + let targetBrowser = targetWindow.gBrowser; + let tabContainer = targetBrowser.tabContainer; + + tabContainer.addEventListener("TabClose", function onClose(aEvent) { + tabContainer.removeEventListener("TabClose", onClose, false); + info("Tab removed and finished closing."); + deferred.resolve(); + }, false); + + targetBrowser.removeTab(tab); + + return deferred.promise; +} + +function openAppManager() { + return addTab(APP_MANAGER_URL); +} + +function addSampleHostedApp() { + info("Adding sample hosted app"); + let projectsWindow = getProjectsWindow(); + let projectsDocument = projectsWindow.document; + let url = projectsDocument.querySelector("#url-input"); + url.value = HOSTED_APP_MANIFEST; + return projectsWindow.UI.addHosted(); +} + +function removeSampleHostedApp() { + info("Removing sample hosted app"); + return AppProjects.remove(HOSTED_APP_MANIFEST); +} + +function addSamplePackagedApp() { + info("Adding sample packaged app"); + let appDir = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile); + appDir.initWithPath(PACKAGED_APP_DIR_PATH); + return getProjectsWindow().UI.addPackaged(appDir); +} + +function removeSamplePackagedApp() { + info("Removing sample packaged app"); + return AppProjects.remove(PACKAGED_APP_DIR_PATH); +} + +function getProjectsWindow() { + return content.document.querySelector(".projects-panel").contentWindow; +} + +function getManifestWindow() { + return getProjectsWindow().document.querySelector(".variables-view") + .contentWindow; +} + +function waitForProjectsPanel(deferred = promise.defer()) { + info("Wait for projects panel"); + + let projectsWindow = getProjectsWindow(); + let projectsUI = projectsWindow.UI; + if (!projectsUI) { + info("projectsUI false"); + projectsWindow.addEventListener("load", function onLoad() { + info("got load event"); + projectsWindow.removeEventListener("load", onLoad); + waitForProjectsPanel(deferred); + }); + return deferred.promise; + } + + if (projectsUI.isReady) { + info("projectsUI ready"); + deferred.resolve(); + return deferred.promise; + } + + info("projectsUI not ready"); + projectsUI.once("ready", deferred.resolve); + return deferred.promise; +} + +function selectProjectsPanel() { + return Task.spawn(function() { + let projectsButton = content.document.querySelector(".projects-button"); + EventUtils.sendMouseEvent({ type: "click" }, projectsButton, content); + + yield waitForProjectsPanel(); + }); +} + +function waitForProjectSelection() { + info("Wait for project selection"); + + let deferred = promise.defer(); + getProjectsWindow().UI.once("project-selected", deferred.resolve); + return deferred.promise; +} + +function selectFirstProject() { + return Task.spawn(function() { + let projectsFrame = content.document.querySelector(".projects-panel"); + let projectsWindow = projectsFrame.contentWindow; + let projectsDoc = projectsWindow.document; + let projectItem = projectsDoc.querySelector(".project-item"); + EventUtils.sendMouseEvent({ type: "click" }, projectItem, projectsWindow); + + yield waitForProjectSelection(); + }); +} + +function showSampleProjectDetails() { + return Task.spawn(function() { + yield selectProjectsPanel(); + yield selectFirstProject(); + }); +} + +function waitForTick() { + let deferred = promise.defer(); + executeSoon(deferred.resolve); + return deferred.promise; +} + +function waitForTime(aDelay) { + let deferred = promise.defer(); + setTimeout(deferred.resolve, aDelay); + return deferred.promise; +} diff --git a/toolkit/devtools/app-manager/test/hosted_app.manifest b/toolkit/devtools/app-manager/test/hosted_app.manifest new file mode 100644 index 000000000..c23776aff --- /dev/null +++ b/toolkit/devtools/app-manager/test/hosted_app.manifest @@ -0,0 +1,3 @@ +{ + "name": "My hosted app" +} diff --git a/toolkit/devtools/app-manager/test/manifest.webapp b/toolkit/devtools/app-manager/test/manifest.webapp new file mode 100644 index 000000000..caa670bf4 --- /dev/null +++ b/toolkit/devtools/app-manager/test/manifest.webapp @@ -0,0 +1,9 @@ +{ + "name": "My packaged app", + "developer": { + "name": "Foo Bar" + }, + "tester" : { + "who": "qa" + } +} diff --git a/toolkit/devtools/app-manager/test/test_app_validator.html b/toolkit/devtools/app-manager/test/test_app_validator.html new file mode 100644 index 000000000..e9376d644 --- /dev/null +++ b/toolkit/devtools/app-manager/test/test_app_validator.html @@ -0,0 +1,206 @@ +<!DOCTYPE html> + +<html> + + <head> + <meta charset="utf8"> + <title></title> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + </head> + + <body> + + <script type="application/javascript;version=1.8"> + const Cu = Components.utils; + const Cc = Components.classes; + const Ci = Components.interfaces; + Cu.import("resource://testing-common/httpd.js"); + const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + const {require} = devtools; + + const {AppValidator} = require("devtools/app-manager/app-validator"); + const {Services} = Cu.import("resource://gre/modules/Services.jsm"); + const nsFile = Components.Constructor("@mozilla.org/file/local;1", + "nsILocalFile", "initWithPath"); + const cr = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIChromeRegistry); + const strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties"); + let httpserver, origin; + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + httpserver = new HttpServer(); + httpserver.start(-1); + origin = "http://localhost:" + httpserver.identity.primaryPort + "/"; + + next(); + } + + function createHosted(path, manifestFile="/manifest.webapp") { + let dirPath = getTestFilePath("validator/" + path); + httpserver.registerDirectory("/", nsFile(dirPath)); + return new AppValidator({ + type: "hosted", + location: origin + manifestFile + }); + } + + function createPackaged(path) { + let dirPath = getTestFilePath("validator/" + path); + return new AppValidator({ + type: "packaged", + location: dirPath + }); + } + + function next() { + let test = tests.shift(); + if (test) { + try { + test(); + } catch(e) { + console.error("exception", String(e), e, e.stack); + } + } else { + httpserver.stop(function() { + SimpleTest.finish(); + }); + } + } + + let tests = [ + // Test a 100% valid example + function () { + let validator = createHosted("valid"); + validator.validate().then(() => { + is(validator.errors.length, 0, "valid app got no error"); + is(validator.warnings.length, 0, "valid app got no warning"); + + next(); + }); + }, + + function () { + let validator = createPackaged("valid"); + validator.validate().then(() => { + is(validator.errors.length, 0, "valid packaged app got no error"); + is(validator.warnings.length, 0, "valid packaged app got no warning"); + + next(); + }); + }, + + // Test a launch path that returns a 404 + function () { + let validator = createHosted("wrong-launch-path"); + validator.validate().then(() => { + is(validator.errors.length, 1, "app with non-existant launch path got an error"); + is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [origin + "wrong-path.html", 404], 2), + "with the right error message"); + is(validator.warnings.length, 0, "but no warning"); + next(); + }); + }, + function () { + let validator = createPackaged("wrong-launch-path"); + validator.validate().then(() => { + is(validator.errors.length, 1, "app with wrong path got an error"); + let file = nsFile(validator.project.location); + file.append("wrong-path.html"); + let url = Services.io.newFileURI(file); + is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1), + "with the expected message"); + is(validator.warnings.length, 0, "but no warning"); + + next(); + }); + }, + + // Test when using a non-absolute path for launch_path + function () { + let validator = createHosted("non-absolute-path"); + validator.validate().then(() => { + is(validator.errors.length, 1, "app with non absolute path got an error"); + is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1), + "with expected message"); + is(validator.warnings.length, 0, "but no warning"); + next(); + }); + }, + function () { + let validator = createPackaged("non-absolute-path"); + validator.validate().then(() => { + is(validator.errors.length, 1, "app with non absolute path got an error"); + is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1), + "with expected message"); + is(validator.warnings.length, 0, "but no warning"); + next(); + }); + }, + + // Test multiple failures (missing name [error] and icon [warning]) + function () { + let validator = createHosted("no-name-or-icon"); + validator.validate().then(() => { + checkNoNameOrIcon(validator); + }); + }, + function () { + let validator = createPackaged("no-name-or-icon"); + validator.validate().then(() => { + checkNoNameOrIcon(validator); + }); + }, + + // Test a regular URL instead of a direct link to the manifest + function () { + let validator = createHosted("valid", "/"); + validator.validate().then(() => { + is(validator.warnings.length, 0, "manifest found got no warning"); + is(validator.errors.length, 0, "manifest found got no error"); + + next(); + }); + }, + + // Test finding a manifest at origin's root + function () { + let validator = createHosted("valid", "/unexisting-dir"); + validator.validate().then(() => { + is(validator.warnings.length, 0, "manifest found at origin root got no warning"); + is(validator.errors.length, 0, "manifest found at origin root got no error"); + + next(); + }); + }, + + // Test priorization of manifest.webapp at provided location instead of a manifest located at origin's root + function() { + let validator = createHosted("valid", "/alsoValid"); + validator.validate().then(() => { + is(validator.manifest.name, "valid at subfolder", "manifest at subfolder was used"); + + next(); + }); + } + ]; + + function checkNoNameOrIcon(validator) { + is(validator.errors.length, 1, "app with no name has an error"); + is(validator.errors[0], + strings.GetStringFromName("validator.missNameManifestProperty"), + "with expected message"); + is(validator.warnings.length, 1, "app with no icon has a warning"); + is(validator.warnings[0], + strings.GetStringFromName("validator.missIconsManifestProperty"), + "with expected message"); + next(); + } + + </script> + </body> +</html> diff --git a/toolkit/devtools/app-manager/test/test_connection_store.html b/toolkit/devtools/app-manager/test/test_connection_store.html new file mode 100644 index 000000000..ef2f0fd0f --- /dev/null +++ b/toolkit/devtools/app-manager/test/test_connection_store.html @@ -0,0 +1,109 @@ +<!DOCTYPE html> + +<!-- +Bug 901519 - [app manager] data store for connections +--> + +<html> + + <head> + <meta charset="utf8"> + <title></title> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + </head> + + <body> + + <div id="root"> + <span id="status" template='{"type":"textContent","path":"status"}'></span> + <span id="host" template='{"type":"textContent","path":"host"}'></span> + <span id="port" template='{"type":"textContent","path":"port"}'></span> + </div> + + <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/template.js"></script> + <script type="application/javascript;version=1.8"> + const Cu = Components.utils; + Cu.import("resource://gre/modules/devtools/dbg-server.jsm"); + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Cu.import("resource://gre/modules/Services.jsm"); + Cu.import("resource:///modules/devtools/gDevTools.jsm"); + + const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + const {require} = devtools; + + const {ConnectionManager} = require("devtools/client/connection-manager"); + const ConnectionStore = require("devtools/app-manager/connection-store"); + + let connection = ConnectionManager.createConnection(); + let store = new ConnectionStore(connection); + + let root = document.querySelector("#root"); + let status = root.querySelector("#status"); + let host = root.querySelector("#host"); + let port = root.querySelector("#port"); + let template = new Template(root, store, () => {}); + template.start(); + + connection.host = "foobar"; + connection.port = 42; + + is(host.textContent, "foobar", "host updated"); + is(port.textContent, 42, "port updated"); + + let been_through_connecting = false; + let been_through_connected = false; + let been_through_disconnected = false; + + is(status.textContent, "disconnected", "status updated (diconnected)"); + + connection.once("connecting", (e) => { + SimpleTest.executeSoon(() => { + been_through_connecting = true; + is(status.textContent, "connecting", "status updated (connecting)"); + }) + }); + + connection.once("connected", (e) => { + SimpleTest.executeSoon(() => { + been_through_connected = true; + is(status.textContent, "connected", "status updated (connected)"); + connection.disconnect(); + }) + }); + + connection.once("disconnected", (e) => { + SimpleTest.executeSoon(() => { + been_through_disconnected = true; + is(status.textContent, "disconnected", "status updated (disconnected)"); + connection.destroy(); + finishup(); + }) + }); + + function finishup() { + ok(been_through_connecting && + been_through_connected && + been_through_disconnected, "All updates happened"); + DebuggerServer.destroy(); + SimpleTest.finish(); + } + + connection.host = null; // force pipe + connection.port = null; + + connection.connect(); + } + + </script> + </body> +</html> diff --git a/toolkit/devtools/app-manager/test/test_device_store.html b/toolkit/devtools/app-manager/test/test_device_store.html new file mode 100644 index 000000000..1c2767d17 --- /dev/null +++ b/toolkit/devtools/app-manager/test/test_device_store.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> + +<!-- +Bug 901520 - [app manager] data store for device +--> + +<html> + + <head> + <meta charset="utf8"> + <title></title> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + </head> + + <body> + + <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/template.js"></script> + <script type="application/javascript;version=1.8"> + const Cu = Components.utils; + Cu.import("resource://gre/modules/devtools/dbg-server.jsm"); + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + function compare(o1, o2, msg) { + is(JSON.stringify(o1), JSON.stringify(o2), msg); + } + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Cu.import("resource://gre/modules/Services.jsm"); + Cu.import("resource:///modules/devtools/gDevTools.jsm"); + + + const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + const {require} = devtools; + + const {ConnectionManager} = require("devtools/client/connection-manager"); + const DeviceStore = require("devtools/app-manager/device-store"); + + let {getDeviceFront} = devtools.require("devtools/server/actors/device"); + + let connection = ConnectionManager.createConnection(); + let store = new DeviceStore(connection); + + connection.once("connected", function() { + store.on("set", function check(event, path, value) { + if (path.join(".") != "description") return; + store.off("set", check); + info("Connected"); + connection.client.listTabs((resp) => { + info("List tabs response"); + let deviceFront = getDeviceFront(connection.client, resp); + deviceFront.getDescription().then(json => { + info("getDescription response: " + JSON.stringify(json)); + json.dpi = Math.ceil(json.dpi); + for (let key in json) { + compare(json[key], store.object.description[key], "description." + key + " is valid"); + compare(json[key], value[key], "description." + key + " is valid"); + } + connection.disconnect(); + }).then(null, (error) => ok(false, "Error:" + error)); + }); + }); + }); + + connection.once("disconnected", function() { + compare(store.object, {description:{},permissions:[],tabs:[]}, "empty store after disconnect") + connection.destroy(); + DebuggerServer.destroy(); + SimpleTest.finish(); + }); + + compare(store.object, {description:{},permissions:[],tabs:[]}, "empty store before disconnect") + + connection.connect(); + + } + + </script> + </body> +</html> diff --git a/toolkit/devtools/app-manager/test/test_projects_store.html b/toolkit/devtools/app-manager/test/test_projects_store.html new file mode 100644 index 000000000..907404c19 --- /dev/null +++ b/toolkit/devtools/app-manager/test/test_projects_store.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> + +<!-- +Bug 907206 - data store for local apps +--> + +<html> + + <head> + <meta charset="utf8"> + <title></title> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + </head> + + <body> + + <script type="application/javascript;version=1.8"> + const Cu = Components.utils; + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + const {require} = devtools; + + const { AppProjects } = require("devtools/app-manager/app-projects"); + + function testHosted(projects) { + let manifestURL = document.location.href.replace("test_projects_store.html", "hosted_app/webapp.manifest"); + AppProjects.addHosted(manifestURL) + .then(function (app) { + is(projects.length, 1, + "Hosted app has been added"); + is(projects[0], app); + is(app.type, "hosted", "valid type"); + is(app.location, manifestURL, "valid location"); + is(AppProjects.get(manifestURL), app, + "get() returns the same app object"); + AppProjects.remove(manifestURL) + .then(function () { + is(projects.length, 0, + "Hosted app has been removed"); + SimpleTest.finish(); + }); + }); + } + + AppProjects.once("ready", function (event, projects) { + is(projects, AppProjects.store.object.projects, + "The ready event data is the store projects list"); + testHosted(projects); + }); + + } + + </script> + </body> +</html> diff --git a/toolkit/devtools/app-manager/test/test_remain_connected.html b/toolkit/devtools/app-manager/test/test_remain_connected.html new file mode 100644 index 000000000..819e3d6c0 --- /dev/null +++ b/toolkit/devtools/app-manager/test/test_remain_connected.html @@ -0,0 +1,122 @@ +<!DOCTYPE html> + +<!-- +Bug 912646 - Closing app toolbox causes phone to disconnect +--> + +<html> + + <head> + <meta charset="utf8"> + <title></title> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + </head> + + <body> + + <script type="application/javascript;version=1.8"> + const Cu = Components.utils; + + Cu.import("resource://gre/modules/devtools/dbg-server.jsm"); + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Cu.import("resource:///modules/devtools/gDevTools.jsm"); + + const {devtools} = + Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + const {require} = devtools; + + const {Connection, ConnectionManager} = + require("devtools/client/connection-manager"); + const ConnectionStore = + require("devtools/app-manager/connection-store"); + + let connection = ConnectionManager.createConnection(); + + connection.host = null; // force pipe + connection.port = null; + + let been_through_connecting = false; + let been_through_connected = false; + let been_through_disconnected = false; + + is(connection.status, Connection.Status.DISCONNECTED, + "status updated (diconnected)"); + + connection.once("connecting", () => { + SimpleTest.executeSoon(() => { + been_through_connecting = true; + is(connection.status, Connection.Status.CONNECTING, + "status updated (connecting)"); + }) + }); + + connection.once("connected", () => { + SimpleTest.executeSoon(() => { + been_through_connected = true; + is(connection.status, Connection.Status.CONNECTED, + "status updated (connected)"); + cycleToolbox(); + }) + }); + + function cycleToolbox() { + connection.client.listTabs(response => { + let options = { + form: response.tabs[0], + client: connection.client, + chrome: true + }; + devtools.TargetFactory.forRemoteTab(options).then(target => { + let hostType = devtools.Toolbox.HostType.WINDOW; + gDevTools.showToolbox(target, + null, + hostType).then(toolbox => { + SimpleTest.executeSoon(() => { + toolbox.once("destroyed", onDestroyToolbox); + toolbox.destroy(); + }); + }); + }); + }); + } + + function onDestroyToolbox() { + is(connection.status, Connection.Status.CONNECTED, + "toolbox cycled, still connected"); + connection.disconnect(); + } + + connection.once("disconnected", () => { + SimpleTest.executeSoon(() => { + been_through_disconnected = true; + is(connection.status, Connection.Status.DISCONNECTED, + "status updated (disconnected)"); + connection.destroy(); + finishUp(); + }) + }); + + function finishUp() { + ok(been_through_connecting && + been_through_connected && + been_through_disconnected, "All updates happened"); + DebuggerServer.destroy(); + SimpleTest.finish(); + } + + connection.connect(); + } + + </script> + </body> +</html> diff --git a/toolkit/devtools/app-manager/test/test_template.html b/toolkit/devtools/app-manager/test/test_template.html new file mode 100644 index 000000000..b116e2268 --- /dev/null +++ b/toolkit/devtools/app-manager/test/test_template.html @@ -0,0 +1,256 @@ +<!DOCTYPE html> + +<html> + + <head> + <meta charset="utf8"> + <title></title> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + </head> + + <div id="root"> + <span template='{"type":"textContent","path":"title"}'></span> + <span template='{"type":"attribute","name":"title","path":"title"}'></span> + <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'></span> + <div template-for='{"path":"mop","childSelector":"#template-for"}'></div> + <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'></div> + </div> + + <div id="ref0"> + <span template='{"type":"textContent","path":"title"}'>ttt</span> + <span title="ttt" template='{"type":"attribute","name":"title","path":"title"}'></span> + <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo_l10n/bar_l10n</span> + <div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop"}'>meh</span></div> + <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span> + </div> + </div> + </div> + + + <div id="ref1"> + <span template='{"type":"textContent","path":"title"}'>xxx</span> + <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span> + <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo2_l10n/bar_l10n</span> + <div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop"}'>meh2</span></div> + <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span> + </div> + </div> + </div> + + <div id="ref2"> + <span template='{"type":"textContent","path":"title"}'>xxx</span> + <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span> + <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span> + <div template-for='{"path":"","childSelector":"#template-for"}'></div> + <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span> + </div> + </div> + </div> + + <div id="ref3"> + <span template='{"type":"textContent","path":"title"}'>xxx</span> + <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span> + <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span> + <div template-for='{"path":"","childSelector":"#template-for"}'></div> + <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2"}'>xx2</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3"}'>xx3</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.4"}'>xx4</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.4"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.4"}'>b</span> + </div> + </div> + </div> + + <div id="ref4"> + <span template='{"type":"textContent","path":"title"}'>xxx</span> + <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span> + <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span> + <div template-for='{"path":"","childSelector":"#template-for"}'></div> + <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2"}'>xx2</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2"}'>b</span> + </div> + <div> + <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3"}'>xx3</span> + <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3"}'>a</span> + <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3"}'>b</span> + </div> + </div> + </div> + + + + <template id="template-loop"> + <div> + <span template='{"type":"textContent","path":"idx"}'></span> + <span template='{"type":"textContent","path":"a"}'></span> + <span template='{"type":"textContent","path":"b"}'></span> + </div> + </template> + + <template id="template-for"> + <span template='{"type":"textContent","path":"name"}'></span> + </template> + + <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/template.js"></script> + <script type="application/javascript;version=1.8"> + SimpleTest.waitForExplicitFinish(); + + const Cu = Components.utils; + Cu.import("resource:///modules/devtools/gDevTools.jsm"); + const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + const {require} = devtools; + const ObservableObject = require("devtools/shared/observable-object"); + + let data = { + title: "ttt", + mop: { + name: "meh", + }, + foo1: { + bar1: [ + {idx: "xx0", a: "a", b: "b"}, + {idx: "xx1", a: "a", b: "b"}, + ], + }, + foo2: { + foo_l10n: "foo_l10n", + bar_l10n: "bar_l10n" + }, + }; + + let store = new ObservableObject(data); + + let changes = [ + { + exec: function() {}, + reference: document.querySelector("#ref0") + }, + { + exec: function() { + store.object.title = "xxx"; + store.object.foo2.foo_l10n = "foo2_l10n"; + store.object.mop.name = "meh2"; + }, + reference: document.querySelector("#ref1") + }, + { + exec: function() { + store.object.foo2 = { + foo_l10n: "yyy", + bar_l10n: "zzz", + } + let forElt = document.querySelector("#root > [template-for]"); + forElt.setAttribute("template-for", '{"path":"","childSelector":"#template-for"}'); + t._processFor(forElt); + }, + reference: document.querySelector("#ref2") + }, + { + exec: function() { + let items = []; + for (let i = 2; i < 5; i++) { + items.push({idx: "xx" + i, a: "a", b: "b"}); + } + + store.object.foo1.bar1 = store.object.foo1.bar1.concat(items); + }, + reference: document.querySelector("#ref3") + }, + { + exec: function() { + store.object.foo1.bar1.pop(); + }, + reference: document.querySelector("#ref4") + }, + ]; + + function compare(node1, node2) { + let text1 = node1.innerHTML; + let text2 = node2.innerHTML; + text1 = text1.replace(/\n/g,""); + text2 = text2.replace(/\n/g,""); + text1 = text1.replace(/\s+/g,""); + text2 = text2.replace(/\s+/g,""); + return text1 == text2; + } + + + let root = document.querySelector("#root"); + + let t = new Template(root, store, (prop, args) => { + return prop + ":" + args.join("/"); + }); + + t.start(); + + for (let i = 0; i < changes.length; i++) { + let change = changes[i]; + change.exec(); + ok(compare(change.reference, root), "Content " + i + " looks good."); + } + SimpleTest.finish(); + + </script> +</html> diff --git a/toolkit/devtools/app-manager/test/validator/no-name-or-icon/home.html b/toolkit/devtools/app-manager/test/validator/no-name-or-icon/home.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/no-name-or-icon/home.html diff --git a/toolkit/devtools/app-manager/test/validator/no-name-or-icon/manifest.webapp b/toolkit/devtools/app-manager/test/validator/no-name-or-icon/manifest.webapp new file mode 100644 index 000000000..149e3fb79 --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/no-name-or-icon/manifest.webapp @@ -0,0 +1,3 @@ +{ + "launch_path": "/home.html" +} diff --git a/toolkit/devtools/app-manager/test/validator/non-absolute-path/manifest.webapp b/toolkit/devtools/app-manager/test/validator/non-absolute-path/manifest.webapp new file mode 100644 index 000000000..64744067f --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/non-absolute-path/manifest.webapp @@ -0,0 +1,7 @@ +{ + "name": "non-absolute path", + "icons": { + "128": "/icon.png" + }, + "launch_path": "non-absolute.html" +} diff --git a/toolkit/devtools/app-manager/test/validator/valid/alsoValid/manifest.webapp b/toolkit/devtools/app-manager/test/validator/valid/alsoValid/manifest.webapp new file mode 100644 index 000000000..20bd97bba --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/valid/alsoValid/manifest.webapp @@ -0,0 +1,7 @@ +{ + "name": "valid at subfolder", + "launch_path": "/home.html", + "icons": { + "128": "/icon.png" + } +} diff --git a/toolkit/devtools/app-manager/test/validator/valid/home.html b/toolkit/devtools/app-manager/test/validator/valid/home.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/valid/home.html diff --git a/toolkit/devtools/app-manager/test/validator/valid/icon.png b/toolkit/devtools/app-manager/test/validator/valid/icon.png new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/valid/icon.png diff --git a/toolkit/devtools/app-manager/test/validator/valid/manifest.webapp b/toolkit/devtools/app-manager/test/validator/valid/manifest.webapp new file mode 100644 index 000000000..2c22a1567 --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/valid/manifest.webapp @@ -0,0 +1,7 @@ +{ + "name": "valid", + "launch_path": "/home.html", + "icons": { + "128": "/icon.png" + } +} diff --git a/toolkit/devtools/app-manager/test/validator/wrong-launch-path/icon.png b/toolkit/devtools/app-manager/test/validator/wrong-launch-path/icon.png new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/wrong-launch-path/icon.png diff --git a/toolkit/devtools/app-manager/test/validator/wrong-launch-path/manifest.webapp b/toolkit/devtools/app-manager/test/validator/wrong-launch-path/manifest.webapp new file mode 100644 index 000000000..08057bae1 --- /dev/null +++ b/toolkit/devtools/app-manager/test/validator/wrong-launch-path/manifest.webapp @@ -0,0 +1,7 @@ +{ + "name": "valid", + "launch_path": "/wrong-path.html", + "icons": { + "128": "/icon.png" + } +} |