mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-25 18:07:31 +09:00
Remove browser tests - Part 1: The Tests (except for experiments)
This commit is contained in:
parent
fa08bd44f7
commit
261374f310
1697 changed files with 0 additions and 144364 deletions
|
|
@ -1,7 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"../../../../testing/mochitest/browser.eslintrc.js"
|
||||
]
|
||||
};
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
image.png
|
||||
uitour.html
|
||||
../UITour-lib.js
|
||||
|
||||
[browser_backgroundTab.js]
|
||||
[browser_closeTab.js]
|
||||
[browser_fxa.js]
|
||||
skip-if = debug || asan # updateAppMenuItem leaks
|
||||
[browser_no_tabs.js]
|
||||
[browser_openPreferences.js]
|
||||
[browser_openSearchPanel.js]
|
||||
skip-if = true # Bug 1113038 - Intermittent "Popup was opened"
|
||||
[browser_trackingProtection.js]
|
||||
skip-if = os == "linux" # Intermittent NS_ERROR_NOT_AVAILABLE [nsIUrlClassifierDBService.beginUpdate]
|
||||
tag = trackingprotection
|
||||
support-files =
|
||||
!/browser/base/content/test/general/benignPage.html
|
||||
!/browser/base/content/test/general/trackingPage.html
|
||||
[browser_trackingProtection_tour.js]
|
||||
tag = trackingprotection
|
||||
[browser_showMenu_controlCenter.js]
|
||||
tag = trackingprotection
|
||||
[browser_UITour.js]
|
||||
skip-if = os == "linux" # Intermittent failures, bug 951965
|
||||
[browser_UITour2.js]
|
||||
[browser_UITour3.js]
|
||||
skip-if = os == "linux" # Linux: Bug 986760, Bug 989101.
|
||||
[browser_UITour_availableTargets.js]
|
||||
[browser_UITour_annotation_size_attributes.js]
|
||||
[browser_UITour_defaultBrowser.js]
|
||||
[browser_UITour_detach_tab.js]
|
||||
[browser_UITour_forceReaderMode.js]
|
||||
[browser_UITour_heartbeat.js]
|
||||
skip-if = os == "win" # Bug 1277107
|
||||
[browser_UITour_modalDialog.js]
|
||||
skip-if = os != "mac" # modal dialog disabling only working on OS X.
|
||||
[browser_UITour_observe.js]
|
||||
[browser_UITour_panel_close_annotation.js]
|
||||
skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
|
||||
[browser_UITour_pocket.js]
|
||||
skip-if = true # Disabled pending removal of pocket UI Tour
|
||||
[browser_UITour_registerPageID.js]
|
||||
[browser_UITour_resetProfile.js]
|
||||
[browser_UITour_showNewTab.js]
|
||||
[browser_UITour_sync.js]
|
||||
[browser_UITour_toggleReaderMode.js]
|
||||
|
|
@ -1,408 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
Components.utils.import("resource://testing-common/TelemetryArchiveTesting.jsm", this);
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function test_untrusted_host(done) {
|
||||
loadUITourTestPage(function() {
|
||||
let bookmarksMenu = document.getElementById("bookmarks-menu-button");
|
||||
is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
|
||||
|
||||
gContentAPI.showMenu("bookmarks");
|
||||
is(bookmarksMenu.open, false, "Bookmark menu should not open on a untrusted host");
|
||||
|
||||
done();
|
||||
}, "http://mochi.test:8888/");
|
||||
},
|
||||
function test_testing_host(done) {
|
||||
// Add two testing origins intentionally surrounded by whitespace to be ignored.
|
||||
Services.prefs.setCharPref("browser.uitour.testingOrigins",
|
||||
"https://test1.example.org, https://test2.example.org:443 ");
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("browser.uitour.testingOrigins");
|
||||
});
|
||||
function callback(result) {
|
||||
ok(result, "Callback should be called on a testing origin");
|
||||
done();
|
||||
}
|
||||
|
||||
loadUITourTestPage(function() {
|
||||
gContentAPI.getConfiguration("appinfo", callback);
|
||||
}, "https://test2.example.org/");
|
||||
},
|
||||
function test_unsecure_host(done) {
|
||||
loadUITourTestPage(function() {
|
||||
let bookmarksMenu = document.getElementById("bookmarks-menu-button");
|
||||
is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
|
||||
|
||||
gContentAPI.showMenu("bookmarks");
|
||||
is(bookmarksMenu.open, false, "Bookmark menu should not open on a unsecure host");
|
||||
|
||||
done();
|
||||
}, "http://example.org/");
|
||||
},
|
||||
function test_unsecure_host_override(done) {
|
||||
Services.prefs.setBoolPref("browser.uitour.requireSecure", false);
|
||||
loadUITourTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
waitForElementToBeVisible(highlight, done, "Highlight should be shown on a unsecure host when override pref is set");
|
||||
|
||||
Services.prefs.setBoolPref("browser.uitour.requireSecure", true);
|
||||
}, "http://example.org/");
|
||||
},
|
||||
function test_disabled(done) {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", false);
|
||||
|
||||
let bookmarksMenu = document.getElementById("bookmarks-menu-button");
|
||||
is(bookmarksMenu.open, false, "Bookmark menu should initially be closed");
|
||||
|
||||
gContentAPI.showMenu("bookmarks");
|
||||
is(bookmarksMenu.open, false, "Bookmark menu should not open when feature is disabled");
|
||||
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
done();
|
||||
},
|
||||
function test_highlight(done) {
|
||||
function test_highlight_2() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
gContentAPI.hideHighlight();
|
||||
|
||||
waitForElementToBeHidden(highlight, test_highlight_3, "Highlight should be hidden after hideHighlight()");
|
||||
}
|
||||
function test_highlight_3() {
|
||||
is_element_hidden(highlight, "Highlight should be hidden after hideHighlight()");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
waitForElementToBeVisible(highlight, test_highlight_4, "Highlight should be shown after showHighlight()");
|
||||
}
|
||||
function test_highlight_4() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
gContentAPI.showHighlight("backForward");
|
||||
waitForElementToBeVisible(highlight, done, "Highlight should be shown after showHighlight()");
|
||||
}
|
||||
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
waitForElementToBeVisible(highlight, test_highlight_2, "Highlight should be shown after showHighlight()");
|
||||
},
|
||||
function test_highlight_circle(done) {
|
||||
function check_highlight_size() {
|
||||
let panel = highlight.parentElement;
|
||||
let anchor = panel.anchorNode;
|
||||
let anchorRect = anchor.getBoundingClientRect();
|
||||
info("addons target: width: " + anchorRect.width + " height: " + anchorRect.height);
|
||||
let maxDimension = Math.round(Math.max(anchorRect.width, anchorRect.height));
|
||||
let highlightRect = highlight.getBoundingClientRect();
|
||||
info("highlight: width: " + highlightRect.width + " height: " + highlightRect.height);
|
||||
is(Math.round(highlightRect.width), maxDimension, "The width of the highlight should be equal to the largest dimension of the target");
|
||||
is(Math.round(highlightRect.height), maxDimension, "The height of the highlight should be equal to the largest dimension of the target");
|
||||
is(Math.round(highlightRect.height), Math.round(highlightRect.width), "The height and width of the highlight should be the same to create a circle");
|
||||
is(highlight.style.borderRadius, "100%", "The border-radius should be 100% to create a circle");
|
||||
done();
|
||||
}
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("addons");
|
||||
waitForElementToBeVisible(highlight, check_highlight_size, "Highlight should be shown after showHighlight()");
|
||||
},
|
||||
function test_highlight_customize_auto_open_close(done) {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
gContentAPI.showHighlight("customize");
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Move the highlight outside which should close the app menu.
|
||||
gContentAPI.showHighlight("appMenu");
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsClosed() {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel should have closed after the highlight moved elsewhere.");
|
||||
done();
|
||||
}, "Highlight should move to the appMenu button");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
},
|
||||
function test_highlight_customize_manual_open_close(done) {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
// Manually open the app menu then show a highlight there. The menu should remain open.
|
||||
let shownPromise = promisePanelShown(window);
|
||||
gContentAPI.showMenu("appMenu");
|
||||
shownPromise.then(() => {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
gContentAPI.showHighlight("customize");
|
||||
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsStillOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should still be open");
|
||||
|
||||
// Move the highlight outside which shouldn't close the app menu since it was manually opened.
|
||||
gContentAPI.showHighlight("appMenu");
|
||||
waitForElementToBeVisible(highlight, function () {
|
||||
isnot(PanelUI.panel.state, "closed",
|
||||
"Panel should remain open since UITour didn't open it in the first place");
|
||||
gContentAPI.hideMenu("appMenu");
|
||||
done();
|
||||
}, "Highlight should move to the appMenu button");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
}).then(null, Components.utils.reportError);
|
||||
},
|
||||
function test_highlight_effect(done) {
|
||||
function waitForHighlightWithEffect(highlightEl, effect, next, error) {
|
||||
return waitForCondition(() => highlightEl.getAttribute("active") == effect,
|
||||
next,
|
||||
error);
|
||||
}
|
||||
function checkDefaultEffect() {
|
||||
is(highlight.getAttribute("active"), "none", "The default should be no effect");
|
||||
|
||||
gContentAPI.showHighlight("urlbar", "none");
|
||||
waitForHighlightWithEffect(highlight, "none", checkZoomEffect, "There should be no effect");
|
||||
}
|
||||
function checkZoomEffect() {
|
||||
gContentAPI.showHighlight("urlbar", "zoom");
|
||||
waitForHighlightWithEffect(highlight, "zoom", () => {
|
||||
let style = window.getComputedStyle(highlight);
|
||||
is(style.animationName, "uitour-zoom", "The animation-name should be uitour-zoom");
|
||||
checkSameEffectOnDifferentTarget();
|
||||
}, "There should be a zoom effect");
|
||||
}
|
||||
function checkSameEffectOnDifferentTarget() {
|
||||
gContentAPI.showHighlight("appMenu", "wobble");
|
||||
waitForHighlightWithEffect(highlight, "wobble", () => {
|
||||
highlight.addEventListener("animationstart", function onAnimationStart(aEvent) {
|
||||
highlight.removeEventListener("animationstart", onAnimationStart);
|
||||
ok(true, "Animation occurred again even though the effect was the same");
|
||||
checkRandomEffect();
|
||||
});
|
||||
gContentAPI.showHighlight("backForward", "wobble");
|
||||
}, "There should be a wobble effect");
|
||||
}
|
||||
function checkRandomEffect() {
|
||||
function waitForActiveHighlight(highlightEl, next, error) {
|
||||
return waitForCondition(() => highlightEl.hasAttribute("active"),
|
||||
next,
|
||||
error);
|
||||
}
|
||||
|
||||
gContentAPI.hideHighlight();
|
||||
gContentAPI.showHighlight("urlbar", "random");
|
||||
waitForActiveHighlight(highlight, () => {
|
||||
ok(highlight.hasAttribute("active"), "The highlight should be active");
|
||||
isnot(highlight.getAttribute("active"), "none", "A random effect other than none should have been chosen");
|
||||
isnot(highlight.getAttribute("active"), "random", "The random effect shouldn't be 'random'");
|
||||
isnot(UITour.highlightEffects.indexOf(highlight.getAttribute("active")), -1, "Check that a supported effect was randomly chosen");
|
||||
done();
|
||||
}, "There should be an active highlight with a random effect");
|
||||
}
|
||||
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
waitForElementToBeVisible(highlight, checkDefaultEffect, "Highlight should be shown after showHighlight()");
|
||||
},
|
||||
function test_highlight_effect_unsupported(done) {
|
||||
function checkUnsupportedEffect() {
|
||||
is(highlight.getAttribute("active"), "none", "No effect should be used when an unsupported effect is requested");
|
||||
done();
|
||||
}
|
||||
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar", "__UNSUPPORTED__");
|
||||
waitForElementToBeVisible(highlight, checkUnsupportedEffect, "Highlight should be shown after showHighlight()");
|
||||
},
|
||||
function test_info_1(done) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let title = document.getElementById("UITourTooltipTitle");
|
||||
let desc = document.getElementById("UITourTooltipDescription");
|
||||
let icon = document.getElementById("UITourTooltipIcon");
|
||||
let buttons = document.getElementById("UITourTooltipButtons");
|
||||
|
||||
popup.addEventListener("popupshown", function onPopupShown() {
|
||||
popup.removeEventListener("popupshown", onPopupShown);
|
||||
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
|
||||
is(title.textContent, "test title", "Popup should have correct title");
|
||||
is(desc.textContent, "test text", "Popup should have correct description text");
|
||||
is(icon.src, "", "Popup should have no icon");
|
||||
is(buttons.hasChildNodes(), false, "Popup should have no buttons");
|
||||
|
||||
popup.addEventListener("popuphidden", function onPopupHidden() {
|
||||
popup.removeEventListener("popuphidden", onPopupHidden);
|
||||
|
||||
popup.addEventListener("popupshown", function onPopupShown() {
|
||||
popup.removeEventListener("popupshown", onPopupShown);
|
||||
done();
|
||||
});
|
||||
|
||||
gContentAPI.showInfo("urlbar", "test title", "test text");
|
||||
|
||||
});
|
||||
gContentAPI.hideInfo();
|
||||
});
|
||||
|
||||
gContentAPI.showInfo("urlbar", "test title", "test text");
|
||||
},
|
||||
taskify(function* test_info_2() {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let title = document.getElementById("UITourTooltipTitle");
|
||||
let desc = document.getElementById("UITourTooltipDescription");
|
||||
let icon = document.getElementById("UITourTooltipIcon");
|
||||
let buttons = document.getElementById("UITourTooltipButtons");
|
||||
|
||||
yield showInfoPromise("urlbar", "urlbar title", "urlbar text");
|
||||
|
||||
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
|
||||
is(title.textContent, "urlbar title", "Popup should have correct title");
|
||||
is(desc.textContent, "urlbar text", "Popup should have correct description text");
|
||||
is(icon.src, "", "Popup should have no icon");
|
||||
is(buttons.hasChildNodes(), false, "Popup should have no buttons");
|
||||
|
||||
yield showInfoPromise("search", "search title", "search text");
|
||||
|
||||
is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
|
||||
is(title.textContent, "search title", "Popup should have correct title");
|
||||
is(desc.textContent, "search text", "Popup should have correct description text");
|
||||
}),
|
||||
function test_getConfigurationVersion(done) {
|
||||
function callback(result) {
|
||||
let props = ["defaultUpdateChannel", "version"];
|
||||
for (let property of props) {
|
||||
ok(typeof(result[property]) !== "undefined", "Check " + property + " isn't undefined.");
|
||||
is(result[property], Services.appinfo[property], "Should have the same " + property + " property.");
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.getConfiguration("appinfo", callback);
|
||||
},
|
||||
function test_getConfigurationDistribution(done) {
|
||||
gContentAPI.getConfiguration("appinfo", (result) => {
|
||||
ok(typeof(result.distribution) !== "undefined", "Check distribution isn't undefined.");
|
||||
is(result.distribution, "default", "Should be \"default\" without preference set.");
|
||||
|
||||
let defaults = Services.prefs.getDefaultBranch("distribution.");
|
||||
let testDistributionID = "TestDistribution";
|
||||
defaults.setCharPref("id", testDistributionID);
|
||||
gContentAPI.getConfiguration("appinfo", (result) => {
|
||||
ok(typeof(result.distribution) !== "undefined", "Check distribution isn't undefined.");
|
||||
is(result.distribution, testDistributionID, "Should have the distribution as set in preference.");
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
function test_addToolbarButton(done) {
|
||||
let placement = CustomizableUI.getPlacementOfWidget("panic-button");
|
||||
is(placement, null, "default UI has panic button in the palette");
|
||||
|
||||
gContentAPI.getConfiguration("availableTargets", (data) => {
|
||||
let available = (data.targets.indexOf("forget") != -1);
|
||||
ok(!available, "Forget button should not be available by default");
|
||||
|
||||
gContentAPI.addNavBarWidget("forget", () => {
|
||||
info("addNavBarWidget callback successfully called");
|
||||
|
||||
let placement = CustomizableUI.getPlacementOfWidget("panic-button");
|
||||
is(placement.area, CustomizableUI.AREA_NAVBAR);
|
||||
|
||||
gContentAPI.getConfiguration("availableTargets", (data) => {
|
||||
let available = (data.targets.indexOf("forget") != -1);
|
||||
ok(available, "Forget button should now be available");
|
||||
|
||||
// Cleanup
|
||||
CustomizableUI.removeWidgetFromArea("panic-button");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
function test_search(done) {
|
||||
Services.search.init(rv => {
|
||||
if (!Components.isSuccessCode(rv)) {
|
||||
ok(false, "search service init failed: " + rv);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
let defaultEngine = Services.search.defaultEngine;
|
||||
gContentAPI.getConfiguration("search", data => {
|
||||
let visibleEngines = Services.search.getVisibleEngines();
|
||||
let expectedEngines = visibleEngines.filter((engine) => engine.identifier)
|
||||
.map((engine) => "searchEngine-" + engine.identifier);
|
||||
|
||||
let engines = data.engines;
|
||||
ok(Array.isArray(engines), "data.engines should be an array");
|
||||
is(engines.sort().toString(), expectedEngines.sort().toString(),
|
||||
"Engines should be as expected");
|
||||
|
||||
is(data.searchEngineIdentifier, defaultEngine.identifier,
|
||||
"the searchEngineIdentifier property should contain the defaultEngine's identifier");
|
||||
|
||||
let someOtherEngineID = data.engines.filter(t => t != "searchEngine-" + defaultEngine.identifier)[0];
|
||||
someOtherEngineID = someOtherEngineID.replace(/^searchEngine-/, "");
|
||||
|
||||
let observe = function (subject, topic, verb) {
|
||||
info("browser-search-engine-modified: " + verb);
|
||||
if (verb == "engine-current") {
|
||||
is(Services.search.defaultEngine.identifier, someOtherEngineID, "correct engine was switched to");
|
||||
done();
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observe, "browser-search-engine-modified", false);
|
||||
registerCleanupFunction(() => {
|
||||
// Clean up
|
||||
Services.obs.removeObserver(observe, "browser-search-engine-modified");
|
||||
Services.search.defaultEngine = defaultEngine;
|
||||
});
|
||||
|
||||
gContentAPI.setDefaultSearchEngine(someOtherEngineID);
|
||||
});
|
||||
});
|
||||
},
|
||||
taskify(function* test_treatment_tag() {
|
||||
let ac = new TelemetryArchiveTesting.Checker();
|
||||
yield ac.promiseInit();
|
||||
yield gContentAPI.setTreatmentTag("foobar", "baz");
|
||||
// Wait until the treatment telemetry is sent before looking in the archive.
|
||||
yield BrowserTestUtils.waitForContentEvent(gTestTab.linkedBrowser, "mozUITourNotification", false,
|
||||
event => event.detail.event === "TreatmentTag:TelemetrySent");
|
||||
yield new Promise((resolve) => {
|
||||
gContentAPI.getTreatmentTag("foobar", (data) => {
|
||||
is(data.value, "baz", "set and retrieved treatmentTag");
|
||||
ac.promiseFindPing("uitour-tag", [
|
||||
[["payload", "tagName"], "foobar"],
|
||||
[["payload", "tagValue"], "baz"],
|
||||
]).then((found) => {
|
||||
ok(found, "Telemetry ping submitted for setTreatmentTag");
|
||||
resolve();
|
||||
}, (err) => {
|
||||
ok(false, "Exception finding uitour telemetry ping: " + err);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}),
|
||||
|
||||
// Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down.
|
||||
taskify(function* cleanupMenus() {
|
||||
let shownPromise = promisePanelShown(window);
|
||||
gContentAPI.showMenu("appMenu");
|
||||
yield shownPromise;
|
||||
}),
|
||||
];
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function test_info_customize_auto_open_close(done) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
gContentAPI.showInfo("customize", "Customization", "Customize me please!");
|
||||
UITour.getTarget(window, "customize").then((customizeTarget) => {
|
||||
waitForPopupAtAnchor(popup, customizeTarget.node, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened before the popup anchored");
|
||||
ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
|
||||
|
||||
// Move the info outside which should close the app menu.
|
||||
gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
|
||||
UITour.getTarget(window, "appMenu").then((target) => {
|
||||
waitForPopupAtAnchor(popup, target.node, function checkPanelIsClosed() {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel should have closed after the info moved elsewhere.");
|
||||
ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
|
||||
done();
|
||||
}, "Info should move to the appMenu button");
|
||||
});
|
||||
}, "Info panel should be anchored to the customize button");
|
||||
});
|
||||
},
|
||||
function test_info_customize_manual_open_close(done) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
// Manually open the app menu then show an info panel there. The menu should remain open.
|
||||
let shownPromise = promisePanelShown(window);
|
||||
gContentAPI.showMenu("appMenu");
|
||||
shownPromise.then(() => {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been set");
|
||||
gContentAPI.showInfo("customize", "Customization", "Customize me please!");
|
||||
|
||||
UITour.getTarget(window, "customize").then((customizeTarget) => {
|
||||
waitForPopupAtAnchor(popup, customizeTarget.node, function checkMenuIsStillOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should still be open");
|
||||
ok(PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should still be set");
|
||||
|
||||
// Move the info outside which shouldn't close the app menu since it was manually opened.
|
||||
gContentAPI.showInfo("appMenu", "Open Me", "You know you want to");
|
||||
UITour.getTarget(window, "appMenu").then((target) => {
|
||||
waitForPopupAtAnchor(popup, target.node, function checkMenuIsStillOpen() {
|
||||
isnot(PanelUI.panel.state, "closed",
|
||||
"Menu should remain open since UITour didn't open it in the first place");
|
||||
waitForElementToBeHidden(window.PanelUI.panel, () => {
|
||||
ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up on close");
|
||||
done();
|
||||
});
|
||||
gContentAPI.hideMenu("appMenu");
|
||||
}, "Info should move to the appMenu button");
|
||||
});
|
||||
}, "Info should be shown after showInfo() for fixed menu panel items");
|
||||
});
|
||||
}).then(null, Components.utils.reportError);
|
||||
},
|
||||
taskify(function* test_bookmarks_menu() {
|
||||
let bookmarksMenuButton = document.getElementById("bookmarks-menu-button");
|
||||
|
||||
is(bookmarksMenuButton.open, false, "Menu should initially be closed");
|
||||
gContentAPI.showMenu("bookmarks");
|
||||
|
||||
yield waitForConditionPromise(() => {
|
||||
return bookmarksMenuButton.open;
|
||||
}, "Menu should be visible after showMenu()");
|
||||
|
||||
gContentAPI.hideMenu("bookmarks");
|
||||
yield waitForConditionPromise(() => {
|
||||
return !bookmarksMenuButton.open;
|
||||
}, "Menu should be hidden after hideMenu()");
|
||||
}),
|
||||
];
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_info_icon() {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let title = document.getElementById("UITourTooltipTitle");
|
||||
let desc = document.getElementById("UITourTooltipDescription");
|
||||
let icon = document.getElementById("UITourTooltipIcon");
|
||||
let buttons = document.getElementById("UITourTooltipButtons");
|
||||
|
||||
// Disable the animation to prevent the mouse clicks from hitting the main
|
||||
// window during the transition instead of the buttons in the popup.
|
||||
popup.setAttribute("animate", "false");
|
||||
|
||||
yield showInfoPromise("urlbar", "a title", "some text", "image.png");
|
||||
|
||||
is(title.textContent, "a title", "Popup should have correct title");
|
||||
is(desc.textContent, "some text", "Popup should have correct description text");
|
||||
|
||||
let imageURL = getRootDirectory(gTestPath) + "image.png";
|
||||
imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
|
||||
is(icon.src, imageURL, "Popup should have correct icon shown");
|
||||
|
||||
is(buttons.hasChildNodes(), false, "Popup should have no buttons");
|
||||
}),
|
||||
|
||||
add_UITour_task(function* test_info_buttons_1() {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let title = document.getElementById("UITourTooltipTitle");
|
||||
let desc = document.getElementById("UITourTooltipDescription");
|
||||
let icon = document.getElementById("UITourTooltipIcon");
|
||||
|
||||
yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", "makeButtons");
|
||||
|
||||
is(title.textContent, "another title", "Popup should have correct title");
|
||||
is(desc.textContent, "moar text", "Popup should have correct description text");
|
||||
|
||||
let imageURL = getRootDirectory(gTestPath) + "image.png";
|
||||
imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
|
||||
is(icon.src, imageURL, "Popup should have correct icon shown");
|
||||
|
||||
let buttons = document.getElementById("UITourTooltipButtons");
|
||||
is(buttons.childElementCount, 4, "Popup should have four buttons");
|
||||
|
||||
is(buttons.childNodes[0].nodeName, "label", "Text label should be a <label>");
|
||||
is(buttons.childNodes[0].getAttribute("value"), "Regular text", "Text label should have correct value");
|
||||
is(buttons.childNodes[0].getAttribute("image"), "", "Text should have no image");
|
||||
is(buttons.childNodes[0].className, "", "Text should have no class");
|
||||
|
||||
is(buttons.childNodes[1].nodeName, "button", "Link should be a <button>");
|
||||
is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
|
||||
is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
|
||||
is(buttons.childNodes[1].className, "button-link", "Check link class");
|
||||
|
||||
is(buttons.childNodes[2].nodeName, "button", "Button 1 should be a <button>");
|
||||
is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
|
||||
is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
|
||||
is(buttons.childNodes[2].className, "", "Button 1 should have no class");
|
||||
|
||||
is(buttons.childNodes[3].nodeName, "button", "Button 2 should be a <button>");
|
||||
is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
|
||||
is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
|
||||
is(buttons.childNodes[3].className, "button-primary", "Check button 2 class");
|
||||
|
||||
let promiseHidden = promisePanelElementHidden(window, popup);
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[2], {}, window);
|
||||
yield promiseHidden;
|
||||
|
||||
ok(true, "Popup should close automatically");
|
||||
|
||||
let returnValue = yield waitForCallbackResultPromise();
|
||||
is(returnValue.result, "button1", "Correct callback should have been called");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_info_buttons_2() {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let title = document.getElementById("UITourTooltipTitle");
|
||||
let desc = document.getElementById("UITourTooltipDescription");
|
||||
let icon = document.getElementById("UITourTooltipIcon");
|
||||
|
||||
yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", "makeButtons");
|
||||
|
||||
is(title.textContent, "another title", "Popup should have correct title");
|
||||
is(desc.textContent, "moar text", "Popup should have correct description text");
|
||||
|
||||
let imageURL = getRootDirectory(gTestPath) + "image.png";
|
||||
imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
|
||||
is(icon.src, imageURL, "Popup should have correct icon shown");
|
||||
|
||||
let buttons = document.getElementById("UITourTooltipButtons");
|
||||
is(buttons.childElementCount, 4, "Popup should have four buttons");
|
||||
|
||||
is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
|
||||
is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
|
||||
ok(buttons.childNodes[1].classList.contains("button-link"), "Link should have button-link class");
|
||||
|
||||
is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
|
||||
is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
|
||||
|
||||
is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
|
||||
is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
|
||||
|
||||
let promiseHidden = promisePanelElementHidden(window, popup);
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[3], {}, window);
|
||||
yield promiseHidden;
|
||||
|
||||
ok(true, "Popup should close automatically");
|
||||
|
||||
let returnValue = yield waitForCallbackResultPromise();
|
||||
|
||||
is(returnValue.result, "button2", "Correct callback should have been called");
|
||||
}),
|
||||
|
||||
add_UITour_task(function* test_info_close_button() {
|
||||
let closeButton = document.getElementById("UITourTooltipClose");
|
||||
|
||||
yield showInfoPromise("urlbar", "Close me", "X marks the spot", null, null, "makeInfoOptions");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(closeButton, {}, window);
|
||||
|
||||
let returnValue = yield waitForCallbackResultPromise();
|
||||
|
||||
is(returnValue.result, "closeButton", "Close button callback called");
|
||||
}),
|
||||
|
||||
add_UITour_task(function* test_info_target_callback() {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
|
||||
yield showInfoPromise("appMenu", "I want to know when the target is clicked", "*click*", null, null, "makeInfoOptions");
|
||||
|
||||
yield PanelUI.show();
|
||||
|
||||
let returnValue = yield waitForCallbackResultPromise();
|
||||
|
||||
is(returnValue.result, "target", "target callback called");
|
||||
is(returnValue.data.target, "appMenu", "target callback was from the appMenu");
|
||||
is(returnValue.data.type, "popupshown", "target callback was from the mousedown");
|
||||
|
||||
// Cleanup.
|
||||
yield hideInfoPromise();
|
||||
|
||||
popup.removeAttribute("animate");
|
||||
}),
|
||||
|
||||
add_UITour_task(function* test_getConfiguration_selectedSearchEngine() {
|
||||
yield new Promise((resolve) => {
|
||||
Services.search.init(Task.async(function*(rv) {
|
||||
ok(Components.isSuccessCode(rv), "Search service initialized");
|
||||
let engine = Services.search.defaultEngine;
|
||||
let data = yield getConfigurationPromise("selectedSearchEngine");
|
||||
is(data.searchEngineIdentifier, engine.identifier, "Correct engine identifier");
|
||||
resolve();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_setSearchTerm() {
|
||||
const TERM = "UITour Search Term";
|
||||
yield gContentAPI.setSearchTerm(TERM);
|
||||
|
||||
let searchbar = document.getElementById("searchbar");
|
||||
// The UITour gets to the searchbar element through a promise, so the value setting
|
||||
// only happens after a tick.
|
||||
yield waitForConditionPromise(() => searchbar.value == TERM, "Correct term set");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_clearSearchTerm() {
|
||||
yield gContentAPI.setSearchTerm("");
|
||||
|
||||
let searchbar = document.getElementById("searchbar");
|
||||
// The UITour gets to the searchbar element through a promise, so the value setting
|
||||
// only happens after a tick.
|
||||
yield waitForConditionPromise(() => searchbar.value == "", "Search term cleared");
|
||||
});
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Test that width and height attributes don't get set by widget code on the highlight panel.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
var highlight = document.getElementById("UITourHighlightContainer");
|
||||
var tooltip = document.getElementById("UITourTooltip");
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_highlight_size_attributes() {
|
||||
yield gContentAPI.showHighlight("appMenu");
|
||||
yield elementVisiblePromise(highlight,
|
||||
"Highlight should be shown after showHighlight() for the appMenu");
|
||||
yield gContentAPI.showHighlight("urlbar");
|
||||
yield elementVisiblePromise(highlight, "Highlight should be moved to the urlbar");
|
||||
yield new Promise((resolve) => {
|
||||
SimpleTest.executeSoon(() => {
|
||||
is(highlight.height, "", "Highlight panel should have no explicit height set");
|
||||
is(highlight.width, "", "Highlight panel should have no explicit width set");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_info_size_attributes() {
|
||||
yield gContentAPI.showInfo("appMenu", "test title", "test text");
|
||||
yield elementVisiblePromise(tooltip, "Tooltip should be shown after showInfo() for the appMenu");
|
||||
yield gContentAPI.showInfo("urlbar", "new title", "new text");
|
||||
yield elementVisiblePromise(tooltip, "Tooltip should be moved to the urlbar");
|
||||
yield new Promise((resolve) => {
|
||||
SimpleTest.executeSoon(() => {
|
||||
is(tooltip.height, "", "Info panel should have no explicit height set");
|
||||
is(tooltip.width, "", "Info panel should have no explicit width set");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
var hasWebIDE = Services.prefs.getBoolPref("devtools.webide.widget.enabled");
|
||||
var hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
|
||||
|
||||
requestLongerTimeout(2);
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_availableTargets() {
|
||||
let data = yield getConfigurationPromise("availableTargets");
|
||||
ok_targets(data, [
|
||||
"accountStatus",
|
||||
"addons",
|
||||
"appMenu",
|
||||
"backForward",
|
||||
"bookmarks",
|
||||
"customize",
|
||||
"help",
|
||||
"home",
|
||||
"devtools",
|
||||
...(hasPocket ? ["pocket"] : []),
|
||||
"privateWindow",
|
||||
"quit",
|
||||
"readerMode-urlBar",
|
||||
"search",
|
||||
"searchIcon",
|
||||
"trackingProtection",
|
||||
"urlbar",
|
||||
...(hasWebIDE ? ["webide"] : [])
|
||||
]);
|
||||
|
||||
ok(UITour.availableTargetsCache.has(window),
|
||||
"Targets should now be cached");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_availableTargets_changeWidgets() {
|
||||
CustomizableUI.removeWidgetFromArea("bookmarks-menu-button");
|
||||
ok(!UITour.availableTargetsCache.has(window),
|
||||
"Targets should be evicted from cache after widget change");
|
||||
let data = yield getConfigurationPromise("availableTargets");
|
||||
ok_targets(data, [
|
||||
"accountStatus",
|
||||
"addons",
|
||||
"appMenu",
|
||||
"backForward",
|
||||
"customize",
|
||||
"help",
|
||||
"devtools",
|
||||
"home",
|
||||
...(hasPocket ? ["pocket"] : []),
|
||||
"privateWindow",
|
||||
"quit",
|
||||
"readerMode-urlBar",
|
||||
"search",
|
||||
"searchIcon",
|
||||
"trackingProtection",
|
||||
"urlbar",
|
||||
...(hasWebIDE ? ["webide"] : [])
|
||||
]);
|
||||
|
||||
ok(UITour.availableTargetsCache.has(window),
|
||||
"Targets should now be cached again");
|
||||
CustomizableUI.reset();
|
||||
ok(!UITour.availableTargetsCache.has(window),
|
||||
"Targets should not be cached after reset");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_availableTargets_exceptionFromGetTarget() {
|
||||
// The query function for the "search" target will throw if it's not found.
|
||||
// Make sure the callback still fires with the other available targets.
|
||||
CustomizableUI.removeWidgetFromArea("search-container");
|
||||
let data = yield getConfigurationPromise("availableTargets");
|
||||
// Default minus "search" and "searchIcon"
|
||||
ok_targets(data, [
|
||||
"accountStatus",
|
||||
"addons",
|
||||
"appMenu",
|
||||
"backForward",
|
||||
"bookmarks",
|
||||
"customize",
|
||||
"help",
|
||||
"home",
|
||||
"devtools",
|
||||
...(hasPocket ? ["pocket"] : []),
|
||||
"privateWindow",
|
||||
"quit",
|
||||
"readerMode-urlBar",
|
||||
"trackingProtection",
|
||||
"urlbar",
|
||||
...(hasWebIDE ? ["webide"] : [])
|
||||
]);
|
||||
|
||||
CustomizableUI.reset();
|
||||
});
|
||||
|
||||
function ok_targets(actualData, expectedTargets) {
|
||||
// Depending on how soon after page load this is called, the selected tab icon
|
||||
// may or may not be showing the loading throbber. Check for its presence and
|
||||
// insert it into expectedTargets if it's visible.
|
||||
let selectedTabIcon =
|
||||
document.getAnonymousElementByAttribute(gBrowser.selectedTab,
|
||||
"anonid",
|
||||
"tab-icon-image");
|
||||
if (selectedTabIcon && UITour.isElementVisible(selectedTabIcon))
|
||||
expectedTargets.push("selectedTabIcon");
|
||||
|
||||
ok(Array.isArray(actualData.targets), "data.targets should be an array");
|
||||
is(actualData.targets.sort().toString(), expectedTargets.sort().toString(),
|
||||
"Targets should be as expected");
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
var setDefaultBrowserCalled = false;
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://mochikit/content/tests/SimpleTest/MockObjects.js", this);
|
||||
|
||||
function MockShellService() {}
|
||||
MockShellService.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIShellService]),
|
||||
isDefaultBrowser: function(aStartupCheck, aForAllTypes) { return false; },
|
||||
setDefaultBrowser: function(aClaimAllTypes, aForAllUsers) {
|
||||
setDefaultBrowserCalled = true;
|
||||
},
|
||||
shouldCheckDefaultBrowser: false,
|
||||
canSetDesktopBackground: false,
|
||||
BACKGROUND_TILE : 1,
|
||||
BACKGROUND_STRETCH : 2,
|
||||
BACKGROUND_CENTER : 3,
|
||||
BACKGROUND_FILL : 4,
|
||||
BACKGROUND_FIT : 5,
|
||||
setDesktopBackground: function(aElement, aPosition) {},
|
||||
APPLICATION_MAIL : 0,
|
||||
APPLICATION_NEWS : 1,
|
||||
openApplication: function(aApplication) {},
|
||||
desktopBackgroundColor: 0,
|
||||
openApplicationWithURI: function(aApplication, aURI) {},
|
||||
defaultFeedReader: 0,
|
||||
};
|
||||
|
||||
var mockShellService = new MockObjectRegisterer("@mozilla.org/browser/shell-service;1",
|
||||
MockShellService);
|
||||
|
||||
// Temporarily disabled, see note at test_setDefaultBrowser.
|
||||
// mockShellService.register();
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
/* This test is disabled (bug 1180714) since the MockObjectRegisterer
|
||||
is not actually replacing the original ShellService.
|
||||
add_UITour_task(function* test_setDefaultBrowser() {
|
||||
try {
|
||||
yield gContentAPI.setConfiguration("defaultBrowser");
|
||||
ok(setDefaultBrowserCalled, "setDefaultBrowser called");
|
||||
} finally {
|
||||
mockShellService.unregister();
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
add_UITour_task(function* test_isDefaultBrowser() {
|
||||
let shell = Components.classes["@mozilla.org/browser/shell-service;1"]
|
||||
.getService(Components.interfaces.nsIShellService);
|
||||
let isDefault = shell.isDefaultBrowser(false);
|
||||
let data = yield getConfigurationPromise("appinfo");
|
||||
is(isDefault, data.defaultBrowser, "gContentAPI result should match shellService.isDefaultBrowser");
|
||||
});
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Detaching a tab to a new window shouldn't break the menu panel.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
var gContentDoc;
|
||||
|
||||
function test() {
|
||||
registerCleanupFunction(function() {
|
||||
gContentDoc = null;
|
||||
});
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* When tab is changed we're tearing the tour down. So the UITour client has to always be aware of this
|
||||
* fact and therefore listens to visibilitychange events.
|
||||
* In particular this scenario happens for detaching the tab (ie. moving it to a new window).
|
||||
*/
|
||||
var tests = [
|
||||
taskify(function* test_move_tab_to_new_window() {
|
||||
const myDocIdentifier = "Hello, I'm a unique expando to identify this document.";
|
||||
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
let windowDestroyedDeferred = Promise.defer();
|
||||
let onDOMWindowDestroyed = (aWindow) => {
|
||||
if (gContentWindow && aWindow == gContentWindow) {
|
||||
Services.obs.removeObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
|
||||
windowDestroyedDeferred.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
let browserStartupDeferred = Promise.defer();
|
||||
Services.obs.addObserver(function onBrowserDelayedStartup(aWindow) {
|
||||
Services.obs.removeObserver(onBrowserDelayedStartup, "browser-delayed-startup-finished");
|
||||
browserStartupDeferred.resolve(aWindow);
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, myDocIdentifier, myDocIdentifier => {
|
||||
let onVisibilityChange = () => {
|
||||
if (!content.document.hidden) {
|
||||
let win = Cu.waiveXrays(content);
|
||||
win.Mozilla.UITour.showHighlight("appMenu");
|
||||
}
|
||||
};
|
||||
content.document.addEventListener("visibilitychange", onVisibilityChange);
|
||||
content.document.myExpando = myDocIdentifier;
|
||||
});
|
||||
gContentAPI.showHighlight("appMenu");
|
||||
|
||||
yield elementVisiblePromise(highlight);
|
||||
|
||||
gContentWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
|
||||
yield browserStartupDeferred.promise;
|
||||
|
||||
// This highlight should be shown thanks to the visibilitychange listener.
|
||||
let newWindowHighlight = gContentWindow.document.getElementById("UITourHighlight");
|
||||
yield elementVisiblePromise(newWindowHighlight);
|
||||
|
||||
let selectedTab = gContentWindow.gBrowser.selectedTab;
|
||||
yield ContentTask.spawn(selectedTab.linkedBrowser, myDocIdentifier, myDocIdentifier => {
|
||||
is(content.document.myExpando, myDocIdentifier, "Document should be selected in new window");
|
||||
});
|
||||
ok(UITour.tourBrowsersByWindow && UITour.tourBrowsersByWindow.has(gContentWindow), "Window should be known");
|
||||
ok(UITour.tourBrowsersByWindow.get(gContentWindow).has(selectedTab.linkedBrowser), "Selected browser should be known");
|
||||
|
||||
// Need this because gContentAPI in e10s land will try to use gTestTab to
|
||||
// spawn a content task, which doesn't work if the tab is dead, for obvious
|
||||
// reasons.
|
||||
gTestTab = gContentWindow.gBrowser.selectedTab;
|
||||
|
||||
let shownPromise = promisePanelShown(gContentWindow);
|
||||
gContentAPI.showMenu("appMenu");
|
||||
yield shownPromise;
|
||||
|
||||
isnot(gContentWindow.PanelUI.panel.state, "closed", "Panel should be open");
|
||||
ok(gContentWindow.PanelUI.contents.children.length > 0, "Panel contents should have children");
|
||||
gContentAPI.hideHighlight();
|
||||
gContentAPI.hideMenu("appMenu");
|
||||
gTestTab = null;
|
||||
|
||||
Services.obs.addObserver(onDOMWindowDestroyed, "dom-window-destroyed", false);
|
||||
gContentWindow.close();
|
||||
|
||||
yield windowDestroyedDeferred.promise;
|
||||
}),
|
||||
];
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function*() {
|
||||
ok(!gBrowser.selectedBrowser.isArticle, "Should not be an article when we start");
|
||||
ok(document.getElementById("reader-mode-button").hidden, "Button should be hidden.");
|
||||
yield gContentAPI.forceShowReaderIcon();
|
||||
yield waitForConditionPromise(() => gBrowser.selectedBrowser.isArticle);
|
||||
ok(gBrowser.selectedBrowser.isArticle, "Should suddenly be an article.");
|
||||
ok(!document.getElementById("reader-mode-button").hidden, "Button should now be visible.");
|
||||
});
|
||||
|
||||
|
|
@ -1,755 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
function getHeartbeatNotification(aId, aChromeWindow = window) {
|
||||
let notificationBox = aChromeWindow.document.getElementById("high-priority-global-notificationbox");
|
||||
// UITour.jsm prefixes the notification box ID with "heartbeat-" to prevent collisions.
|
||||
return notificationBox.getNotificationWithValue("heartbeat-" + aId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a click on a rating element in the Heartbeat notification.
|
||||
*
|
||||
* @param aId
|
||||
* The id of the notification box.
|
||||
* @param aScore
|
||||
* The score related to the rating element we want to click on.
|
||||
*/
|
||||
function simulateVote(aId, aScore) {
|
||||
let notification = getHeartbeatNotification(aId);
|
||||
|
||||
let ratingContainer = notification.childNodes[0];
|
||||
ok(ratingContainer, "The notification has a valid rating container.");
|
||||
|
||||
let ratingElement = ratingContainer.getElementsByAttribute("data-score", aScore);
|
||||
ok(ratingElement[0], "The rating container contains the requested rating element.");
|
||||
|
||||
ratingElement[0].click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a click on the learn-more link.
|
||||
*
|
||||
* @param aId
|
||||
* The id of the notification box.
|
||||
*/
|
||||
function clickLearnMore(aId) {
|
||||
let notification = getHeartbeatNotification(aId);
|
||||
|
||||
let learnMoreLabel = notification.childNodes[2];
|
||||
ok(learnMoreLabel, "The notification has a valid learn more label.");
|
||||
|
||||
learnMoreLabel.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the notification box.
|
||||
*
|
||||
* @param aId
|
||||
* The id of the notification box to remove.
|
||||
* @param [aChromeWindow=window]
|
||||
* The chrome window the notification box is in.
|
||||
*/
|
||||
function cleanUpNotification(aId, aChromeWindow = window) {
|
||||
let notification = getHeartbeatNotification(aId, aChromeWindow);
|
||||
notification.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check telemetry payload for proper format and expected content.
|
||||
*
|
||||
* @param aPayload
|
||||
* The Telemetry payload to verify
|
||||
* @param aFlowId
|
||||
* Expected value of the flowId field.
|
||||
* @param aExpectedFields
|
||||
* Array of expected fields. No other fields are allowed.
|
||||
*/
|
||||
function checkTelemetry(aPayload, aFlowId, aExpectedFields) {
|
||||
// Basic payload format
|
||||
is(aPayload.version, 1, "Telemetry ping must have heartbeat version=1");
|
||||
is(aPayload.flowId, aFlowId, "Flow ID in the Telemetry ping must match");
|
||||
|
||||
// Check for superfluous fields
|
||||
let extraKeys = new Set(Object.keys(aPayload));
|
||||
extraKeys.delete("version");
|
||||
extraKeys.delete("flowId");
|
||||
|
||||
// Check for expected fields
|
||||
for (let field of aExpectedFields) {
|
||||
ok(field in aPayload, "The payload should have the field '" + field + "'");
|
||||
if (field.endsWith("TS")) {
|
||||
let ts = aPayload[field];
|
||||
ok(Number.isInteger(ts) && ts > 0, "Timestamp '" + field + "' must be a natural number");
|
||||
}
|
||||
extraKeys.delete(field);
|
||||
}
|
||||
|
||||
is(extraKeys.size, 0, "No unexpected fields in the Telemetry payload");
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an UITour notification dispatched through |UITour.notify|. This should be
|
||||
* done with |gContentAPI.observe|. Unfortunately, in e10s, |gContentAPI.observe| doesn't
|
||||
* allow for multiple calls to the same callback, allowing to catch just the first
|
||||
* notification.
|
||||
*
|
||||
* @param aEventName
|
||||
* The notification name to wait for.
|
||||
* @return {Promise} Resolved with the data that comes with the event.
|
||||
*/
|
||||
function promiseWaitHeartbeatNotification(aEventName) {
|
||||
return ContentTask.spawn(gTestTab.linkedBrowser, { aEventName },
|
||||
function({ aEventName }) {
|
||||
return new Promise(resolve => {
|
||||
addEventListener("mozUITourNotification", function listener(event) {
|
||||
if (event.detail.event !== aEventName) {
|
||||
return;
|
||||
}
|
||||
removeEventListener("mozUITourNotification", listener, false);
|
||||
resolve(event.detail.params);
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for UITour notifications dispatched through |UITour.notify|. This works like
|
||||
* |promiseWaitHeartbeatNotification|, but waits for all the passed notifications to
|
||||
* be received before resolving. If it receives an unaccounted notification, it rejects.
|
||||
*
|
||||
* @param events
|
||||
* An array of expected notification names to wait for.
|
||||
* @return {Promise} Resolved with the data that comes with the event. Rejects with the
|
||||
* name of an undesired notification if received.
|
||||
*/
|
||||
function promiseWaitExpectedNotifications(events) {
|
||||
return ContentTask.spawn(gTestTab.linkedBrowser, { events },
|
||||
function({ events }) {
|
||||
let stillToReceive = events;
|
||||
return new Promise((res, rej) => {
|
||||
addEventListener("mozUITourNotification", function listener(event) {
|
||||
if (stillToReceive.includes(event.detail.event)) {
|
||||
// Filter out the received event.
|
||||
stillToReceive = stillToReceive.filter(x => x !== event.detail.event);
|
||||
} else {
|
||||
removeEventListener("mozUITourNotification", listener, false);
|
||||
rej(event.detail.event);
|
||||
}
|
||||
// We still need to catch some notifications. Don't do anything.
|
||||
if (stillToReceive.length > 0) {
|
||||
return;
|
||||
}
|
||||
// We don't need to listen for other notifications. Resolve the promise.
|
||||
removeEventListener("mozUITourNotification", listener, false);
|
||||
res();
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validateTimestamp(eventName, timestamp) {
|
||||
info("'" + eventName + "' notification received (timestamp " + timestamp.toString() + ").");
|
||||
ok(Number.isFinite(timestamp), "Timestamp must be a number.");
|
||||
}
|
||||
|
||||
add_task(function* test_setup() {
|
||||
yield setup_UITourTest();
|
||||
requestLongerTimeout(2);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("browser.uitour.surveyDuration");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Check that the "stars" heartbeat UI correctly shows and closes.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_stars_show() {
|
||||
let flowId = "ui-ratefirefox-" + Math.random();
|
||||
let engagementURL = "http://example.com";
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(
|
||||
["Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
|
||||
|
||||
// Validate the returned timestamp.
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Close the heartbeat notification.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
cleanUpNotification(flowId);
|
||||
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "closedTS"]);
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Check that the heartbeat UI correctly takes optional icon URL.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_take_optional_icon_URL() {
|
||||
let flowId = "ui-ratefirefox-" + Math.random();
|
||||
let engagementURL = "http://example.com";
|
||||
let iconURL = "chrome://branding/content/icon48.png";
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(
|
||||
["Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL, null, null, {
|
||||
iconURL: iconURL
|
||||
});
|
||||
|
||||
// Validate the returned timestamp.
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Check the icon URL
|
||||
let notification = getHeartbeatNotification(flowId);
|
||||
is(notification.image, iconURL, "The optional icon URL is not taken correctly");
|
||||
|
||||
// Close the heartbeat notification.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
cleanUpNotification(flowId);
|
||||
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "closedTS"]);
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Test that the heartbeat UI correctly works with null engagement URL.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_null_engagementURL() {
|
||||
let flowId = "ui-ratefirefox-" + Math.random();
|
||||
let originalTabCount = gBrowser.tabs.length;
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
|
||||
"Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
|
||||
|
||||
// Validate the returned timestamp.
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
|
||||
// wait for them here.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
|
||||
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
|
||||
simulateVote(flowId, 2);
|
||||
data = yield votedPromise;
|
||||
validateTimestamp('Heartbeat:Voted', data.timestamp);
|
||||
|
||||
// Validate the closing timestamp.
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
|
||||
|
||||
// Validate the data we send out.
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received.");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
|
||||
is(data.score, 2, "Checking Telemetry payload.score");
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Test that the heartbeat UI correctly works with an invalid, but non null, engagement URL.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_invalid_engagement_URL() {
|
||||
let flowId = "ui-ratefirefox-" + Math.random();
|
||||
let originalTabCount = gBrowser.tabs.length;
|
||||
let invalidEngagementURL = "invalidEngagement";
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
|
||||
"Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, invalidEngagementURL);
|
||||
|
||||
// Validate the returned timestamp.
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
|
||||
// wait for them here.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
|
||||
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
|
||||
simulateVote(flowId, 2);
|
||||
data = yield votedPromise;
|
||||
validateTimestamp('Heartbeat:Voted', data.timestamp);
|
||||
|
||||
// Validate the closing timestamp.
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
|
||||
|
||||
// Validate the data we send out.
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received.");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
|
||||
is(data.score, 2, "Checking Telemetry payload.score");
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Test that the score is correctly reported.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_stars_vote() {
|
||||
const expectedScore = 4;
|
||||
let originalTabCount = gBrowser.tabs.length;
|
||||
let flowId = "ui-ratefirefox-" + Math.random();
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
|
||||
"Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
|
||||
|
||||
// Validate the returned timestamp.
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
|
||||
// wait for them here.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
|
||||
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
|
||||
simulateVote(flowId, expectedScore);
|
||||
data = yield votedPromise;
|
||||
validateTimestamp('Heartbeat:Voted', data.timestamp);
|
||||
is(data.score, expectedScore, "Should report a score of " + expectedScore);
|
||||
|
||||
// Validate the closing timestamp and vote.
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
|
||||
|
||||
// Validate the data we send out.
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received.");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
|
||||
is(data.score, expectedScore, "Checking Telemetry payload.score");
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Test that the engagement page is correctly opened when voting.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_engagement_tab() {
|
||||
let engagementURL = "http://example.com";
|
||||
let flowId = "ui-ratefirefox-" + Math.random();
|
||||
let originalTabCount = gBrowser.tabs.length;
|
||||
const expectedTabCount = originalTabCount + 1;
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
|
||||
"Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
|
||||
|
||||
// Validate the returned timestamp.
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
|
||||
// wait for them here.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
|
||||
// The UI was just shown. We can simulate a click on a rating element (i.e., "star").
|
||||
simulateVote(flowId, 1);
|
||||
data = yield votedPromise;
|
||||
validateTimestamp('Heartbeat:Voted', data.timestamp);
|
||||
|
||||
// Validate the closing timestamp, vote and make sure the engagement page was opened.
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
|
||||
gBrowser.removeCurrentTab();
|
||||
|
||||
// Validate the data we send out.
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received.");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
|
||||
is(data.score, 1, "Checking Telemetry payload.score");
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Test that the engagement button opens the engagement URL.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_engagement_button() {
|
||||
let engagementURL = "http://example.com";
|
||||
let flowId = "ui-engagewithfirefox-" + Math.random();
|
||||
let originalTabCount = gBrowser.tabs.length;
|
||||
const expectedTabCount = originalTabCount + 1;
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
|
||||
"Heartbeat:NotificationClosed", "Heartbeat:Engaged", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL, null, null, {
|
||||
engagementButtonLabel: "Engage Me",
|
||||
});
|
||||
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Wait an the Engaged, Closed and Telemetry Sent events. They are fired together, so
|
||||
// wait for them here.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let engagedPromise = promiseWaitHeartbeatNotification("Heartbeat:Engaged");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
|
||||
// Simulate user engagement.
|
||||
let notification = getHeartbeatNotification(flowId);
|
||||
is(notification.querySelectorAll(".star-x").length, 0, "No stars should be present");
|
||||
// The UI was just shown. We can simulate a click on the engagement button.
|
||||
let engagementButton = notification.querySelector(".notification-button");
|
||||
is(engagementButton.label, "Engage Me", "Check engagement button text");
|
||||
engagementButton.doCommand();
|
||||
|
||||
data = yield engagedPromise;
|
||||
validateTimestamp('Heartbeat:Engaged', data.timestamp);
|
||||
|
||||
// Validate the closing timestamp, vote and make sure the engagement page was opened.
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
|
||||
gBrowser.removeCurrentTab();
|
||||
|
||||
// Validate the data we send out.
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received.");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "engagedTS", "closedTS"]);
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Test that the learn more link is displayed and that the page is correctly opened when
|
||||
* clicking on it.
|
||||
*/
|
||||
add_UITour_task(function* test_heartbeat_learnmore() {
|
||||
let dummyURL = "http://example.com";
|
||||
let flowId = "ui-ratefirefox-" + Math.random();
|
||||
let originalTabCount = gBrowser.tabs.length;
|
||||
const expectedTabCount = originalTabCount + 1;
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
|
||||
"Heartbeat:NotificationClosed", "Heartbeat:LearnMore", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, dummyURL,
|
||||
"What is this?", dummyURL);
|
||||
|
||||
let data = yield shownPromise;
|
||||
validateTimestamp('Heartbeat:Offered', data.timestamp);
|
||||
|
||||
// Wait an the LearnMore, Closed and Telemetry Sent events. They are fired together, so
|
||||
// wait for them here.
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let learnMorePromise = promiseWaitHeartbeatNotification("Heartbeat:LearnMore");
|
||||
let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
|
||||
// The UI was just shown. Simulate a click on the learn more link.
|
||||
clickLearnMore(flowId);
|
||||
|
||||
data = yield learnMorePromise;
|
||||
validateTimestamp('Heartbeat:LearnMore', data.timestamp);
|
||||
cleanUpNotification(flowId);
|
||||
|
||||
// The notification was closed.
|
||||
data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
is(gBrowser.tabs.length, expectedTabCount, "Learn more URL should open in a new tab.");
|
||||
gBrowser.removeCurrentTab();
|
||||
|
||||
// Validate the data we send out.
|
||||
data = yield pingSentPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received.");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "learnMoreTS", "closedTS"]);
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
add_UITour_task(function* test_invalidEngagementButtonLabel() {
|
||||
let engagementURL = "http://example.com";
|
||||
let flowId = "invalidEngagementButtonLabel-" + Math.random();
|
||||
|
||||
let eventPromise = promisePageEvent();
|
||||
|
||||
gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
|
||||
null, null, {
|
||||
engagementButtonLabel: 42,
|
||||
});
|
||||
|
||||
yield eventPromise;
|
||||
ok(!isTourBrowser(gBrowser.selectedBrowser),
|
||||
"Invalid engagementButtonLabel should prevent init");
|
||||
|
||||
})
|
||||
|
||||
add_UITour_task(function* test_privateWindowsOnly_noneOpen() {
|
||||
let engagementURL = "http://example.com";
|
||||
let flowId = "privateWindowsOnly_noneOpen-" + Math.random();
|
||||
|
||||
let eventPromise = promisePageEvent();
|
||||
|
||||
gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
|
||||
null, null, {
|
||||
engagementButtonLabel: "Yes!",
|
||||
privateWindowsOnly: true,
|
||||
});
|
||||
|
||||
yield eventPromise;
|
||||
ok(!isTourBrowser(gBrowser.selectedBrowser),
|
||||
"If there are no private windows opened, tour init should be prevented");
|
||||
})
|
||||
|
||||
add_UITour_task(function* test_privateWindowsOnly_notMostRecent() {
|
||||
let engagementURL = "http://example.com";
|
||||
let flowId = "notMostRecent-" + Math.random();
|
||||
|
||||
let privateWin = yield BrowserTestUtils.openNewBrowserWindow({ private: true });
|
||||
let mostRecentWin = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
|
||||
let eventPromise = promisePageEvent();
|
||||
|
||||
gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
|
||||
null, null, {
|
||||
engagementButtonLabel: "Yes!",
|
||||
privateWindowsOnly: true,
|
||||
});
|
||||
|
||||
yield eventPromise;
|
||||
is(getHeartbeatNotification(flowId, window), null,
|
||||
"Heartbeat shouldn't appear in the default window");
|
||||
is(!!getHeartbeatNotification(flowId, privateWin), true,
|
||||
"Heartbeat should appear in the most recent private window");
|
||||
is(getHeartbeatNotification(flowId, mostRecentWin), null,
|
||||
"Heartbeat shouldn't appear in the most recent non-private window");
|
||||
|
||||
yield BrowserTestUtils.closeWindow(mostRecentWin);
|
||||
yield BrowserTestUtils.closeWindow(privateWin);
|
||||
})
|
||||
|
||||
add_UITour_task(function* test_privateWindowsOnly() {
|
||||
let engagementURL = "http://example.com";
|
||||
let learnMoreURL = "http://example.org/learnmore/";
|
||||
let flowId = "ui-privateWindowsOnly-" + Math.random();
|
||||
|
||||
let privateWin = yield BrowserTestUtils.openNewBrowserWindow({ private: true });
|
||||
|
||||
yield new Promise((resolve) => {
|
||||
gContentAPI.observe(function(aEventName, aData) {
|
||||
info(aEventName + " notification received: " + JSON.stringify(aData, null, 2));
|
||||
ok(false, "No heartbeat notifications should arrive for privateWindowsOnly");
|
||||
}, resolve);
|
||||
});
|
||||
|
||||
gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
|
||||
"Learn More", learnMoreURL, {
|
||||
engagementButtonLabel: "Yes!",
|
||||
privateWindowsOnly: true,
|
||||
});
|
||||
|
||||
yield promisePageEvent();
|
||||
|
||||
ok(isTourBrowser(gBrowser.selectedBrowser), "UITour should have been init for the browser");
|
||||
|
||||
let notification = getHeartbeatNotification(flowId, privateWin);
|
||||
|
||||
is(notification.querySelectorAll(".star-x").length, 0, "No stars should be present");
|
||||
|
||||
info("Test the learn more link.");
|
||||
let learnMoreLink = notification.querySelector(".text-link");
|
||||
is(learnMoreLink.value, "Learn More", "Check learn more label");
|
||||
let learnMoreTabPromise = BrowserTestUtils.waitForNewTab(privateWin.gBrowser, null);
|
||||
learnMoreLink.click();
|
||||
let learnMoreTab = yield learnMoreTabPromise;
|
||||
is(learnMoreTab.linkedBrowser.currentURI.host, "example.org", "Check learn more site opened");
|
||||
ok(PrivateBrowsingUtils.isBrowserPrivate(learnMoreTab.linkedBrowser), "Ensure the learn more tab is private");
|
||||
yield BrowserTestUtils.removeTab(learnMoreTab);
|
||||
|
||||
info("Test the engagement button's new tab.");
|
||||
let engagementButton = notification.querySelector(".notification-button");
|
||||
is(engagementButton.label, "Yes!", "Check engagement button text");
|
||||
let engagementTabPromise = BrowserTestUtils.waitForNewTab(privateWin.gBrowser, null);
|
||||
engagementButton.doCommand();
|
||||
let engagementTab = yield engagementTabPromise;
|
||||
is(engagementTab.linkedBrowser.currentURI.host, "example.com", "Check enagement site opened");
|
||||
ok(PrivateBrowsingUtils.isBrowserPrivate(engagementTab.linkedBrowser), "Ensure the engagement tab is private");
|
||||
yield BrowserTestUtils.removeTab(engagementTab);
|
||||
|
||||
yield BrowserTestUtils.closeWindow(privateWin);
|
||||
})
|
||||
|
||||
/**
|
||||
* Test that the survey closes itself after a while and submits Telemetry
|
||||
*/
|
||||
add_UITour_task(function* test_telemetry_surveyExpired() {
|
||||
let flowId = "survey-expired-" + Math.random();
|
||||
let engagementURL = "http://example.com";
|
||||
let surveyDuration = 1; // 1 second (pref is in seconds)
|
||||
Services.prefs.setIntPref("browser.uitour.surveyDuration", surveyDuration);
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
|
||||
"Heartbeat:NotificationClosed", "Heartbeat:SurveyExpired", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
|
||||
|
||||
let expiredPromise = promiseWaitHeartbeatNotification("Heartbeat:SurveyExpired");
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let pingPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
|
||||
yield Promise.all([shownPromise, expiredPromise, closedPromise]);
|
||||
// Validate the ping data.
|
||||
let data = yield pingPromise;
|
||||
checkTelemetry(data, flowId, ["offeredTS", "expiredTS", "closedTS"]);
|
||||
|
||||
Services.prefs.clearUserPref("browser.uitour.surveyDuration");
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
||||
/**
|
||||
* Check that certain whitelisted experiment parameters get reflected in the
|
||||
* Telemetry ping
|
||||
*/
|
||||
add_UITour_task(function* test_telemetry_params() {
|
||||
let flowId = "telemetry-params-" + Math.random();
|
||||
let engagementURL = "http://example.com";
|
||||
let extraParams = {
|
||||
"surveyId": "foo",
|
||||
"surveyVersion": 1.5,
|
||||
"testing": true,
|
||||
"notWhitelisted": 123,
|
||||
};
|
||||
let expectedFields = ["surveyId", "surveyVersion", "testing"];
|
||||
|
||||
// We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
|
||||
// in UITour-lib.js, otherwise no message will get propagated.
|
||||
gContentAPI.observe(() => {});
|
||||
|
||||
let receivedExpectedPromise = promiseWaitExpectedNotifications(
|
||||
["Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed", "Heartbeat:TelemetrySent"]);
|
||||
|
||||
// Show the Heartbeat notification and wait for it to be displayed.
|
||||
let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
|
||||
gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!",
|
||||
flowId, engagementURL, null, null, extraParams);
|
||||
yield shownPromise;
|
||||
|
||||
let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
|
||||
let pingPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
|
||||
cleanUpNotification(flowId);
|
||||
|
||||
// The notification was closed.
|
||||
let data = yield closedPromise;
|
||||
validateTimestamp('Heartbeat:NotificationClosed', data.timestamp);
|
||||
|
||||
// Validate the data we send out.
|
||||
data = yield pingPromise;
|
||||
info("'Heartbeat:TelemetrySent' notification received.");
|
||||
checkTelemetry(data, flowId, ["offeredTS", "closedTS"].concat(expectedFields));
|
||||
for (let param of expectedFields) {
|
||||
is(data[param], extraParams[param],
|
||||
"Whitelisted experiment configs should be copied into Telemetry pings");
|
||||
}
|
||||
|
||||
// This rejects whenever an unexpected notification is received.
|
||||
yield receivedExpectedPromise;
|
||||
})
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
var handleDialog;
|
||||
|
||||
// Modified from toolkit/components/passwordmgr/test/prompt_common.js
|
||||
var didDialog;
|
||||
|
||||
var timer; // keep in outer scope so it's not GC'd before firing
|
||||
function startCallbackTimer() {
|
||||
didDialog = false;
|
||||
|
||||
// Delay before the callback twiddles the prompt.
|
||||
const dialogDelay = 10;
|
||||
|
||||
// Use a timer to invoke a callback to twiddle the authentication dialog
|
||||
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.init(observer, dialogDelay, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
|
||||
var observer = SpecialPowers.wrapCallbackObject({
|
||||
QueryInterface : function (iid) {
|
||||
const interfaces = [Ci.nsIObserver,
|
||||
Ci.nsISupports, Ci.nsISupportsWeakReference];
|
||||
|
||||
if (!interfaces.some( function(v) { return iid.equals(v) } ))
|
||||
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
},
|
||||
|
||||
observe : function (subject, topic, data) {
|
||||
var doc = getDialogDoc();
|
||||
if (doc)
|
||||
handleDialog(doc);
|
||||
else
|
||||
startCallbackTimer(); // try again in a bit
|
||||
}
|
||||
});
|
||||
|
||||
function getDialogDoc() {
|
||||
// Find the <browser> which contains notifyWindow, by looking
|
||||
// through all the open windows and all the <browsers> in each.
|
||||
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
// var enumerator = wm.getEnumerator("navigator:browser");
|
||||
var enumerator = wm.getXULWindowEnumerator(null);
|
||||
|
||||
while (enumerator.hasMoreElements()) {
|
||||
var win = enumerator.getNext();
|
||||
var windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
|
||||
|
||||
var containedDocShells = windowDocShell.getDocShellEnumerator(
|
||||
Ci.nsIDocShellTreeItem.typeChrome,
|
||||
Ci.nsIDocShell.ENUMERATE_FORWARDS);
|
||||
while (containedDocShells.hasMoreElements()) {
|
||||
// Get the corresponding document for this docshell
|
||||
var childDocShell = containedDocShells.getNext();
|
||||
// We don't want it if it's not done loading.
|
||||
if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
|
||||
continue;
|
||||
var childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
|
||||
.contentViewer
|
||||
.DOMDocument;
|
||||
|
||||
// ok(true, "Got window: " + childDoc.location.href);
|
||||
if (childDoc.location.href == "chrome://global/content/commonDialog.xul")
|
||||
return childDoc;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
|
||||
var tests = [
|
||||
taskify(function* test_modal_dialog_while_opening_tooltip() {
|
||||
let panelShown;
|
||||
let popup;
|
||||
|
||||
handleDialog = (doc) => {
|
||||
popup = document.getElementById("UITourTooltip");
|
||||
gContentAPI.showInfo("appMenu", "test title", "test text");
|
||||
doc.defaultView.setTimeout(function() {
|
||||
is(popup.state, "closed", "Popup shouldn't be shown while dialog is up");
|
||||
panelShown = promisePanelElementShown(window, popup);
|
||||
let dialog = doc.getElementById("commonDialog");
|
||||
dialog.acceptDialog();
|
||||
}, 1000);
|
||||
};
|
||||
startCallbackTimer();
|
||||
executeSoon(() => alert("test"));
|
||||
yield waitForConditionPromise(() => panelShown, "Timed out waiting for panel promise to be assigned", 100);
|
||||
yield panelShown;
|
||||
|
||||
yield hideInfoPromise();
|
||||
})
|
||||
];
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function test_no_params(done) {
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-1", "Correct event name");
|
||||
is(params, null, "No param object");
|
||||
gContentAPI.observe(null);
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
UITour.notify("test-event-1");
|
||||
});
|
||||
},
|
||||
function test_param_string(done) {
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-2", "Correct event name");
|
||||
is(params, "a param", "Correct param string");
|
||||
gContentAPI.observe(null);
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
UITour.notify("test-event-2", "a param");
|
||||
});
|
||||
},
|
||||
function test_param_object(done) {
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-3", "Correct event name");
|
||||
is(JSON.stringify(params), JSON.stringify({key: "something"}), "Correct param object");
|
||||
gContentAPI.observe(null);
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
UITour.notify("test-event-3", {key: "something"});
|
||||
});
|
||||
},
|
||||
function test_background_tab(done) {
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-background-1", "Correct event name");
|
||||
is(params, null, "No param object");
|
||||
gContentAPI.observe(null);
|
||||
gBrowser.removeCurrentTab();
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
||||
isnot(gBrowser.selectedTab, gTestTab, "Make sure the selected tab changed");
|
||||
|
||||
UITour.notify("test-event-background-1");
|
||||
});
|
||||
},
|
||||
// Make sure the tab isn't torn down when switching back to the tour one.
|
||||
function test_background_then_foreground_tab(done) {
|
||||
let blankTab = null;
|
||||
function listener(event, params) {
|
||||
is(event, "test-event-4", "Correct event name");
|
||||
is(params, null, "No param object");
|
||||
gContentAPI.observe(null);
|
||||
gBrowser.removeTab(blankTab);
|
||||
done();
|
||||
}
|
||||
|
||||
gContentAPI.observe(listener, () => {
|
||||
blankTab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
||||
isnot(gBrowser.selectedTab, gTestTab, "Make sure the selected tab changed");
|
||||
gBrowser.selectedTab = gTestTab;
|
||||
is(gBrowser.selectedTab, gTestTab, "Switch back to the test tab");
|
||||
|
||||
UITour.notify("test-event-4");
|
||||
});
|
||||
},
|
||||
];
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that annotations disappear when their target is hidden.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
var highlight = document.getElementById("UITourHighlight");
|
||||
var tooltip = document.getElementById("UITourTooltip");
|
||||
|
||||
function test() {
|
||||
registerCleanupFunction(() => {
|
||||
// Close the find bar in case it's open in the remaining tab
|
||||
gBrowser.getFindBar(gBrowser.selectedTab).close();
|
||||
});
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function test_highlight_move_outside_panel(done) {
|
||||
gContentAPI.showInfo("urlbar", "test title", "test text");
|
||||
gContentAPI.showHighlight("customize");
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Move the highlight outside which should close the app menu.
|
||||
gContentAPI.showHighlight("appMenu");
|
||||
waitForPopupAtAnchor(highlight.parentElement, document.getElementById("PanelUI-button"), () => {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel should have closed after the highlight moved elsewhere.");
|
||||
ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
|
||||
done();
|
||||
}, "Highlight should move to the appMenu button and still be visible");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
},
|
||||
|
||||
function test_highlight_panel_hideMenu(done) {
|
||||
gContentAPI.showHighlight("customize");
|
||||
gContentAPI.showInfo("search", "test title", "test text");
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Close the app menu and make sure the highlight also disappeared.
|
||||
gContentAPI.hideMenu("appMenu");
|
||||
waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel still should have closed");
|
||||
ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
|
||||
done();
|
||||
}, "Highlight should have disappeared when panel closed");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
},
|
||||
|
||||
function test_highlight_panel_click_find(done) {
|
||||
gContentAPI.showHighlight("help");
|
||||
gContentAPI.showInfo("searchIcon", "test title", "test text");
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Click the find button which should close the panel.
|
||||
let findButton = document.getElementById("find-button");
|
||||
EventUtils.synthesizeMouseAtCenter(findButton, {});
|
||||
waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel should have closed when the find bar opened");
|
||||
ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
|
||||
done();
|
||||
}, "Highlight should have disappeared when panel closed");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
},
|
||||
|
||||
function test_highlight_info_panel_click_find(done) {
|
||||
gContentAPI.showHighlight("help");
|
||||
gContentAPI.showInfo("customize", "customize me!", "awesome!");
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Click the find button which should close the panel.
|
||||
let findButton = document.getElementById("find-button");
|
||||
EventUtils.synthesizeMouseAtCenter(findButton, {});
|
||||
waitForElementToBeHidden(highlight, function checkPanelIsClosed() {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Panel should have closed when the find bar opened");
|
||||
waitForElementToBeHidden(tooltip, function checkTooltipIsClosed() {
|
||||
isnot(tooltip.state, "open", "The info panel should have closed too");
|
||||
done();
|
||||
}, "Tooltip should hide with the menu");
|
||||
}, "Highlight should have disappeared when panel closed");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
},
|
||||
|
||||
function test_highlight_panel_open_subview(done) {
|
||||
gContentAPI.showHighlight("customize");
|
||||
gContentAPI.showInfo("backForward", "test title", "test text");
|
||||
waitForElementToBeVisible(highlight, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Click the help button which should open the subview in the panel menu.
|
||||
let helpButton = document.getElementById("PanelUI-help");
|
||||
EventUtils.synthesizeMouseAtCenter(helpButton, {});
|
||||
waitForElementToBeHidden(highlight, function highlightHidden() {
|
||||
is(PanelUI.panel.state, "open",
|
||||
"Panel should have stayed open when the subview opened");
|
||||
ok(tooltip.state == "showing" || tooltip.state == "open", "The info panel should have remained open");
|
||||
PanelUI.hide();
|
||||
done();
|
||||
}, "Highlight should have disappeared when the subview opened");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
},
|
||||
|
||||
function test_info_panel_open_subview(done) {
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
gContentAPI.showInfo("customize", "customize me!", "Open a subview");
|
||||
waitForElementToBeVisible(tooltip, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Click the help button which should open the subview in the panel menu.
|
||||
let helpButton = document.getElementById("PanelUI-help");
|
||||
EventUtils.synthesizeMouseAtCenter(helpButton, {});
|
||||
waitForElementToBeHidden(tooltip, function tooltipHidden() {
|
||||
is(PanelUI.panel.state, "open",
|
||||
"Panel should have stayed open when the subview opened");
|
||||
is(highlight.parentElement.state, "open", "The highlight should have remained open");
|
||||
PanelUI.hide();
|
||||
done();
|
||||
}, "Tooltip should have disappeared when the subview opened");
|
||||
}, "Highlight should be shown after showHighlight() for fixed panel items");
|
||||
},
|
||||
|
||||
function test_info_move_outside_panel(done) {
|
||||
gContentAPI.showInfo("addons", "test title", "test text");
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
let addonsButton = document.getElementById("add-ons-button");
|
||||
waitForPopupAtAnchor(tooltip, addonsButton, function checkPanelIsOpen() {
|
||||
isnot(PanelUI.panel.state, "closed", "Panel should have opened");
|
||||
|
||||
// Move the info panel outside which should close the app menu.
|
||||
gContentAPI.showInfo("appMenu", "Cool menu button", "It's three lines");
|
||||
waitForPopupAtAnchor(tooltip, document.getElementById("PanelUI-button"), () => {
|
||||
isnot(PanelUI.panel.state, "open",
|
||||
"Menu should have closed after the highlight moved elsewhere.");
|
||||
is(highlight.parentElement.state, "open", "The highlight should have remained visible");
|
||||
done();
|
||||
}, "Tooltip should move to the appMenu button and still be visible");
|
||||
}, "Tooltip should be shown after showInfo() for a panel item");
|
||||
},
|
||||
|
||||
];
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
var button;
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
taskify(function* test_menu_show_navbar() {
|
||||
is(button.open, false, "Menu should initially be closed");
|
||||
gContentAPI.showMenu("pocket");
|
||||
|
||||
// The panel gets created dynamically.
|
||||
let widgetPanel = null;
|
||||
yield waitForConditionPromise(() => {
|
||||
widgetPanel = document.getElementById("customizationui-widget-panel");
|
||||
return widgetPanel && widgetPanel.state == "open";
|
||||
}, "Menu should be visible after showMenu()");
|
||||
|
||||
ok(button.open, "Button should know its view is open");
|
||||
ok(!widgetPanel.hasAttribute("noautohide"), "@noautohide shouldn't be on the pocket panel");
|
||||
ok(button.hasAttribute("open"), "Pocket button should know that the menu is open");
|
||||
|
||||
widgetPanel.hidePopup();
|
||||
checkPanelIsHidden(widgetPanel);
|
||||
}),
|
||||
taskify(function* test_menu_show_appMenu() {
|
||||
CustomizableUI.addWidgetToArea("pocket-button", CustomizableUI.AREA_PANEL);
|
||||
|
||||
is(PanelUI.multiView.hasAttribute("panelopen"), false, "Multiview should initially be closed");
|
||||
gContentAPI.showMenu("pocket");
|
||||
|
||||
yield waitForConditionPromise(() => {
|
||||
return PanelUI.panel.state == "open";
|
||||
}, "Menu should be visible after showMenu()");
|
||||
|
||||
ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide shouldn't be on the pocket panel");
|
||||
ok(PanelUI.multiView.showingSubView, "Subview should be open");
|
||||
ok(PanelUI.multiView.hasAttribute("panelopen"), "Multiview should know it's open");
|
||||
|
||||
PanelUI.showMainView();
|
||||
PanelUI.panel.hidePopup();
|
||||
checkPanelIsHidden(PanelUI.panel);
|
||||
}),
|
||||
];
|
||||
|
||||
// End tests
|
||||
|
||||
function checkPanelIsHidden(aPanel) {
|
||||
if (aPanel.parentElement) {
|
||||
is_hidden(aPanel);
|
||||
} else {
|
||||
ok(!aPanel.parentElement, "Widget panel should have been removed");
|
||||
}
|
||||
is(button.hasAttribute("open"), false, "Pocket button should know that the panel is closed");
|
||||
}
|
||||
|
||||
if (Services.prefs.getBoolPref("extensions.pocket.enabled")) {
|
||||
let placement = CustomizableUI.getPlacementOfWidget("pocket-button");
|
||||
|
||||
// Add the button to the nav-bar by default.
|
||||
if (!placement || placement.area != CustomizableUI.AREA_NAVBAR) {
|
||||
CustomizableUI.addWidgetToArea("pocket-button", CustomizableUI.AREA_NAVBAR);
|
||||
}
|
||||
registerCleanupFunction(() => {
|
||||
CustomizableUI.reset();
|
||||
});
|
||||
|
||||
let widgetGroupWrapper = CustomizableUI.getWidget("pocket-button");
|
||||
button = widgetGroupWrapper.forWindow(window).node;
|
||||
ok(button, "Got button node");
|
||||
} else {
|
||||
todo(false, "Pocket is disabled so skip its UITour tests");
|
||||
tests = [];
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
Components.utils.import("resource://gre/modules/UITelemetry.jsm");
|
||||
Components.utils.import("resource:///modules/BrowserUITelemetry.jsm");
|
||||
|
||||
add_task(function* setup_telemetry() {
|
||||
UITelemetry._enabled = true;
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.uitour.seenPageIDs");
|
||||
resetSeenPageIDsLazyGetter();
|
||||
UITelemetry._enabled = undefined;
|
||||
BrowserUITelemetry.setBucket(null);
|
||||
delete window.UITelemetry;
|
||||
delete window.BrowserUITelemetry;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
function resetSeenPageIDsLazyGetter() {
|
||||
delete UITour.seenPageIDs;
|
||||
// This should be kept in sync with how UITour.init() sets this.
|
||||
Object.defineProperty(UITour, "seenPageIDs", {
|
||||
get: UITour.restoreSeenPageIDs.bind(UITour),
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
function checkExpectedSeenPageIDs(expected) {
|
||||
is(UITour.seenPageIDs.size, expected.length, "Should be " + expected.length + " total seen page IDs");
|
||||
|
||||
for (let id of expected)
|
||||
ok(UITour.seenPageIDs.has(id), "Should have seen '" + id + "' page ID");
|
||||
|
||||
let prefData = Services.prefs.getCharPref("browser.uitour.seenPageIDs");
|
||||
prefData = new Map(JSON.parse(prefData));
|
||||
|
||||
is(prefData.size, expected.length, "Should be " + expected.length + " total seen page IDs persisted");
|
||||
|
||||
for (let id of expected)
|
||||
ok(prefData.has(id), "Should have seen '" + id + "' page ID persisted");
|
||||
}
|
||||
|
||||
|
||||
add_UITour_task(function test_seenPageIDs_restore() {
|
||||
info("Setting up seenPageIDs to be restored from pref");
|
||||
let data = JSON.stringify([
|
||||
["savedID1", { lastSeen: Date.now() }],
|
||||
["savedID2", { lastSeen: Date.now() }],
|
||||
// 9 weeks ago, should auto expire.
|
||||
["savedID3", { lastSeen: Date.now() - 9 * 7 * 24 * 60 * 60 * 1000 }],
|
||||
]);
|
||||
Services.prefs.setCharPref("browser.uitour.seenPageIDs",
|
||||
data);
|
||||
|
||||
resetSeenPageIDsLazyGetter();
|
||||
checkExpectedSeenPageIDs(["savedID1", "savedID2"]);
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_seenPageIDs_set_1() {
|
||||
yield gContentAPI.registerPageID("testpage1");
|
||||
|
||||
yield waitForConditionPromise(() => UITour.seenPageIDs.size == 3, "Waiting for page to be registered.");
|
||||
|
||||
checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1"]);
|
||||
|
||||
const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
|
||||
const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
|
||||
|
||||
let bucket = PREFIX + "UITour" + SEP + "testpage1";
|
||||
is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
||||
bucket = PREFIX + "UITour" + SEP + "testpage1" + SEP + "inactive" + SEP + "1m";
|
||||
is(BrowserUITelemetry.currentBucket, bucket,
|
||||
"After switching tabs, bucket should be expiring");
|
||||
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
gBrowser.selectedTab = gTestTab;
|
||||
BrowserUITelemetry.setBucket(null);
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_seenPageIDs_set_2() {
|
||||
yield gContentAPI.registerPageID("testpage2");
|
||||
|
||||
yield waitForConditionPromise(() => UITour.seenPageIDs.size == 4, "Waiting for page to be registered.");
|
||||
|
||||
checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1", "testpage2"]);
|
||||
|
||||
const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
|
||||
const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
|
||||
|
||||
let bucket = PREFIX + "UITour" + SEP + "testpage2";
|
||||
is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
|
||||
|
||||
gBrowser.removeTab(gTestTab);
|
||||
gTestTab = null;
|
||||
bucket = PREFIX + "UITour" + SEP + "testpage2" + SEP + "closed" + SEP + "1m";
|
||||
is(BrowserUITelemetry.currentBucket, bucket,
|
||||
"After closing tab, bucket should be expiring");
|
||||
|
||||
BrowserUITelemetry.setBucket(null);
|
||||
});
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
// Test that a reset profile dialog appears when "resetFirefox" event is triggered
|
||||
add_UITour_task(function* test_resetFirefox() {
|
||||
let canReset = yield getConfigurationPromise("canReset");
|
||||
ok(!canReset, "Shouldn't be able to reset from mochitest's temporary profile.");
|
||||
let dialogPromise = new Promise((resolve) => {
|
||||
let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
winWatcher.registerNotification(function onOpen(subj, topic, data) {
|
||||
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
|
||||
subj.addEventListener("load", function onLoad() {
|
||||
subj.removeEventListener("load", onLoad);
|
||||
if (subj.document.documentURI ==
|
||||
"chrome://global/content/resetProfile.xul") {
|
||||
winWatcher.unregisterNotification(onOpen);
|
||||
ok(true, "Observed search manager window open");
|
||||
is(subj.opener, window,
|
||||
"Reset Firefox event opened a reset profile window.");
|
||||
subj.close();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// make reset possible.
|
||||
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].
|
||||
getService(Ci.nsIToolkitProfileService);
|
||||
let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
let profileName = "mochitest-test-profile-temp-" + Date.now();
|
||||
let tempProfile = profileService.createProfile(currentProfileDir, profileName);
|
||||
canReset = yield getConfigurationPromise("canReset");
|
||||
ok(canReset, "Should be able to reset from mochitest's temporary profile once it's in the profile manager.");
|
||||
yield gContentAPI.resetFirefox();
|
||||
yield dialogPromise;
|
||||
tempProfile.remove(false);
|
||||
canReset = yield getConfigurationPromise("canReset");
|
||||
ok(!canReset, "Shouldn't be able to reset from mochitest's temporary profile once removed from the profile manager.");
|
||||
});
|
||||
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
// Test that we can switch to about:newtab
|
||||
add_UITour_task(function* test_aboutNewTab() {
|
||||
let newTabLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, "about:newtab");
|
||||
info("Showing about:newtab");
|
||||
yield gContentAPI.showNewTab();
|
||||
info("Waiting for about:newtab to load");
|
||||
yield newTabLoaded;
|
||||
is(gBrowser.selectedBrowser.currentURI.spec, "about:newtab", "Loaded about:newtab");
|
||||
});
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
});
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_checkSyncSetup_disabled() {
|
||||
let result = yield getConfigurationPromise("sync");
|
||||
is(result.setup, false, "Sync shouldn't be setup by default");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_checkSyncSetup_enabled() {
|
||||
Services.prefs.setCharPref("services.sync.username", "uitour@tests.mozilla.org");
|
||||
let result = yield getConfigurationPromise("sync");
|
||||
is(result.setup, true, "Sync should be setup");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_checkSyncCounts() {
|
||||
Services.prefs.setIntPref("services.sync.clients.devices.desktop", 4);
|
||||
Services.prefs.setIntPref("services.sync.clients.devices.mobile", 5);
|
||||
Services.prefs.setIntPref("services.sync.numClients", 9);
|
||||
let result = yield getConfigurationPromise("sync");
|
||||
is(result.mobileDevices, 5, "mobileDevices should be set");
|
||||
is(result.desktopDevices, 4, "desktopDevices should be set");
|
||||
is(result.totalDevices, 9, "totalDevices should be set");
|
||||
|
||||
Services.prefs.clearUserPref("services.sync.clients.devices.desktop");
|
||||
result = yield getConfigurationPromise("sync");
|
||||
is(result.mobileDevices, 5, "mobileDevices should be set");
|
||||
is(result.desktopDevices, 0, "desktopDevices should be 0");
|
||||
is(result.totalDevices, 9, "totalDevices should be set");
|
||||
|
||||
Services.prefs.clearUserPref("services.sync.clients.devices.mobile");
|
||||
result = yield getConfigurationPromise("sync");
|
||||
is(result.mobileDevices, 0, "mobileDevices should be 0");
|
||||
is(result.desktopDevices, 0, "desktopDevices should be 0");
|
||||
is(result.totalDevices, 9, "totalDevices should be set");
|
||||
|
||||
Services.prefs.clearUserPref("services.sync.numClients");
|
||||
result = yield getConfigurationPromise("sync");
|
||||
is(result.mobileDevices, 0, "mobileDevices should be 0");
|
||||
is(result.desktopDevices, 0, "desktopDevices should be 0");
|
||||
is(result.totalDevices, 0, "totalDevices should be 0");
|
||||
});
|
||||
|
||||
// The showFirefoxAccounts API is sync related, so we test that here too...
|
||||
add_UITour_task(function* test_firefoxAccountsNoParams() {
|
||||
yield gContentAPI.showFirefoxAccounts();
|
||||
yield BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
|
||||
"about:accounts?action=signup&entrypoint=uitour");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_firefoxAccountsValidParams() {
|
||||
yield gContentAPI.showFirefoxAccounts({ utm_foo: "foo", utm_bar: "bar" });
|
||||
yield BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
|
||||
"about:accounts?action=signup&entrypoint=uitour&utm_foo=foo&utm_bar=bar");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_firefoxAccountsNonAlphaValue() {
|
||||
// All characters in the value are allowed, but they must be automatically escaped.
|
||||
// (we throw a unicode character in there too - it's not auto-utf8 encoded,
|
||||
// but that's ok, so long as it is escaped correctly.)
|
||||
let value = "foo& /=?:\\\xa9";
|
||||
// encodeURIComponent encodes spaces to %20 but we want "+"
|
||||
let expected = encodeURIComponent(value).replace(/%20/g, "+");
|
||||
yield gContentAPI.showFirefoxAccounts({ utm_foo: value });
|
||||
yield BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
|
||||
"about:accounts?action=signup&entrypoint=uitour&utm_foo=" + expected);
|
||||
});
|
||||
|
||||
// A helper to check the request was ignored due to invalid params.
|
||||
function* checkAboutAccountsNotLoaded() {
|
||||
try {
|
||||
yield waitForConditionPromise(() => {
|
||||
return gBrowser.selectedBrowser.currentURI.spec.startsWith("about:accounts");
|
||||
}, "Check if about:accounts opened");
|
||||
ok(false, "No about:accounts tab should have opened");
|
||||
} catch (ex) {
|
||||
ok(true, "No about:accounts tab opened");
|
||||
}
|
||||
}
|
||||
|
||||
add_UITour_task(function* test_firefoxAccountsNonObject() {
|
||||
// non-string should be rejected.
|
||||
yield gContentAPI.showFirefoxAccounts(99);
|
||||
yield checkAboutAccountsNotLoaded();
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_firefoxAccountsNonUtmPrefix() {
|
||||
// Any non "utm_" name should should be rejected.
|
||||
yield gContentAPI.showFirefoxAccounts({ utm_foo: "foo", bar: "bar" });
|
||||
yield checkAboutAccountsNotLoaded();
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_firefoxAccountsNonAlphaName() {
|
||||
// Any "utm_" name which includes non-alpha chars should be rejected.
|
||||
yield gContentAPI.showFirefoxAccounts({ utm_foo: "foo", "utm_bar=": "bar" });
|
||||
yield checkAboutAccountsNotLoaded();
|
||||
});
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function*() {
|
||||
ok(!gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"),
|
||||
"Should not be in reader mode at start of test.");
|
||||
yield gContentAPI.toggleReaderMode();
|
||||
yield waitForConditionPromise(() => gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"));
|
||||
ok(gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"),
|
||||
"Should be in reader mode now.");
|
||||
});
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
requestLongerTimeout(2);
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_bg_getConfiguration() {
|
||||
info("getConfiguration is on the allowed list so should work");
|
||||
yield* loadForegroundTab();
|
||||
let data = yield getConfigurationPromise("availableTargets");
|
||||
ok(data, "Got data from getConfiguration");
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_bg_showInfo() {
|
||||
info("showInfo isn't on the allowed action list so should be denied");
|
||||
yield* loadForegroundTab();
|
||||
|
||||
yield showInfoPromise("appMenu", "Hello from the background", "Surprise!").then(
|
||||
() => ok(false, "panel shouldn't have shown from a background tab"),
|
||||
() => ok(true, "panel wasn't shown from a background tab"));
|
||||
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
|
||||
function* loadForegroundTab() {
|
||||
// Spawn a content task that resolves once we're sure the visibilityState was
|
||||
// changed. This state is what the tests in this file rely on.
|
||||
let promise = ContentTask.spawn(gBrowser.selectedTab.linkedBrowser, null, function* () {
|
||||
return new Promise(resolve => {
|
||||
let document = content.document;
|
||||
document.addEventListener("visibilitychange", function onStateChange() {
|
||||
Assert.equal(document.visibilityState, "hidden", "UITour page should be hidden now.");
|
||||
document.removeEventListener("visibilitychange", onStateChange);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
yield promise;
|
||||
isnot(gBrowser.selectedTab, gTestTab, "Make sure tour tab isn't selected");
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_closeTab() {
|
||||
// Setting gTestTab to null indicates that the tab has already been closed,
|
||||
// and if this does not happen the test run will fail.
|
||||
let closePromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabClose");
|
||||
yield gContentAPI.closeTab();
|
||||
yield closePromise;
|
||||
gTestTab = null;
|
||||
});
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
registerCleanupFunction(function*() {
|
||||
yield signOut();
|
||||
gFxAccounts.updateAppMenuItem();
|
||||
});
|
||||
|
||||
var tests = [
|
||||
taskify(function* test_highlight_accountStatus_loggedOut() {
|
||||
let userData = yield fxAccounts.getSignedInUser();
|
||||
is(userData, null, "Not logged in initially");
|
||||
yield showMenuPromise("appMenu");
|
||||
yield showHighlightPromise("accountStatus");
|
||||
let highlight = document.getElementById("UITourHighlightContainer");
|
||||
is(highlight.getAttribute("targetName"), "accountStatus", "Correct highlight target");
|
||||
}),
|
||||
|
||||
taskify(function* test_highlight_accountStatus_loggedIn() {
|
||||
yield setSignedInUser();
|
||||
let userData = yield fxAccounts.getSignedInUser();
|
||||
isnot(userData, null, "Logged in now");
|
||||
gFxAccounts.updateAppMenuItem(); // Causes a leak
|
||||
yield showMenuPromise("appMenu");
|
||||
yield showHighlightPromise("accountStatus");
|
||||
let highlight = document.getElementById("UITourHighlightContainer");
|
||||
is(highlight.popupBoxObject.anchorNode.id, "PanelUI-fxa-avatar", "Anchored on avatar");
|
||||
is(highlight.getAttribute("targetName"), "accountStatus", "Correct highlight target");
|
||||
}),
|
||||
];
|
||||
|
||||
// Helpers copied from browser_aboutAccounts.js
|
||||
// watch out - these will fire observers which if you aren't careful, may
|
||||
// interfere with the tests.
|
||||
function setSignedInUser(data) {
|
||||
if (!data) {
|
||||
data = {
|
||||
email: "foo@example.com",
|
||||
uid: "1234@lcip.org",
|
||||
assertion: "foobar",
|
||||
sessionToken: "dead",
|
||||
kA: "beef",
|
||||
kB: "cafe",
|
||||
verified: true
|
||||
};
|
||||
}
|
||||
return fxAccounts.setSignedInUser(data);
|
||||
}
|
||||
|
||||
function signOut() {
|
||||
// we always want a "localOnly" signout here...
|
||||
return fxAccounts.signOut(true);
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var HiddenFrame = Cu.import("resource:///modules/HiddenFrame.jsm", {}).HiddenFrame;
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
/**
|
||||
* Create a frame in the |hiddenDOMWindow| to host a |browser|, then load the URL in the
|
||||
* latter.
|
||||
*
|
||||
* @param aURL
|
||||
* The URL to open in the browser.
|
||||
**/
|
||||
function createHiddenBrowser(aURL) {
|
||||
let frame = new HiddenFrame();
|
||||
return new Promise(resolve =>
|
||||
frame.get().then(aFrame => {
|
||||
let doc = aFrame.document;
|
||||
let browser = doc.createElementNS(XUL_NS, "browser");
|
||||
browser.setAttribute("type", "content");
|
||||
browser.setAttribute("disableglobalhistory", "true");
|
||||
browser.setAttribute("src", aURL);
|
||||
|
||||
doc.documentElement.appendChild(browser);
|
||||
resolve({frame: frame, browser: browser});
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the browser and the HiddenFrame.
|
||||
*
|
||||
* @param aFrame
|
||||
* The HiddenFrame to dismiss.
|
||||
* @param aBrowser
|
||||
* The browser to dismiss.
|
||||
*/
|
||||
function destroyHiddenBrowser(aFrame, aBrowser) {
|
||||
// Dispose of the hidden browser.
|
||||
aBrowser.remove();
|
||||
|
||||
// Take care of the frame holding our invisible browser.
|
||||
aFrame.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that UITour works when called when no tabs are available (e.g., when using windowless
|
||||
* browsers).
|
||||
*/
|
||||
add_task(function* test_windowless_UITour() {
|
||||
// Get the URL for the test page.
|
||||
let pageURL = getRootDirectory(gTestPath) + "uitour.html";
|
||||
|
||||
// Allow the URL to use the UITour.
|
||||
info("Adding UITour permission to the test page.");
|
||||
let pageURI = Services.io.newURI(pageURL, null, null);
|
||||
Services.perms.add(pageURI, "uitour", Services.perms.ALLOW_ACTION);
|
||||
|
||||
// UITour's ping will resolve this promise.
|
||||
let deferredPing = Promise.defer();
|
||||
|
||||
// Create a windowless browser and test that UITour works in it.
|
||||
let browserPromise = createHiddenBrowser(pageURL);
|
||||
browserPromise.then(frameInfo => {
|
||||
isnot(frameInfo.browser, null, "The browser must exist and not be null.");
|
||||
|
||||
// Load UITour frame script.
|
||||
frameInfo.browser.messageManager.loadFrameScript(
|
||||
"chrome://browser/content/content-UITour.js", false);
|
||||
|
||||
// When the page loads, try to use UITour API.
|
||||
frameInfo.browser.addEventListener("load", function loadListener() {
|
||||
info("The test page was correctly loaded.");
|
||||
|
||||
frameInfo.browser.removeEventListener("load", loadListener, true);
|
||||
|
||||
// Get a reference to the UITour API.
|
||||
info("Testing access to the UITour API.");
|
||||
let contentWindow = Cu.waiveXrays(frameInfo.browser.contentDocument.defaultView);
|
||||
isnot(contentWindow, null, "The content window must exist and not be null.");
|
||||
|
||||
let uitourAPI = contentWindow.Mozilla.UITour;
|
||||
|
||||
// Test the UITour API with a ping.
|
||||
uitourAPI.ping(function() {
|
||||
info("Ping response received from the UITour API.");
|
||||
|
||||
// Make sure to clean up.
|
||||
destroyHiddenBrowser(frameInfo.frame, frameInfo.browser);
|
||||
|
||||
// Resolve our promise.
|
||||
deferredPing.resolve();
|
||||
});
|
||||
}, true);
|
||||
});
|
||||
|
||||
// Wait for the UITour ping to complete.
|
||||
yield deferredPing.promise;
|
||||
});
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_openPreferences() {
|
||||
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences");
|
||||
yield gContentAPI.openPreferences();
|
||||
let tab = yield promiseTabOpened;
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_openInvalidPreferences() {
|
||||
yield gContentAPI.openPreferences(999);
|
||||
|
||||
try {
|
||||
yield waitForConditionPromise(() => {
|
||||
return gBrowser.selectedBrowser.currentURI.spec.startsWith("about:preferences");
|
||||
}, "Check if about:preferences opened");
|
||||
ok(false, "No about:preferences tab should have opened");
|
||||
} catch (ex) {
|
||||
ok(true, "No about:preferences tab opened: " + ex);
|
||||
}
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_openPrivacyPreferences() {
|
||||
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy");
|
||||
yield gContentAPI.openPreferences("privacy");
|
||||
let tab = yield promiseTabOpened;
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function test_openSearchPanel(done) {
|
||||
let searchbar = document.getElementById("searchbar");
|
||||
|
||||
// If suggestions are enabled, the panel will attempt to use the network to connect
|
||||
// to the suggestions provider, causing the test suite to fail.
|
||||
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("browser.search.suggest.enabled");
|
||||
});
|
||||
|
||||
ok(!searchbar.textbox.open, "Popup starts as closed");
|
||||
gContentAPI.openSearchPanel(() => {
|
||||
ok(searchbar.textbox.open, "Popup was opened");
|
||||
searchbar.textbox.closePopup();
|
||||
ok(!searchbar.textbox.open, "Popup was closed");
|
||||
done();
|
||||
});
|
||||
},
|
||||
];
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
const CONTROL_CENTER_PANEL = gIdentityHandler._identityPopup;
|
||||
const CONTROL_CENTER_MENU_NAME = "controlCenter";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_UITour_task(function* test_showMenu() {
|
||||
is_element_hidden(CONTROL_CENTER_PANEL, "Panel should initially be hidden");
|
||||
yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
|
||||
is_element_visible(CONTROL_CENTER_PANEL, "Panel should be visible after showMenu");
|
||||
|
||||
yield gURLBar.focus();
|
||||
is_element_visible(CONTROL_CENTER_PANEL, "Panel should remain visible after focus outside");
|
||||
|
||||
yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
|
||||
is_element_visible(CONTROL_CENTER_PANEL,
|
||||
"Panel should remain visible and callback called after a 2nd showMenu");
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, function*() {
|
||||
ok(true, "Tab opened");
|
||||
});
|
||||
|
||||
is_element_hidden(CONTROL_CENTER_PANEL, "Panel should hide upon tab switch");
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_hideMenu() {
|
||||
is_element_hidden(CONTROL_CENTER_PANEL, "Panel should initially be hidden");
|
||||
yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
|
||||
is_element_visible(CONTROL_CENTER_PANEL, "Panel should be visible after showMenu");
|
||||
let hidePromise = promisePanelElementHidden(window, CONTROL_CENTER_PANEL);
|
||||
yield gContentAPI.hideMenu(CONTROL_CENTER_MENU_NAME);
|
||||
yield hidePromise;
|
||||
|
||||
is_element_hidden(CONTROL_CENTER_PANEL, "Panel should hide after hideMenu");
|
||||
});
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
const PREF_INTRO_COUNT = "privacy.trackingprotection.introCount";
|
||||
const PREF_TP_ENABLED = "privacy.trackingprotection.enabled";
|
||||
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html";
|
||||
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html";
|
||||
const TOOLTIP_PANEL = document.getElementById("UITourTooltip");
|
||||
const TOOLTIP_ANCHOR = document.getElementById("tracking-protection-icon");
|
||||
|
||||
var {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
Services.prefs.clearUserPref(PREF_TP_ENABLED);
|
||||
Services.prefs.clearUserPref(PREF_INTRO_COUNT);
|
||||
});
|
||||
|
||||
function allowOneIntro() {
|
||||
Services.prefs.setIntPref(PREF_INTRO_COUNT, TrackingProtection.MAX_INTROS - 1);
|
||||
}
|
||||
|
||||
add_task(function* setup_test() {
|
||||
Services.prefs.setBoolPref(PREF_TP_ENABLED, true);
|
||||
yield UrlClassifierTestUtils.addTestTrackers();
|
||||
});
|
||||
|
||||
add_task(function* test_benignPage() {
|
||||
info("Load a test page not containing tracking elements");
|
||||
allowOneIntro();
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url: BENIGN_PAGE}, function*() {
|
||||
yield waitForConditionPromise(() => {
|
||||
return is_visible(TOOLTIP_PANEL);
|
||||
}, "Info panel shouldn't appear on a benign page").
|
||||
then(() => ok(false, "Info panel shouldn't appear"),
|
||||
() => {
|
||||
ok(true, "Info panel didn't appear on a benign page");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_trackingPages() {
|
||||
info("Load a test page containing tracking elements");
|
||||
allowOneIntro();
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url: TRACKING_PAGE}, function*() {
|
||||
yield new Promise((resolve, reject) => {
|
||||
waitForPopupAtAnchor(TOOLTIP_PANEL, TOOLTIP_ANCHOR, resolve,
|
||||
"Intro panel should appear");
|
||||
});
|
||||
|
||||
is(Services.prefs.getIntPref(PREF_INTRO_COUNT), TrackingProtection.MAX_INTROS, "Check intro count increased");
|
||||
|
||||
let step2URL = Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL") +
|
||||
"?step=2&newtab=true";
|
||||
let buttons = document.getElementById("UITourTooltipButtons");
|
||||
|
||||
info("Click the step text and nothing should happen");
|
||||
let tabCount = gBrowser.tabs.length;
|
||||
yield EventUtils.synthesizeMouseAtCenter(buttons.children[0], {});
|
||||
is(gBrowser.tabs.length, tabCount, "Same number of tabs should be open");
|
||||
|
||||
info("Resetting count to test that viewing the tour prevents future panels");
|
||||
allowOneIntro();
|
||||
|
||||
let panelHiddenPromise = promisePanelElementHidden(window, TOOLTIP_PANEL);
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, step2URL);
|
||||
info("Clicking the main button");
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.children[1], {});
|
||||
let tab = yield tabPromise;
|
||||
is(Services.prefs.getIntPref(PREF_INTRO_COUNT), TrackingProtection.MAX_INTROS,
|
||||
"Check intro count is at the max after opening step 2");
|
||||
is(gBrowser.tabs.length, tabCount + 1, "Tour step 2 tab opened");
|
||||
yield panelHiddenPromise;
|
||||
ok(true, "Panel hid when the button was clicked");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
info("Open another tracking page and make sure we don't show the panel again");
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url: TRACKING_PAGE}, function*() {
|
||||
yield waitForConditionPromise(() => {
|
||||
return is_visible(TOOLTIP_PANEL);
|
||||
}, "Info panel shouldn't appear more than MAX_INTROS").
|
||||
then(() => ok(false, "Info panel shouldn't appear again"),
|
||||
() => {
|
||||
ok(true, "Info panel didn't appear more than MAX_INTROS on tracking pages");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var gTestTab;
|
||||
var gContentAPI;
|
||||
var gContentWindow;
|
||||
|
||||
const { UrlClassifierTestUtils } = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
|
||||
|
||||
const TP_ENABLED_PREF = "privacy.trackingprotection.enabled";
|
||||
|
||||
add_task(setup_UITourTest);
|
||||
|
||||
add_task(function* test_setup() {
|
||||
Services.prefs.setBoolPref("privacy.trackingprotection.enabled", true);
|
||||
yield UrlClassifierTestUtils.addTestTrackers();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
Services.prefs.clearUserPref("privacy.trackingprotection.enabled");
|
||||
});
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_unblock_target() {
|
||||
yield* checkToggleTarget("controlCenter-trackingUnblock");
|
||||
});
|
||||
|
||||
add_UITour_task(function* setup_block_target() {
|
||||
// Preparation for test_block_target. These are separate since the reload
|
||||
// interferes with UITour as it does a teardown. All we really care about
|
||||
// is the permission manager entry but UITour tests shouldn't rely on that
|
||||
// implementation detail.
|
||||
TrackingProtection.disableForCurrentPage();
|
||||
});
|
||||
|
||||
add_UITour_task(function* test_block_target() {
|
||||
yield* checkToggleTarget("controlCenter-trackingBlock");
|
||||
TrackingProtection.enableForCurrentPage();
|
||||
});
|
||||
|
||||
|
||||
function* checkToggleTarget(targetID) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
|
||||
let doc = content.document;
|
||||
let iframe = doc.createElement("iframe");
|
||||
iframe.setAttribute("id", "tracking-element");
|
||||
iframe.setAttribute("src", "https://tracking.example.com/");
|
||||
doc.body.insertBefore(iframe, doc.body.firstChild);
|
||||
});
|
||||
|
||||
let testTargetAvailability = function* (expectedAvailable) {
|
||||
let data = yield getConfigurationPromise("availableTargets");
|
||||
let available = (data.targets.indexOf(targetID) != -1);
|
||||
is(available, expectedAvailable, "Target has expected availability.");
|
||||
};
|
||||
yield testTargetAvailability(false);
|
||||
yield showMenuPromise("controlCenter");
|
||||
yield testTargetAvailability(true);
|
||||
|
||||
yield showInfoPromise(targetID, "This is " + targetID,
|
||||
"My arrow should be on the side");
|
||||
is(popup.popupBoxObject.alignmentPosition, "end_before",
|
||||
"Check " + targetID + " position");
|
||||
|
||||
let hideMenuPromise =
|
||||
promisePanelElementHidden(window, gIdentityHandler._identityPopup);
|
||||
yield gContentAPI.hideMenu("controlCenter");
|
||||
yield hideMenuPromise;
|
||||
|
||||
ok(!is_visible(popup), "The tooltip should now be hidden.");
|
||||
yield testTargetAvailability(false);
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
|
||||
content.document.getElementById("tracking-element").remove();
|
||||
});
|
||||
}
|
||||
|
|
@ -1,449 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||
"resource:///modules/UITour.jsm");
|
||||
|
||||
|
||||
const SINGLE_TRY_TIMEOUT = 100;
|
||||
const NUMBER_OF_TRIES = 30;
|
||||
|
||||
function waitForConditionPromise(condition, timeoutMsg, tryCount=NUMBER_OF_TRIES) {
|
||||
let defer = Promise.defer();
|
||||
let tries = 0;
|
||||
function checkCondition() {
|
||||
if (tries >= tryCount) {
|
||||
defer.reject(timeoutMsg);
|
||||
}
|
||||
var conditionPassed;
|
||||
try {
|
||||
conditionPassed = condition();
|
||||
} catch (e) {
|
||||
return defer.reject(e);
|
||||
}
|
||||
if (conditionPassed) {
|
||||
return defer.resolve();
|
||||
}
|
||||
tries++;
|
||||
setTimeout(checkCondition, SINGLE_TRY_TIMEOUT);
|
||||
return undefined;
|
||||
}
|
||||
setTimeout(checkCondition, SINGLE_TRY_TIMEOUT);
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
function waitForCondition(condition, nextTest, errorMsg) {
|
||||
waitForConditionPromise(condition, errorMsg).then(nextTest, (reason) => {
|
||||
ok(false, reason + (reason.stack ? "\n" + reason.stack : ""));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper to partially transition tests to Task. Use `add_UITour_task` instead for new tests.
|
||||
*/
|
||||
function taskify(fun) {
|
||||
return (done) => {
|
||||
// Output the inner function name otherwise no name will be output.
|
||||
info("\t" + fun.name);
|
||||
return Task.spawn(fun).then(done, (reason) => {
|
||||
ok(false, reason);
|
||||
done();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function is_hidden(element) {
|
||||
var style = element.ownerGlobal.getComputedStyle(element);
|
||||
if (style.display == "none")
|
||||
return true;
|
||||
if (style.visibility != "visible")
|
||||
return true;
|
||||
if (style.display == "-moz-popup")
|
||||
return ["hiding", "closed"].indexOf(element.state) != -1;
|
||||
|
||||
// Hiding a parent element will hide all its children
|
||||
if (element.parentNode != element.ownerDocument)
|
||||
return is_hidden(element.parentNode);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_visible(element) {
|
||||
var style = element.ownerGlobal.getComputedStyle(element);
|
||||
if (style.display == "none")
|
||||
return false;
|
||||
if (style.visibility != "visible")
|
||||
return false;
|
||||
if (style.display == "-moz-popup" && element.state != "open")
|
||||
return false;
|
||||
|
||||
// Hiding a parent element will hide all its children
|
||||
if (element.parentNode != element.ownerDocument)
|
||||
return is_visible(element.parentNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function is_element_visible(element, msg) {
|
||||
isnot(element, null, "Element should not be null, when checking visibility");
|
||||
ok(is_visible(element), msg);
|
||||
}
|
||||
|
||||
function waitForElementToBeVisible(element, nextTest, msg) {
|
||||
waitForCondition(() => is_visible(element),
|
||||
() => {
|
||||
ok(true, msg);
|
||||
nextTest();
|
||||
},
|
||||
"Timeout waiting for visibility: " + msg);
|
||||
}
|
||||
|
||||
function waitForElementToBeHidden(element, nextTest, msg) {
|
||||
waitForCondition(() => is_hidden(element),
|
||||
() => {
|
||||
ok(true, msg);
|
||||
nextTest();
|
||||
},
|
||||
"Timeout waiting for invisibility: " + msg);
|
||||
}
|
||||
|
||||
function elementVisiblePromise(element, msg) {
|
||||
return waitForConditionPromise(() => is_visible(element), "Timeout waiting for visibility: " + msg);
|
||||
}
|
||||
|
||||
function elementHiddenPromise(element, msg) {
|
||||
return waitForConditionPromise(() => is_hidden(element), "Timeout waiting for invisibility: " + msg);
|
||||
}
|
||||
|
||||
function waitForPopupAtAnchor(popup, anchorNode, nextTest, msg) {
|
||||
waitForCondition(() => is_visible(popup) && popup.popupBoxObject.anchorNode == anchorNode,
|
||||
() => {
|
||||
ok(true, msg);
|
||||
is_element_visible(popup, "Popup should be visible");
|
||||
nextTest();
|
||||
},
|
||||
"Timeout waiting for popup at anchor: " + msg);
|
||||
}
|
||||
|
||||
function getConfigurationPromise(configName) {
|
||||
return ContentTask.spawn(gTestTab.linkedBrowser, configName, configName => {
|
||||
return new Promise((resolve) => {
|
||||
let contentWin = Components.utils.waiveXrays(content);
|
||||
contentWin.Mozilla.UITour.getConfiguration(configName, resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hideInfoPromise(...args) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
gContentAPI.hideInfo.apply(gContentAPI, args);
|
||||
return promisePanelElementHidden(window, popup);
|
||||
}
|
||||
|
||||
/**
|
||||
* `buttons` and `options` require functions from the content scope so we take a
|
||||
* function name to call to generate the buttons/options instead of the
|
||||
* buttons/options themselves. This makes the signature differ from the content one.
|
||||
*/
|
||||
function showInfoPromise(target, title, text, icon, buttonsFunctionName, optionsFunctionName) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let shownPromise = promisePanelElementShown(window, popup);
|
||||
return ContentTask.spawn(gTestTab.linkedBrowser, [...arguments], args => {
|
||||
let contentWin = Components.utils.waiveXrays(content);
|
||||
let [target, title, text, icon, buttonsFunctionName, optionsFunctionName] = args;
|
||||
let buttons = buttonsFunctionName ? contentWin[buttonsFunctionName]() : null;
|
||||
let options = optionsFunctionName ? contentWin[optionsFunctionName]() : null;
|
||||
contentWin.Mozilla.UITour.showInfo(target, title, text, icon, buttons, options);
|
||||
}).then(() => shownPromise);
|
||||
}
|
||||
|
||||
function showHighlightPromise(...args) {
|
||||
let popup = document.getElementById("UITourHighlightContainer");
|
||||
gContentAPI.showHighlight.apply(gContentAPI, args);
|
||||
return promisePanelElementShown(window, popup);
|
||||
}
|
||||
|
||||
function showMenuPromise(name) {
|
||||
return ContentTask.spawn(gTestTab.linkedBrowser, name, name => {
|
||||
return new Promise((resolve) => {
|
||||
let contentWin = Components.utils.waiveXrays(content);
|
||||
contentWin.Mozilla.UITour.showMenu(name, resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForCallbackResultPromise() {
|
||||
return ContentTask.spawn(gTestTab.linkedBrowser, null, function*() {
|
||||
let contentWin = Components.utils.waiveXrays(content);
|
||||
yield ContentTaskUtils.waitForCondition(() => {
|
||||
return contentWin.callbackResult;
|
||||
}, "callback should be called");
|
||||
return {
|
||||
data: contentWin.callbackData,
|
||||
result: contentWin.callbackResult,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function promisePanelShown(win) {
|
||||
let panelEl = win.PanelUI.panel;
|
||||
return promisePanelElementShown(win, panelEl);
|
||||
}
|
||||
|
||||
function promisePanelElementEvent(win, aPanel, aEvent) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timeoutId = win.setTimeout(() => {
|
||||
aPanel.removeEventListener(aEvent, onPanelEvent);
|
||||
reject(aEvent + " event did not happen within 5 seconds.");
|
||||
}, 5000);
|
||||
|
||||
function onPanelEvent(e) {
|
||||
aPanel.removeEventListener(aEvent, onPanelEvent);
|
||||
win.clearTimeout(timeoutId);
|
||||
// Wait one tick to let UITour.jsm process the event as well.
|
||||
executeSoon(resolve);
|
||||
}
|
||||
|
||||
aPanel.addEventListener(aEvent, onPanelEvent);
|
||||
});
|
||||
}
|
||||
|
||||
function promisePanelElementShown(win, aPanel) {
|
||||
return promisePanelElementEvent(win, aPanel, "popupshown");
|
||||
}
|
||||
|
||||
function promisePanelElementHidden(win, aPanel) {
|
||||
return promisePanelElementEvent(win, aPanel, "popuphidden");
|
||||
}
|
||||
|
||||
function is_element_hidden(element, msg) {
|
||||
isnot(element, null, "Element should not be null, when checking visibility");
|
||||
ok(is_hidden(element), msg);
|
||||
}
|
||||
|
||||
function isTourBrowser(aBrowser) {
|
||||
let chromeWindow = aBrowser.ownerGlobal;
|
||||
return UITour.tourBrowsersByWindow.has(chromeWindow) &&
|
||||
UITour.tourBrowsersByWindow.get(chromeWindow).has(aBrowser);
|
||||
}
|
||||
|
||||
function promisePageEvent() {
|
||||
return new Promise((resolve) => {
|
||||
Services.mm.addMessageListener("UITour:onPageEvent", function onPageEvent(aMessage) {
|
||||
Services.mm.removeMessageListener("UITour:onPageEvent", onPageEvent);
|
||||
SimpleTest.executeSoon(resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadUITourTestPage(callback, host = "https://example.org/") {
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
|
||||
let url = getRootDirectory(gTestPath) + "uitour.html";
|
||||
url = url.replace("chrome://mochitests/content/", host);
|
||||
|
||||
gTestTab = gBrowser.addTab(url);
|
||||
gBrowser.selectedTab = gTestTab;
|
||||
|
||||
gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
|
||||
gTestTab.linkedBrowser.removeEventListener("load", onLoad, true);
|
||||
|
||||
if (gMultiProcessBrowser) {
|
||||
// When e10s is enabled, make gContentAPI and gContentWindow proxies which has every property
|
||||
// return a function which calls the method of the same name on
|
||||
// contentWin.Mozilla.UITour/contentWin in a ContentTask.
|
||||
let contentWinHandler = {
|
||||
get(target, prop, receiver) {
|
||||
return (...args) => {
|
||||
let taskArgs = {
|
||||
methodName: prop,
|
||||
args,
|
||||
};
|
||||
return ContentTask.spawn(gTestTab.linkedBrowser, taskArgs, args => {
|
||||
let contentWin = Components.utils.waiveXrays(content);
|
||||
return contentWin[args.methodName].apply(contentWin, args.args);
|
||||
});
|
||||
};
|
||||
},
|
||||
};
|
||||
gContentWindow = new Proxy({}, contentWinHandler);
|
||||
|
||||
let UITourHandler = {
|
||||
get(target, prop, receiver) {
|
||||
return (...args) => {
|
||||
let browser = gTestTab.linkedBrowser;
|
||||
const proxyFunctionName = "UITourHandler:proxiedfunction-";
|
||||
// We need to proxy any callback functions using messages:
|
||||
let callbackMap = new Map();
|
||||
let fnIndices = [];
|
||||
args = args.map((arg, index) => {
|
||||
// Replace function arguments with "", and add them to the list of
|
||||
// forwarded functions. We'll construct a function on the content-side
|
||||
// that forwards all its arguments to a message, and we'll listen for
|
||||
// those messages on our side and call the corresponding function with
|
||||
// the arguments we got from the content side.
|
||||
if (typeof arg == "function") {
|
||||
callbackMap.set(index, arg);
|
||||
fnIndices.push(index);
|
||||
let handler = function(msg) {
|
||||
// Please note that this handler assumes that the callback is used only once.
|
||||
// That means that a single gContentAPI.observer() call can't be used to observe
|
||||
// multiple events.
|
||||
browser.messageManager.removeMessageListener(proxyFunctionName + index, handler);
|
||||
callbackMap.get(index).apply(null, msg.data);
|
||||
};
|
||||
browser.messageManager.addMessageListener(proxyFunctionName + index, handler);
|
||||
return "";
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
let taskArgs = {
|
||||
methodName: prop,
|
||||
args,
|
||||
fnIndices,
|
||||
};
|
||||
return ContentTask.spawn(browser, taskArgs, function*(args) {
|
||||
let contentWin = Components.utils.waiveXrays(content);
|
||||
let callbacksCalled = 0;
|
||||
let resolveCallbackPromise;
|
||||
let allCallbacksCalledPromise = new Promise(resolve => resolveCallbackPromise = resolve);
|
||||
let argumentsWithFunctions = args.args.map((arg, index) => {
|
||||
if (arg === "" && args.fnIndices.includes(index)) {
|
||||
return function() {
|
||||
callbacksCalled++;
|
||||
sendAsyncMessage("UITourHandler:proxiedfunction-" + index, Array.from(arguments));
|
||||
if (callbacksCalled >= args.fnIndices.length) {
|
||||
resolveCallbackPromise();
|
||||
}
|
||||
};
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
let rv = contentWin.Mozilla.UITour[args.methodName].apply(contentWin.Mozilla.UITour,
|
||||
argumentsWithFunctions);
|
||||
if (args.fnIndices.length) {
|
||||
yield allCallbacksCalledPromise;
|
||||
}
|
||||
return rv;
|
||||
});
|
||||
};
|
||||
},
|
||||
};
|
||||
gContentAPI = new Proxy({}, UITourHandler);
|
||||
} else {
|
||||
gContentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
|
||||
gContentAPI = gContentWindow.Mozilla.UITour;
|
||||
}
|
||||
|
||||
waitForFocus(callback, gTestTab.linkedBrowser);
|
||||
}, true);
|
||||
}
|
||||
|
||||
// Wrapper for UITourTest to be used by add_task tests.
|
||||
function* setup_UITourTest() {
|
||||
return UITourTest(true);
|
||||
}
|
||||
|
||||
// Use `add_task(setup_UITourTest);` instead as we will fold this into `setup_UITourTest` once all tests are using `add_UITour_task`.
|
||||
function UITourTest(usingAddTask = false) {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
let testHttpsUri = Services.io.newURI("https://example.org", null, null);
|
||||
let testHttpUri = Services.io.newURI("http://example.org", null, null);
|
||||
Services.perms.add(testHttpsUri, "uitour", Services.perms.ALLOW_ACTION);
|
||||
Services.perms.add(testHttpUri, "uitour", Services.perms.ALLOW_ACTION);
|
||||
|
||||
// If a test file is using add_task, we don't need to have a test function or
|
||||
// call `waitForExplicitFinish`.
|
||||
if (!usingAddTask) {
|
||||
waitForExplicitFinish();
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
delete window.gContentWindow;
|
||||
delete window.gContentAPI;
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
delete window.gTestTab;
|
||||
Services.prefs.clearUserPref("browser.uitour.enabled");
|
||||
Services.perms.remove(testHttpsUri, "uitour");
|
||||
Services.perms.remove(testHttpUri, "uitour");
|
||||
});
|
||||
|
||||
// When using tasks, the harness will call the next added task for us.
|
||||
if (!usingAddTask) {
|
||||
nextTest();
|
||||
}
|
||||
}
|
||||
|
||||
function done(usingAddTask = false) {
|
||||
info("== Done test, doing shared checks before teardown ==");
|
||||
return new Promise((resolve) => {
|
||||
executeSoon(() => {
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
gTestTab = null;
|
||||
|
||||
let highlight = document.getElementById("UITourHighlightContainer");
|
||||
is_element_hidden(highlight, "Highlight should be closed/hidden after UITour tab is closed");
|
||||
|
||||
let tooltip = document.getElementById("UITourTooltip");
|
||||
is_element_hidden(tooltip, "Tooltip should be closed/hidden after UITour tab is closed");
|
||||
|
||||
ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up");
|
||||
ok(!PanelUI.panel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
|
||||
isnot(PanelUI.panel.state, "open", "The panel shouldn't be open");
|
||||
is(document.getElementById("PanelUI-menu-button").hasAttribute("open"), false, "Menu button should know that the menu is closed");
|
||||
|
||||
info("Done shared checks");
|
||||
if (usingAddTask) {
|
||||
executeSoon(resolve);
|
||||
} else {
|
||||
executeSoon(nextTest);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length == 0) {
|
||||
info("finished tests in this file");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
let test = tests.shift();
|
||||
info("Starting " + test.name);
|
||||
waitForFocus(function() {
|
||||
loadUITourTestPage(function() {
|
||||
test(done);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* All new tests that need the help of `loadUITourTestPage` should use this
|
||||
* wrapper around their test's generator function to reduce boilerplate.
|
||||
*/
|
||||
function add_UITour_task(func) {
|
||||
let genFun = function*() {
|
||||
yield new Promise((resolve) => {
|
||||
waitForFocus(function() {
|
||||
loadUITourTestPage(function() {
|
||||
let funcPromise = Task.spawn(func)
|
||||
.then(() => done(true),
|
||||
(reason) => {
|
||||
ok(false, reason);
|
||||
return done(true);
|
||||
});
|
||||
resolve(funcPromise);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
Object.defineProperty(genFun, "name", {
|
||||
configurable: true,
|
||||
value: func.name,
|
||||
});
|
||||
add_task(genFun);
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB |
|
|
@ -1,42 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>UITour test</title>
|
||||
<script type="application/javascript" src="UITour-lib.js">
|
||||
</script>
|
||||
<script type="application/javascript">
|
||||
var callbackResult, callbackData;
|
||||
function makeCallback(name) {
|
||||
return (function(data) {
|
||||
callbackResult = name;
|
||||
callbackData = data;
|
||||
});
|
||||
}
|
||||
|
||||
// Defined in content to avoid weird issues when crossing between chrome/content.
|
||||
function makeButtons() {
|
||||
return [
|
||||
{label: "Regular text", style: "text"},
|
||||
{label: "Link", callback: makeCallback("link"), style: "link"},
|
||||
{label: "Button 1", callback: makeCallback("button1")},
|
||||
{label: "Button 2", callback: makeCallback("button2"), icon: "image.png",
|
||||
style: "primary"}
|
||||
];
|
||||
}
|
||||
|
||||
function makeInfoOptions() {
|
||||
return {
|
||||
closeButtonCallback: makeCallback("closeButton"),
|
||||
targetCallback: makeCallback("target"),
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>UITour tests</h1>
|
||||
<p>Because Firefox is...</p>
|
||||
<p>Never gonna let you down</p>
|
||||
<p>Never gonna give you up</p>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue