mirror of
https://github.com/zen-browser/desktop.git
synced 2025-10-26 12:27:50 +00:00
Compare commits
13 Commits
window-syn
...
test/5880
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5058746de | ||
|
|
1d509e30a9 | ||
|
|
fbad844a08 | ||
|
|
f52f913043 | ||
|
|
346ef9c158 | ||
|
|
5038bd5cf7 | ||
|
|
b63cefa98b | ||
|
|
f8375a1155 | ||
|
|
34424a8f95 | ||
|
|
c883572de8 | ||
|
|
a53b4f9c94 | ||
|
|
d1bba0a8e2 | ||
|
|
d21e127bd7 |
@@ -17,6 +17,8 @@ engine/
|
||||
|
||||
surfer.json
|
||||
|
||||
src/zen/tests/mochitests/*
|
||||
|
||||
src/browser/app/profile/*.js
|
||||
pnpm-lock.yaml
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import zenGlobals from './src/zen/zen.globals.js';
|
||||
|
||||
export default defineConfig([
|
||||
@@ -23,4 +23,5 @@ export default defineConfig([
|
||||
},
|
||||
ignores: ['**/vendor/**', '**/tests/**'],
|
||||
},
|
||||
globalIgnores(['**/mochitests/**']),
|
||||
]);
|
||||
|
||||
@@ -38,7 +38,7 @@ zen-library-sidebar-workspaces =
|
||||
zen-library-sidebar-mods =
|
||||
.label = Mods
|
||||
zen-toggle-compact-mode-button =
|
||||
.label = Compact Mode
|
||||
.label = Kompakter Modus
|
||||
.tooltiptext = Compact Mode umschalten
|
||||
|
||||
# note: Do not translate the "<br/>" tags in the following string
|
||||
|
||||
@@ -179,6 +179,9 @@ category-zen-CKS =
|
||||
.tooltiptext = { pane-zen-CKS-title }
|
||||
pane-settings-CKS-title = { -brand-short-name } Keyboard Shortcuts
|
||||
|
||||
category-zen-marketplace =
|
||||
.tooltiptext = Zen Mods
|
||||
|
||||
zen-settings-CKS-header = Customize your keyboard shortcuts
|
||||
zen-settings-CKS-description = Change the default keyboard shortcuts to your liking and improve your browsing experience
|
||||
|
||||
|
||||
115
scripts/import_external_tests.py
Normal file
115
scripts/import_external_tests.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
import tomllib
|
||||
import shutil
|
||||
|
||||
BASE_PATH = os.path.join("src", "zen", "tests")
|
||||
EXTERNAL_TESTS_MANIFEST = os.path.join(BASE_PATH, "manifest.toml")
|
||||
EXTERNAL_TESTS_OUTPUT = os.path.join(BASE_PATH, "mochitests")
|
||||
|
||||
FILE_PREFIX = """
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# This file is autogenerated by scripts/import_external_tests.py
|
||||
# Do not edit manually.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"""
|
||||
|
||||
FILE_SUFFIX = "]"
|
||||
|
||||
def get_tests_manifest():
|
||||
with open(EXTERNAL_TESTS_MANIFEST, "rb") as f:
|
||||
return tomllib.load(f)
|
||||
|
||||
def die_with_error(message):
|
||||
print(f"ERROR: {message}")
|
||||
exit(1)
|
||||
|
||||
def validate_tests_path(path, files, ignore_list):
|
||||
for ignore in ignore_list:
|
||||
if ignore not in files:
|
||||
die_with_error(f"Ignore file '{ignore}' not found in tests folder '{path}'")
|
||||
if "browser.toml" not in files or "browser.js" in ignore_list:
|
||||
die_with_error(f"'browser.toml' not found in tests folder '{path}'")
|
||||
|
||||
def disable_and_replace_manifest(manifest, output_path):
|
||||
toml_file = os.path.join(output_path, "browser.toml")
|
||||
disabled_tests = manifest.get("disable", [])
|
||||
with open(toml_file, "r") as f:
|
||||
data = f.read()
|
||||
for test in disabled_tests:
|
||||
segment = f'["{test}"]'
|
||||
if segment not in data:
|
||||
die_with_error(f"Could not disable test '{test}' as it was not found in '{toml_file}'")
|
||||
replace_with = f'["{test}"]\ndisabled="Disabled by import_external_tests.py"'
|
||||
data = data.replace(segment, replace_with)
|
||||
for replacement in manifest.get("replace-manifest", {}).keys():
|
||||
if replacement not in data:
|
||||
die_with_error(f"Could not replace manifest entry '{replacement}' as it was not found in '{toml_file}'")
|
||||
data = data.replace(replacement, manifest["replace-manifest"][replacement])
|
||||
with open(toml_file, "w") as f:
|
||||
f.write(data)
|
||||
|
||||
def import_test_suite(test_suite, source_path, output_path, ignore_list, manifest, is_direct_path=False):
|
||||
print(f"Importing test suite '{test_suite}' from '{source_path}'")
|
||||
tests_folder = os.path.join("engine", source_path)
|
||||
if not is_direct_path:
|
||||
tests_folder = os.path.join(tests_folder, "tests")
|
||||
if not os.path.exists(tests_folder):
|
||||
die_with_error(f"Tests folder not found: {tests_folder}")
|
||||
files = os.listdir(tests_folder)
|
||||
validate_tests_path(tests_folder, files, ignore_list)
|
||||
if os.path.exists(output_path):
|
||||
shutil.rmtree(output_path)
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
for item in files:
|
||||
if item in ignore_list:
|
||||
continue
|
||||
s = os.path.join(tests_folder, item)
|
||||
d = os.path.join(output_path, item)
|
||||
if os.path.isdir(s):
|
||||
shutil.copytree(s, d)
|
||||
else:
|
||||
shutil.copy2(s, d)
|
||||
disable_and_replace_manifest(manifest[test_suite], output_path)
|
||||
|
||||
def write_moz_build_file(manifest):
|
||||
moz_build_path = os.path.join(EXTERNAL_TESTS_OUTPUT, "moz.build")
|
||||
print(f"Writing moz.build file to '{moz_build_path}'")
|
||||
with open(moz_build_path, "w") as f:
|
||||
f.write(FILE_PREFIX)
|
||||
for test_suite in manifest.keys():
|
||||
f.write(f'\t"{test_suite}/browser.toml",\n')
|
||||
f.write(FILE_SUFFIX)
|
||||
|
||||
def make_sure_ordered_tests(manifest):
|
||||
ordered_tests = sorted(manifest.keys())
|
||||
if list(manifest.keys()) != ordered_tests:
|
||||
die_with_error("Test suites in manifest.toml are not in alphabetical order.")
|
||||
|
||||
def main():
|
||||
manifest = get_tests_manifest()
|
||||
if os.path.exists(EXTERNAL_TESTS_OUTPUT):
|
||||
shutil.rmtree(EXTERNAL_TESTS_OUTPUT)
|
||||
os.makedirs(EXTERNAL_TESTS_OUTPUT, exist_ok=True)
|
||||
|
||||
make_sure_ordered_tests(manifest)
|
||||
for test_suite, config in manifest.items():
|
||||
import_test_suite(
|
||||
test_suite=test_suite,
|
||||
source_path=config["source"],
|
||||
output_path=os.path.join(EXTERNAL_TESTS_OUTPUT, test_suite),
|
||||
ignore_list=config.get("ignore", []),
|
||||
is_direct_path=config.get("is_direct_path", False),
|
||||
manifest=manifest
|
||||
)
|
||||
write_moz_build_file(manifest)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -15,6 +15,7 @@ IGNORE_PREFS_FILE_OUT = os.path.join(
|
||||
'engine', 'testing', 'mochitest', 'ignorePrefs.json'
|
||||
)
|
||||
|
||||
MOCHITEST_NAME = "mochitests"
|
||||
|
||||
class JSONWithCommentsDecoder(json.JSONDecoder):
|
||||
def __init__(self, **kw):
|
||||
@@ -68,7 +69,9 @@ def main():
|
||||
os.execvp(command[0], command)
|
||||
|
||||
if path in ("", "all"):
|
||||
test_dirs = [p for p in Path("zen/tests").iterdir() if p.is_dir()]
|
||||
test_dirs = [p for p in Path("zen/tests").iterdir() if p.is_dir() and p.name != MOCHITEST_NAME]
|
||||
mochitest_dirs = [p for p in Path(f"zen/tests/{MOCHITEST_NAME}").iterdir() if p.is_dir()]
|
||||
test_dirs.extend(mochitest_dirs)
|
||||
test_paths = [str(p) for p in test_dirs]
|
||||
run_mach_with_paths(test_paths)
|
||||
else:
|
||||
|
||||
@@ -58,4 +58,3 @@
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenEmojiPicker.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaceCreation.mjs"></script>
|
||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWindowSyncing.mjs"></script>
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
||||
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
||||
content/browser/zen-components/ZenWorkspaces.mjs (../../zen/workspaces/ZenWorkspaces.mjs)
|
||||
content/browser/zen-components/ZenWindowSyncing.mjs (../../zen/workspaces/ZenWindowSyncing.mjs)
|
||||
content/browser/zen-components/ZenWorkspaceCreation.mjs (../../zen/workspaces/ZenWorkspaceCreation.mjs)
|
||||
content/browser/zen-components/ZenWorkspacesStorage.mjs (../../zen/workspaces/ZenWorkspacesStorage.mjs)
|
||||
content/browser/zen-components/ZenWorkspacesSync.mjs (../../zen/workspaces/ZenWorkspacesSync.mjs)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
index d9a059f608779fea7cd8c595a432f6fe95183e0c..09a7c4045afd0b96027d0bbbad22e02e52fd7b22 100644
|
||||
index d9a059f608779fea7cd8c595a432f6fe95183e0c..31c43bc3d5b05713299c1b822b9774909445e862 100644
|
||||
--- a/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
+++ b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
@@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -158,7 +158,7 @@ index d9a059f608779fea7cd8c595a432f6fe95183e0c..09a7c4045afd0b96027d0bbbad22e02e
|
||||
continue;
|
||||
}
|
||||
- sum += parseFloat(style.marginLeft) + parseFloat(style.marginRight);
|
||||
+ sum += parseFloat(style.marginLeft) + Math.max(0, parseFloat(style.marginRight));
|
||||
+ sum += parseFloat(style.marginLeft) + (win.gZenVerticalTabsManager._hasSetSingleToolbar ? Math.max(0, parseFloat(style.marginRight)) : parseFloat(style.marginRight));
|
||||
if (child != aExceptChild) {
|
||||
sum += getInlineSize(child);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css
|
||||
index e06addf1602dc26ff4e75a8db6251231690f3f80..86e2cd0194bb37fa140a2f93eccfdd61419a9aec 100644
|
||||
index e06addf1602dc26ff4e75a8db6251231690f3f80..ffac005d5040852eda8f574f65f2eadf5ecbd642 100644
|
||||
--- a/browser/themes/shared/tabbrowser/content-area.css
|
||||
+++ b/browser/themes/shared/tabbrowser/content-area.css
|
||||
@@ -276,7 +276,7 @@
|
||||
@@ -134,7 +134,6 @@
|
||||
}
|
||||
|
||||
browser:is([blank], [pendingpaint]) {
|
||||
- opacity: 0;
|
||||
}
|
||||
|
||||
browser[type="content"] {
|
||||
@@ -276,7 +275,7 @@
|
||||
|
||||
.dialogStack {
|
||||
z-index: var(--browser-stack-z-index-dialog-stack);
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
const { exists: shouldExist = true } = descendantSelectors;
|
||||
if (exists === shouldExist) {
|
||||
if (!element.hasAttribute(stateAttribute)) {
|
||||
element.setAttribute(stateAttribute, 'true');
|
||||
gZenCompactModeManager._setElementExpandAttribute(element, true, stateAttribute);
|
||||
}
|
||||
} else {
|
||||
if (element.hasAttribute(stateAttribute)) {
|
||||
element.removeAttribute(stateAttribute);
|
||||
gZenCompactModeManager._setElementExpandAttribute(element, false, stateAttribute);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -143,6 +143,7 @@ var gZenCompactModeManager = {
|
||||
},
|
||||
|
||||
addHasPolyfillObserver() {
|
||||
const attributes = ['panelopen', 'open', 'breakout-extend', 'zen-floating-urlbar'];
|
||||
this.sidebarObserverId = ZenHasPolyfill.observeSelectorExistence(
|
||||
this.sidebar,
|
||||
[
|
||||
@@ -152,8 +153,21 @@ var gZenCompactModeManager = {
|
||||
},
|
||||
],
|
||||
'zen-compact-mode-active',
|
||||
['panelopen', 'open', 'breakout-extend', 'zen-floating-urlbar']
|
||||
attributes
|
||||
);
|
||||
this.toolbarObserverId = ZenHasPolyfill.observeSelectorExistence(
|
||||
document.getElementById('zen-appcontent-navbar-wrapper'),
|
||||
[
|
||||
{
|
||||
selector:
|
||||
":is([panelopen='true'], [open='true'], #urlbar:focus-within, [breakout-extend='true']):not(.zen-compact-mode-ignore)",
|
||||
},
|
||||
],
|
||||
'zen-compact-mode-active',
|
||||
attributes
|
||||
);
|
||||
// Always connect this observer, we need it even if compact mode is disabled
|
||||
ZenHasPolyfill.connectObserver(this.toolbarObserverId);
|
||||
},
|
||||
|
||||
flashSidebarIfNecessary(aInstant = false) {
|
||||
@@ -202,7 +216,7 @@ var gZenCompactModeManager = {
|
||||
},
|
||||
|
||||
updateCompactModeContext(isSingleToolbar) {
|
||||
isSingleToolbar ||= this.checkIfIllegalState();
|
||||
const isIllegalState = this.checkIfIllegalState();
|
||||
const menuitem = document.getElementById('zen-context-menu-compact-mode-toggle');
|
||||
const menu = document.getElementById('zen-context-menu-compact-mode');
|
||||
if (isSingleToolbar) {
|
||||
@@ -212,6 +226,14 @@ var gZenCompactModeManager = {
|
||||
menu.removeAttribute('hidden');
|
||||
menu.querySelector('menupopup').prepend(menuitem);
|
||||
}
|
||||
const hideToolbarMenuItem = document.getElementById(
|
||||
'zen-context-menu-compact-mode-hide-toolbar'
|
||||
);
|
||||
if (isIllegalState) {
|
||||
hideToolbarMenuItem.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
hideToolbarMenuItem.removeAttribute('disabled');
|
||||
}
|
||||
},
|
||||
|
||||
hideSidebar() {
|
||||
@@ -601,7 +623,7 @@ var gZenCompactModeManager = {
|
||||
},
|
||||
|
||||
_setElementExpandAttribute(element, value, attr = 'zen-has-hover') {
|
||||
const kVerifiedAttributes = ['zen-has-hover', 'has-popup-menu'];
|
||||
const kVerifiedAttributes = ['zen-has-hover', 'has-popup-menu', 'zen-compact-mode-active'];
|
||||
const isToolbar = element.id === 'zen-appcontent-navbar-wrapper';
|
||||
if (value) {
|
||||
element.setAttribute(attr, 'true');
|
||||
@@ -612,8 +634,7 @@ var gZenCompactModeManager = {
|
||||
document.documentElement.hasAttribute('zen-has-bookmarks'))) ||
|
||||
(this.preference &&
|
||||
Services.prefs.getBoolPref('zen.view.compact.hide-toolbar') &&
|
||||
!gZenVerticalTabsManager._hasSetSingleToolbar &&
|
||||
!gURLBar.hasAttribute('breakout-extend')))
|
||||
!gZenVerticalTabsManager._hasSetSingleToolbar))
|
||||
) {
|
||||
gBrowser.tabpanels.setAttribute('has-toolbar-hovered', 'true');
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
overflow: clip;
|
||||
|
||||
& #urlbar:not([breakout-extend='true']) {
|
||||
opacity: 0;
|
||||
/* Sometimes, "opacity: 1" is forced elsewhere */
|
||||
opacity: 0 !important;
|
||||
pointer-events: none;
|
||||
transition: opacity var(--zen-hidden-toolbar-transition);
|
||||
}
|
||||
@@ -48,20 +49,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
& #zen-appcontent-navbar-wrapper[zen-has-hover],
|
||||
& #zen-appcontent-navbar-wrapper[has-popup-menu],
|
||||
&
|
||||
#zen-appcontent-navbar-wrapper:has(
|
||||
*:is([panelopen='true'], [open='true'], #urlbar:focus-within, [breakout-extend='true']):not(.zen-compact-mode-ignore)
|
||||
) {
|
||||
|
||||
& #zen-appcontent-navbar-wrapper:is(
|
||||
[zen-has-hover],
|
||||
[has-popup-menu],
|
||||
[zen-compact-mode-active]
|
||||
) {
|
||||
height: var(--zen-toolbar-height-with-bookmarks);
|
||||
overflow: inherit;
|
||||
|
||||
%include windows-captions-fix-active.inc.css
|
||||
|
||||
& #urlbar {
|
||||
opacity: 1;
|
||||
opacity: 1 !important;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#duringOpening = false;
|
||||
#ignoreClose = false;
|
||||
|
||||
// Click handling
|
||||
#lastLinkClickData = { clientX: 0, clientY: 0, height: 0, width: 0 };
|
||||
|
||||
// Arc animation configuration
|
||||
#ARC_CONFIG = Object.freeze({
|
||||
ARC_STEPS: 70, // Increased for smoother bounce
|
||||
@@ -268,10 +271,31 @@
|
||||
data.height
|
||||
);
|
||||
return await this.#imageBitmapToBase64(
|
||||
await window.browsingContext.currentWindowGlobal.drawSnapshot(rect, 1, 'transparent', true)
|
||||
await window.browsingContext.currentWindowGlobal.drawSnapshot(
|
||||
rect,
|
||||
1,
|
||||
'transparent',
|
||||
undefined
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last link click data
|
||||
* @param {Object} data - The link click data
|
||||
*/
|
||||
set lastLinkClickData(data) {
|
||||
this.#lastLinkClickData = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last link click data
|
||||
* @returns {Object} The last link click data
|
||||
*/
|
||||
get lastLinkClickData() {
|
||||
return this.#lastLinkClickData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a glance overlay with the specified data
|
||||
* @param {Object} data - Glance data including URL, position, and dimensions
|
||||
@@ -289,6 +313,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.height || !data.width) {
|
||||
data = {
|
||||
...data,
|
||||
...this.lastLinkClickData,
|
||||
};
|
||||
}
|
||||
|
||||
this.#setAnimationState(true);
|
||||
const currentTab = ownerTab ?? gBrowser.selectedTab;
|
||||
const browserElement = this.#createBrowserElement(data.url, currentTab, existingTab);
|
||||
@@ -325,12 +356,13 @@
|
||||
gZenViewSplitter.onLocationChange(browserElement);
|
||||
this.#prepareGlanceAnimation(data, browserElement);
|
||||
if (data.width && data.height) {
|
||||
// It is guaranteed that we will animate this opacity later on
|
||||
// when we start animating the glance.
|
||||
this.contentWrapper.style.opacity = 0;
|
||||
data.elementData = await this.#getElementPreviewData(data);
|
||||
}
|
||||
this.#glances.get(this.#currentGlanceID).elementData = data.elementData;
|
||||
window.requestAnimationFrame(() => {
|
||||
this.#executeGlanceAnimation(data, browserElement, resolve);
|
||||
});
|
||||
this.#executeGlanceAnimation(data, browserElement, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -344,7 +376,6 @@
|
||||
const newButtons = this.#createNewOverlayButtons();
|
||||
this.browserWrapper.appendChild(newButtons);
|
||||
|
||||
this.#animateParentBackground();
|
||||
this.#setupGlancePositioning(data);
|
||||
this.#configureBrowserElement(browserElement);
|
||||
}
|
||||
@@ -488,7 +519,6 @@
|
||||
// nice fade-in effect to the content. But if it doesn't exist,
|
||||
// we just fall back to always showing the browser directly.
|
||||
if (data.elementData) {
|
||||
this.contentWrapper.style.opacity = 0;
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
this.contentWrapper,
|
||||
@@ -503,6 +533,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
this.#animateParentBackground();
|
||||
gZenUIManager.motion
|
||||
.animate(this.browserWrapper, arcSequence, {
|
||||
duration: gZenUIManager.testingEnabled ? 0 : 0.4,
|
||||
@@ -990,7 +1021,7 @@
|
||||
if (!onTabClose) {
|
||||
this.quickCloseGlance({ clearID: false });
|
||||
}
|
||||
this.browserWrapper.style.display = 'none';
|
||||
this.overlay.style.display = 'none';
|
||||
this.overlay.removeAttribute('fade-out');
|
||||
this.browserWrapper.removeAttribute('animate');
|
||||
|
||||
@@ -1357,18 +1388,9 @@
|
||||
* @param {Tab} tab - The tab to open glance for
|
||||
*/
|
||||
#openGlanceForTab(tab) {
|
||||
const browserRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabbox);
|
||||
const clickPosition = gZenUIManager._lastClickPosition || {
|
||||
clientX: browserRect.width / 2,
|
||||
clientY: browserRect.height / 2,
|
||||
};
|
||||
|
||||
this.openGlance(
|
||||
{
|
||||
url: undefined,
|
||||
...clickPosition,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
tab,
|
||||
tab.owner
|
||||
|
||||
@@ -35,22 +35,30 @@ export class ZenGlanceChild extends JSWindowActorChild {
|
||||
return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey);
|
||||
}
|
||||
|
||||
openGlance(target, originalTarget) {
|
||||
#openGlance(target) {
|
||||
let url = target.href;
|
||||
// Add domain to relative URLs
|
||||
if (!url.match(/^(?:[a-z]+:)?\/\//i)) {
|
||||
url = this.contentWindow.location.origin + url;
|
||||
}
|
||||
this.sendAsyncMessage('ZenGlance:OpenGlance', {
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
#sendClickDataToParent(target, element) {
|
||||
if (!element || !target) {
|
||||
return;
|
||||
}
|
||||
// Get the largest element we can get. If the `A` element
|
||||
// is a parent of the original target, use the anchor element,
|
||||
// otherwise use the original target.
|
||||
let rect = originalTarget.getBoundingClientRect();
|
||||
let rect = element.getBoundingClientRect();
|
||||
const anchorRect = target.getBoundingClientRect();
|
||||
if (anchorRect.width * anchorRect.height > rect.width * rect.height) {
|
||||
rect = anchorRect;
|
||||
}
|
||||
this.sendAsyncMessage('ZenGlance:OpenGlance', {
|
||||
url,
|
||||
this.sendAsyncMessage('ZenGlance:RecordLinkClickData', {
|
||||
clientX: rect.left,
|
||||
clientY: rect.top,
|
||||
width: rect.width,
|
||||
@@ -59,7 +67,19 @@ export class ZenGlanceChild extends JSWindowActorChild {
|
||||
}
|
||||
|
||||
handleClick(event) {
|
||||
if (this.ensureOnlyKeyModifiers(event) || event.button !== 0 || event.defaultPrevented) {
|
||||
if (event.button !== 0 || event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
// get closest A element
|
||||
const target = event.target.closest('A');
|
||||
const elementToRecord = event.originalTarget || event.target;
|
||||
// We record the link data anyway, even if the glance may be invoked
|
||||
// or not. We have some cases where glance would open, for example,
|
||||
// when clicking on a link with a different domain where glance would open.
|
||||
// The problem is that at that stage we don't know the rect or even what
|
||||
// element has been clicked, so we send the data here.
|
||||
this.#sendClickDataToParent(target, elementToRecord);
|
||||
if (this.ensureOnlyKeyModifiers(event)) {
|
||||
return;
|
||||
}
|
||||
const activationMethod = this.#activationMethod;
|
||||
@@ -72,13 +92,11 @@ export class ZenGlanceChild extends JSWindowActorChild {
|
||||
} else if (activationMethod === 'meta' && !event.metaKey) {
|
||||
return;
|
||||
}
|
||||
// get closest A element
|
||||
const target = event.target.closest('A');
|
||||
if (target) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.openGlance(target, event.originalTarget || event.target);
|
||||
this.#openGlance(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ export class ZenGlanceParent extends JSWindowActorParent {
|
||||
this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(params);
|
||||
break;
|
||||
}
|
||||
case 'ZenGlance:RecordLinkClickData': {
|
||||
this.browsingContext.topChromeWindow.gZenGlanceManager.lastLinkClickData = message.data;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.warn(`[glance]: Unknown message: ${message.name}`);
|
||||
}
|
||||
|
||||
@@ -171,10 +171,10 @@
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
border-radius: var(--zen-native-inner-radius);
|
||||
inset: 50%;
|
||||
translate: -50% -50%;
|
||||
top: 0%;
|
||||
left: 50%;
|
||||
translate: -50% 0%;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
const FILE_NAME = 'zen-sessions.jsonlz4';
|
||||
|
||||
export class nsZenSessionFile {
|
||||
#path;
|
||||
|
||||
#windows;
|
||||
|
||||
constructor() {
|
||||
this.#path = PathUtils.join(profileDir, FILE_NAME);
|
||||
}
|
||||
|
||||
async read() {
|
||||
try {
|
||||
return await IOUtils.readJSON(this.#path, { compress: true });
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async write(data) {
|
||||
await IOUtils.writeJSON(this.#path, data, { compress: true });
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import {
|
||||
cancelIdleCallback,
|
||||
clearTimeout,
|
||||
requestIdleCallback,
|
||||
setTimeout,
|
||||
} from 'resource://gre/modules/Timer.sys.mjs';
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
ZenSessionFile: 'resource://gre/modules/ZenSessionFile.sys.mjs',
|
||||
PrivateBrowsingUtils: 'resource://gre/modules/PrivateBrowsingUtils.sys.mjs',
|
||||
RunState: 'resource:///modules/sessionstore/RunState.sys.mjs',
|
||||
});
|
||||
|
||||
class nsZenSessionManager {
|
||||
#file;
|
||||
|
||||
constructor() {
|
||||
this.#file = null;
|
||||
}
|
||||
|
||||
get file() {
|
||||
if (!this.#file) {
|
||||
this.#file = lazy.ZenSessionFile;
|
||||
}
|
||||
return this.#file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current session state. Collects data and writes to disk.
|
||||
*
|
||||
* @param forceUpdateAllWindows (optional)
|
||||
* Forces us to recollect data for all windows and will bypass and
|
||||
* update the corresponding caches.
|
||||
*/
|
||||
saveState(forceUpdateAllWindows = false) {
|
||||
if (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
||||
// Don't save (or even collect) anything in permanent private
|
||||
// browsing mode
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ZenSessionStore = new nsZenSessionManager();
|
||||
@@ -1,35 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export class ZenSessionWindow {
|
||||
#id;
|
||||
#selectedWorkspace;
|
||||
#selectedTab;
|
||||
|
||||
constructor(id) {
|
||||
this.#id = id;
|
||||
this.#selectedWorkspace = null;
|
||||
this.#selectedTab = null;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
get selectedWorkspace() {
|
||||
return this.#selectedWorkspace;
|
||||
}
|
||||
|
||||
set selectedWorkspace(workspace) {
|
||||
this.#selectedWorkspace = workspace;
|
||||
}
|
||||
|
||||
get selectedTab() {
|
||||
return this.#selectedTab;
|
||||
}
|
||||
|
||||
set selectedTab(tab) {
|
||||
this.#selectedTab = tab;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,6 @@
|
||||
}
|
||||
|
||||
onTabIconChanged(tab, url = null) {
|
||||
tab.dispatchEvent(new CustomEvent('ZenTabIconChanged', { bubbles: true, detail: { tab } }));
|
||||
const iconUrl = url ?? tab.iconImage.src;
|
||||
if (!iconUrl && tab.hasAttribute('zen-pin-id')) {
|
||||
try {
|
||||
@@ -1556,7 +1555,6 @@
|
||||
}
|
||||
|
||||
async onTabLabelChanged(tab) {
|
||||
tab.dispatchEvent(new CustomEvent('ZenTabLabelChanged', { detail: { tab } }));
|
||||
if (!this._pinsCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@ z-index: 1;
|
||||
|
||||
%include ../../compact-mode/windows-captions-fix-active.inc.css
|
||||
|
||||
&:not([zen-has-hover='true']):not([has-popup-menu]):not(:focus-within):not(
|
||||
:has(*:is([panelopen='true'], [open='true']))
|
||||
) {
|
||||
&:not([zen-has-hover='true']):not([has-popup-menu]):not([zen-compact-mode-active]) {
|
||||
height: var(--zen-element-separation);
|
||||
opacity: 0;
|
||||
& #zen-appcontent-navbar-container {
|
||||
|
||||
@@ -963,7 +963,7 @@
|
||||
:root[zen-single-toolbar='true'] & {
|
||||
--zen-toolbar-height: 36px;
|
||||
@media (-moz-platform: macos) {
|
||||
--zen-toolbar-height: 42px;
|
||||
--zen-toolbar-height: 38px;
|
||||
}
|
||||
|
||||
& #PanelUI-button {
|
||||
|
||||
29
src/zen/tests/manifest.toml
Normal file
29
src/zen/tests/manifest.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
[reportbrokensite]
|
||||
source = "browser/components/reportbrokensite/test/browser"
|
||||
is_direct_path = true
|
||||
disable = [
|
||||
"browser_addon_data_sent.js"
|
||||
]
|
||||
|
||||
[reportbrokensite.replace-manifest]
|
||||
"../../../../../" = "../../../../"
|
||||
|
||||
[safebrowsing]
|
||||
source = "browser/components/safebrowsing/content/test"
|
||||
is_direct_path = true
|
||||
|
||||
[shell]
|
||||
source = "browser/components/shell/test"
|
||||
is_direct_path = true
|
||||
|
||||
[tooltiptext]
|
||||
source = "toolkit/components/tooltiptext"
|
||||
|
||||
[translations]
|
||||
source = "browser/components/translations/tests/browser"
|
||||
is_direct_path = true
|
||||
15
src/zen/tests/mochitests/moz.build
Normal file
15
src/zen/tests/mochitests/moz.build
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# This file is autogenerated by scripts/import_external_tests.py
|
||||
# Do not edit manually.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"reportbrokensite/browser.toml",
|
||||
"safebrowsing/browser.toml",
|
||||
"shell/browser.toml",
|
||||
"tooltiptext/browser.toml",
|
||||
"translations/browser.toml",
|
||||
]
|
||||
50
src/zen/tests/mochitests/reportbrokensite/browser.toml
Normal file
50
src/zen/tests/mochitests/reportbrokensite/browser.toml
Normal file
@@ -0,0 +1,50 @@
|
||||
[DEFAULT]
|
||||
tags = "report-broken-site"
|
||||
support-files = [
|
||||
"example_report_page.html",
|
||||
"head.js",
|
||||
"sendMoreInfoTestEndpoint.html",
|
||||
]
|
||||
|
||||
["browser_addon_data_sent.js"]
|
||||
disabled="Disabled by import_external_tests.py"
|
||||
support-files = [ "send_more_info.js" ]
|
||||
skip-if = ["os == 'win' && os_version == '11.26100' && processor == 'x86_64' && opt"] # Bug 1955805
|
||||
|
||||
["browser_antitracking_data_sent.js"]
|
||||
support-files = [ "send_more_info.js" ]
|
||||
|
||||
["browser_back_buttons.js"]
|
||||
|
||||
["browser_error_messages.js"]
|
||||
|
||||
["browser_experiment_data_sent.js"]
|
||||
support-files = [ "send_more_info.js" ]
|
||||
|
||||
["browser_keyboard_navigation.js"]
|
||||
skip-if = ["os == 'linux' && os_version == '24.04' && processor == 'x86_64' && tsan"] # Bug 1867132
|
||||
|
||||
["browser_parent_menuitems.js"]
|
||||
|
||||
["browser_prefers_contrast.js"]
|
||||
|
||||
["browser_reason_dropdown.js"]
|
||||
|
||||
["browser_report_send.js"]
|
||||
support-files = [ "send.js" ]
|
||||
|
||||
["browser_send_more_info.js"]
|
||||
support-files = [
|
||||
"send_more_info.js",
|
||||
"../../../../toolkit/components/gfx/content/videotest.mp4",
|
||||
]
|
||||
|
||||
["browser_tab_key_order.js"]
|
||||
|
||||
["browser_tab_switch_handling.js"]
|
||||
|
||||
["browser_webcompat.com_fallback.js"]
|
||||
support-files = [
|
||||
"send_more_info.js",
|
||||
"../../../../toolkit/components/gfx/content/videotest.mp4",
|
||||
]
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the right data is sent for
|
||||
* private windows and when ETP blocks content.
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { AddonTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/AddonTestUtils.sys.mjs"
|
||||
);
|
||||
AddonTestUtils.initMochitest(this);
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const TEMP_ID = "testtempaddon@tests.mozilla.org";
|
||||
const TEMP_NAME = "Temporary Addon";
|
||||
const TEMP_VERSION = "0.1.0";
|
||||
|
||||
const PERM_ID = "testpermaddon@tests.mozilla.org";
|
||||
const PERM_NAME = "Permanent Addon";
|
||||
const PERM_VERSION = "0.2.0";
|
||||
|
||||
const DISABLED_ID = "testdisabledaddon@tests.mozilla.org";
|
||||
const DISABLED_NAME = "Disabled Addon";
|
||||
const DISABLED_VERSION = "0.3.0";
|
||||
|
||||
const EXPECTED_ADDONS = [
|
||||
{ id: PERM_ID, name: PERM_NAME, temporary: false, version: PERM_VERSION },
|
||||
{ id: TEMP_ID, name: TEMP_NAME, temporary: true, version: TEMP_VERSION },
|
||||
];
|
||||
|
||||
function loadAddon(id, name, version, isTemp = false) {
|
||||
return ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
browser_specific_settings: { gecko: { id } },
|
||||
name,
|
||||
version,
|
||||
},
|
||||
useAddonManager: isTemp ? "temporary" : "permanent",
|
||||
});
|
||||
}
|
||||
|
||||
async function installAddons() {
|
||||
const temp = await loadAddon(TEMP_ID, TEMP_NAME, TEMP_VERSION, true);
|
||||
await temp.startup();
|
||||
|
||||
const perm = await loadAddon(PERM_ID, PERM_NAME, PERM_VERSION);
|
||||
await perm.startup();
|
||||
|
||||
const dis = await loadAddon(DISABLED_ID, DISABLED_NAME, DISABLED_VERSION);
|
||||
await dis.startup();
|
||||
await (await AddonManager.getAddonByID(DISABLED_ID)).disable();
|
||||
|
||||
return async () => {
|
||||
await temp.unload();
|
||||
await perm.unload();
|
||||
await dis.unload();
|
||||
};
|
||||
}
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
const addonCleanup = await installAddons();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab, AppMenu(), {
|
||||
addons: EXPECTED_ADDONS,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
await addonCleanup();
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
const addonCleanup = await installAddons();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSendMoreInfo(tab, HelpMenu(), {
|
||||
addons: EXPECTED_ADDONS,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
await addonCleanup();
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the right data is sent for
|
||||
* private windows and when ETP blocks content.
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(setupStrictETP);
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
|
||||
const blockedPromise = waitForContentBlockingEvent(3, win);
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL3, win);
|
||||
await blockedPromise;
|
||||
|
||||
await testSend(tab, AppMenu(win), {
|
||||
breakageCategory: "adblocker",
|
||||
description: "another test description",
|
||||
antitracking: {
|
||||
blockList: "strict",
|
||||
isPrivateBrowsing: true,
|
||||
hasTrackingContentBlocked: true,
|
||||
hasMixedActiveContentBlocked: true,
|
||||
hasMixedDisplayContentBlocked: true,
|
||||
btpHasPurgedSite: false,
|
||||
etpCategory: "strict",
|
||||
},
|
||||
frameworks: {
|
||||
fastclick: true,
|
||||
marfeel: true,
|
||||
mobify: true,
|
||||
},
|
||||
});
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
|
||||
const blockedPromise = waitForContentBlockingEvent(3, win);
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL3, win);
|
||||
await blockedPromise;
|
||||
|
||||
await testSendMoreInfo(tab, HelpMenu(win), {
|
||||
antitracking: {
|
||||
blockList: "strict",
|
||||
isPrivateBrowsing: true,
|
||||
hasTrackingContentBlocked: true,
|
||||
hasMixedActiveContentBlocked: true,
|
||||
hasMixedDisplayContentBlocked: true,
|
||||
btpHasPurgedSite: false,
|
||||
etpCategory: "strict",
|
||||
},
|
||||
frameworks: { fastclick: true, mobify: true, marfeel: true },
|
||||
consoleLog: [
|
||||
{
|
||||
level: "error",
|
||||
log(actual) {
|
||||
// "Blocked loading mixed display content http://example.com/tests/image/test/mochitest/blue.png"
|
||||
return (
|
||||
Array.isArray(actual) &&
|
||||
actual.length == 1 &&
|
||||
actual[0].includes("blue.png")
|
||||
);
|
||||
},
|
||||
pos: "0:1",
|
||||
uri: REPORTABLE_PAGE_URL3,
|
||||
},
|
||||
{
|
||||
level: "error",
|
||||
log(actual) {
|
||||
// "Blocked loading mixed active content http://tracking.example.org/browser/browser/base/content/test/protectionsUI/benignPage.html",
|
||||
return (
|
||||
Array.isArray(actual) &&
|
||||
actual.length == 1 &&
|
||||
actual[0].includes("benignPage.html")
|
||||
);
|
||||
},
|
||||
pos: "0:1",
|
||||
uri: REPORTABLE_PAGE_URL3,
|
||||
},
|
||||
{
|
||||
level: "warn",
|
||||
log(actual) {
|
||||
// "The resource at https://trackertest.org/ was blocked because content blocking is enabled.",
|
||||
return (
|
||||
Array.isArray(actual) &&
|
||||
actual.length == 1 &&
|
||||
actual[0].includes("trackertest.org")
|
||||
);
|
||||
},
|
||||
pos: "0:1",
|
||||
uri: REPORTABLE_PAGE_URL3,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that Report Broken Site popups will be
|
||||
* reset to whichever tab the user is on as they change
|
||||
* between windows and tabs. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testBackButtonsAreAdded() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
rbs.isBackButtonEnabled();
|
||||
await rbs.clickBack();
|
||||
await rbs.close();
|
||||
|
||||
rbs = await HelpMenu().openReportBrokenSite();
|
||||
ok(!rbs.backButton, "Back button is not shown for Help Menu");
|
||||
await rbs.close();
|
||||
|
||||
rbs = await ProtectionsPanel().openReportBrokenSite();
|
||||
rbs.isBackButtonEnabled();
|
||||
await rbs.clickBack();
|
||||
await rbs.close();
|
||||
|
||||
rbs = await HelpMenu().openReportBrokenSite();
|
||||
ok(!rbs.backButton, "Back button is not shown for Help Menu");
|
||||
await rbs.close();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Test that the Report Broken Site errors messages are shown on
|
||||
* the UI if the user enters an invalid URL or clicks the send
|
||||
* button while it is disabled due to not selecting a "reason"
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function test() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonRequired();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
for (const menu of [AppMenu(), ProtectionsPanel(), HelpMenu()]) {
|
||||
const rbs = await menu.openReportBrokenSite();
|
||||
const { sendButton, URLInput } = rbs;
|
||||
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("");
|
||||
window.document.activeElement.blur();
|
||||
rbs.isURLInvalidMessageShown();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("https://asdf");
|
||||
window.document.activeElement.blur();
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("http:/ /asdf");
|
||||
window.document.activeElement.blur();
|
||||
rbs.isURLInvalidMessageShown();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("https://asdf");
|
||||
const selectPromise = BrowserTestUtils.waitForSelectPopupShown(window);
|
||||
EventUtils.synthesizeMouseAtCenter(sendButton, {}, window);
|
||||
await selectPromise;
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageShown();
|
||||
await rbs.dismissDropdownPopup();
|
||||
|
||||
rbs.chooseReason("slow");
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("");
|
||||
rbs.chooseReason("choose");
|
||||
window.ownerGlobal.document.activeElement?.blur();
|
||||
const focusPromise = BrowserTestUtils.waitForEvent(URLInput, "focus");
|
||||
EventUtils.synthesizeMouseAtCenter(sendButton, {}, window);
|
||||
await focusPromise;
|
||||
rbs.isURLInvalidMessageShown();
|
||||
rbs.isReasonNeededMessageShown();
|
||||
|
||||
rbs.clickCancel();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the right data is sent for
|
||||
* private windows and when ETP blocks content.
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
const { ExperimentAPI } = ChromeUtils.importESModule(
|
||||
"resource://nimbus/ExperimentAPI.sys.mjs"
|
||||
);
|
||||
const { NimbusTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/NimbusTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const EXPECTED_EXPERIMENTS_IN_REPORT = [
|
||||
{ slug: "test-experiment", branch: "branch", kind: "nimbusExperiment" },
|
||||
{ slug: "test-experiment-rollout", branch: "branch", kind: "nimbusRollout" },
|
||||
];
|
||||
|
||||
let EXPERIMENT_CLEANUPS;
|
||||
|
||||
add_setup(async function () {
|
||||
await ExperimentAPI.ready();
|
||||
EXPERIMENT_CLEANUPS = [
|
||||
await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{ featureId: "no-feature-firefox-desktop", value: {} },
|
||||
{ slug: "test-experiment", branchSlug: "branch" }
|
||||
),
|
||||
await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{ featureId: "no-feature-firefox-desktop", value: {} },
|
||||
{ slug: "test-experiment-rollout", isRollout: true, branchSlug: "branch" }
|
||||
),
|
||||
async () => {
|
||||
ExperimentAPI.manager.store._deleteForTests("test-experiment-disabled");
|
||||
await NimbusTestUtils.flushStore();
|
||||
},
|
||||
];
|
||||
|
||||
await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{ featureId: "no-feature-firefox-desktop", value: {} },
|
||||
{ slug: "test-experiment-disabled" }
|
||||
);
|
||||
await ExperimentAPI.manager.unenroll("test-experiment-disabled");
|
||||
});
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab, AppMenu(), {
|
||||
experiments: EXPECTED_EXPERIMENTS_IN_REPORT,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSendMoreInfo(tab, HelpMenu(), {
|
||||
experiments: EXPECTED_EXPERIMENTS_IN_REPORT,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function teardown() {
|
||||
for (const cleanup of EXPERIMENT_CLEANUPS) {
|
||||
await cleanup();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that sending or canceling reports with
|
||||
* the Send and Cancel buttons work (as well as the Okay button)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
async function testPressingKey(key, tabToMatch, makePromise, followUp) {
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
for (const menu of [AppMenu(), ProtectionsPanel(), HelpMenu()]) {
|
||||
info(
|
||||
`Opening RBS to test pressing ${key} for ${tabToMatch} on ${menu.menuDescription}`
|
||||
);
|
||||
const rbs = await menu.openReportBrokenSite();
|
||||
const promise = makePromise(rbs);
|
||||
if (tabToMatch) {
|
||||
if (await tabTo(tabToMatch)) {
|
||||
await pressKeyAndAwait(promise, key);
|
||||
followUp && (await followUp(rbs));
|
||||
await rbs.close();
|
||||
ok(true, `was able to activate ${tabToMatch} with keyboard`);
|
||||
} else {
|
||||
await rbs.close();
|
||||
ok(false, `could not tab to ${tabToMatch}`);
|
||||
}
|
||||
} else {
|
||||
await pressKeyAndAwait(promise, key);
|
||||
followUp && (await followUp(rbs));
|
||||
await rbs.close();
|
||||
ok(true, `was able to use keyboard`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function testSendMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-send-more-info-link",
|
||||
rbs => rbs.waitForSendMoreInfoTab(),
|
||||
() => gBrowser.removeCurrentTab()
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testCancel() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-cancel-button",
|
||||
rbs => BrowserTestUtils.waitForEvent(rbs.mainView, "ViewHiding")
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testSendAndOkay() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-send-button",
|
||||
rbs => rbs.awaitReportSentViewOpened(),
|
||||
async rbs => {
|
||||
await tabTo("#report-broken-site-popup-okay-button");
|
||||
const promise = BrowserTestUtils.waitForEvent(rbs.sentView, "ViewHiding");
|
||||
await pressKeyAndAwait(promise, "KEY_Enter");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testESCOnMain() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey("KEY_Escape", undefined, rbs =>
|
||||
BrowserTestUtils.waitForEvent(rbs.mainView, "ViewHiding")
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testESCOnSent() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-send-button",
|
||||
rbs => rbs.awaitReportSentViewOpened(),
|
||||
async rbs => {
|
||||
const promise = BrowserTestUtils.waitForEvent(rbs.sentView, "ViewHiding");
|
||||
await pressKeyAndAwait(promise, "KEY_Escape");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testBackButtons() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
for (const menu of [AppMenu(), ProtectionsPanel()]) {
|
||||
await menu.openReportBrokenSite();
|
||||
await tabTo("#report-broken-site-popup-mainView .subviewbutton-back");
|
||||
const promise = BrowserTestUtils.waitForEvent(menu.popup, "ViewShown");
|
||||
await pressKeyAndAwait(promise, "KEY_Enter");
|
||||
menu.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Test that the Report Broken Site menu items are disabled
|
||||
* when the active tab is not on a reportable URL, and is hidden
|
||||
* when the feature is disabled via pref. Also ensure that the
|
||||
* Report Broken Site item that is automatically generated in
|
||||
* the app menu's help sub-menu is hidden.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testMenus() {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
const appMenu = AppMenu();
|
||||
const menus = [appMenu, ProtectionsPanel(), HelpMenu()];
|
||||
|
||||
async function forceMenuItemStateUpdate() {
|
||||
ReportBrokenSite.enableOrDisableMenuitems(window);
|
||||
|
||||
// the hidden/disabled state of all of the menuitems may not update until one
|
||||
// is rendered; then the related <command>'s state is propagated to them all.
|
||||
await appMenu.open();
|
||||
await appMenu.close();
|
||||
}
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:blank", async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden on invalid page when preffed off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden on valid page when preffed off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:blank", async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemDisabled(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option disabled on invalid page when preffed on`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemEnabled(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option enabled on valid page when preffed on`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden again when pref toggled back off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReportBrokenSiteDisabledByPolicy();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden when disabled by DisableFeedbackCommands enterprise policy`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Test that the background color of the "report sent"
|
||||
* view is not green in non-default contrast modes.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const HIGH_CONTRAST_MODE_OFF = [[PREFS.USE_ACCESSIBILITY_THEME, 0]];
|
||||
|
||||
const HIGH_CONTRAST_MODE_ON = [[PREFS.USE_ACCESSIBILITY_THEME, 1]];
|
||||
|
||||
add_task(async function testReportSentViewBGColor() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonDisabled();
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
REPORTABLE_PAGE_URL,
|
||||
async function (browser) {
|
||||
const { defaultView } = browser.ownerGlobal.document;
|
||||
|
||||
const menu = AppMenu();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({ set: HIGH_CONTRAST_MODE_OFF });
|
||||
const rbs = await menu.openReportBrokenSite();
|
||||
const { mainView, sentView } = rbs;
|
||||
mainView.style.backgroundColor = "var(--background-color-success)";
|
||||
const expectedReportSentBGColor =
|
||||
defaultView.getComputedStyle(mainView).backgroundColor;
|
||||
mainView.style.backgroundColor = "";
|
||||
const expectedPrefersReducedBGColor =
|
||||
defaultView.getComputedStyle(mainView).backgroundColor;
|
||||
|
||||
await rbs.clickSend();
|
||||
is(
|
||||
defaultView.getComputedStyle(sentView).backgroundColor,
|
||||
expectedReportSentBGColor,
|
||||
"Using green bgcolor when not prefers-contrast"
|
||||
);
|
||||
await rbs.clickOkay();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({ set: HIGH_CONTRAST_MODE_ON });
|
||||
await menu.openReportBrokenSite();
|
||||
await rbs.clickSend();
|
||||
is(
|
||||
defaultView.getComputedStyle(sentView).backgroundColor,
|
||||
expectedPrefersReducedBGColor,
|
||||
"Using default bgcolor when prefers-contrast"
|
||||
);
|
||||
await rbs.clickOkay();
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,156 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the reason dropdown is shown or hidden
|
||||
* based on its pref, and that its optional and required modes affect
|
||||
* the Send button and report appropriately.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
async function clickSendAndCheckPing(rbs, expectedReason = null) {
|
||||
await GleanPings.brokenSiteReport.testSubmission(
|
||||
() =>
|
||||
Assert.equal(
|
||||
Glean.brokenSiteReport.breakageCategory.testGetValue(),
|
||||
expectedReason
|
||||
),
|
||||
() => rbs.clickSend()
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function testReasonDropdown() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
ensureReasonDisabled();
|
||||
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonHidden();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs);
|
||||
await rbs.clickOkay();
|
||||
|
||||
ensureReasonOptional();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonOptional();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs);
|
||||
await rbs.clickOkay();
|
||||
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonOptional();
|
||||
rbs.chooseReason("slow");
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs, "slow");
|
||||
await rbs.clickOkay();
|
||||
|
||||
ensureReasonRequired();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonRequired();
|
||||
await rbs.isSendButtonEnabled();
|
||||
const selectPromise = BrowserTestUtils.waitForSelectPopupShown(window);
|
||||
EventUtils.synthesizeMouseAtCenter(rbs.sendButton, {}, window);
|
||||
await selectPromise;
|
||||
rbs.chooseReason("media");
|
||||
await rbs.dismissDropdownPopup();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs, "media");
|
||||
await rbs.clickOkay();
|
||||
});
|
||||
});
|
||||
|
||||
async function getListItems(rbs) {
|
||||
const items = Array.from(rbs.reasonInput.querySelectorAll("option")).map(i =>
|
||||
i.id.replace("report-broken-site-popup-reason-", "")
|
||||
);
|
||||
Assert.equal(items[0], "choose", "First option is always 'choose'");
|
||||
return items.join(",");
|
||||
}
|
||||
|
||||
add_task(async function testReasonDropdownRandomized() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const USER_ID_PREF = "app.normandy.user_id";
|
||||
const RANDOMIZE_PREF = "ui.new-webcompat-reporter.reason-dropdown.randomized";
|
||||
|
||||
const origNormandyUserID = Services.prefs.getCharPref(
|
||||
USER_ID_PREF,
|
||||
undefined
|
||||
);
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
// confirm that the default order is initially used
|
||||
Services.prefs.setBoolPref(RANDOMIZE_PREF, false);
|
||||
const rbs = await AppMenu().openReportBrokenSite();
|
||||
const defaultOrder = [
|
||||
"choose",
|
||||
"checkout",
|
||||
"load",
|
||||
"slow",
|
||||
"media",
|
||||
"content",
|
||||
"account",
|
||||
"adblocker",
|
||||
"notsupported",
|
||||
"other",
|
||||
];
|
||||
Assert.deepEqual(
|
||||
await getListItems(rbs),
|
||||
defaultOrder,
|
||||
"non-random order is correct"
|
||||
);
|
||||
|
||||
// confirm that a random order happens per user
|
||||
let randomOrder;
|
||||
let isRandomized = false;
|
||||
Services.prefs.setBoolPref(RANDOMIZE_PREF, true);
|
||||
|
||||
// This becomes ClientEnvironment.randomizationId, which we can set to
|
||||
// any value which results in a different order from the default ordering.
|
||||
Services.prefs.setCharPref("app.normandy.user_id", "dummy");
|
||||
|
||||
// clicking cancel triggers a reset, which is when the randomization
|
||||
// logic is called. so we must click cancel after pref-changes here.
|
||||
rbs.clickCancel();
|
||||
await AppMenu().openReportBrokenSite();
|
||||
randomOrder = await getListItems(rbs);
|
||||
Assert.notEqual(
|
||||
randomOrder,
|
||||
defaultOrder,
|
||||
"options are randomized with pref on"
|
||||
);
|
||||
|
||||
// confirm that the order doesn't change per user
|
||||
isRandomized = false;
|
||||
for (let attempt = 0; attempt < 5; ++attempt) {
|
||||
rbs.clickCancel();
|
||||
await AppMenu().openReportBrokenSite();
|
||||
const order = await getListItems(rbs);
|
||||
|
||||
if (order != randomOrder) {
|
||||
isRandomized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.ok(!isRandomized, "options keep the same order per user");
|
||||
|
||||
// confirm that the order reverts to the default if pref flipped to false
|
||||
Services.prefs.setBoolPref(RANDOMIZE_PREF, false);
|
||||
rbs.clickCancel();
|
||||
await AppMenu().openReportBrokenSite();
|
||||
Assert.deepEqual(
|
||||
defaultOrder,
|
||||
await getListItems(rbs),
|
||||
"reverts to non-random order correctly"
|
||||
);
|
||||
rbs.clickCancel();
|
||||
});
|
||||
|
||||
Services.prefs.setCharPref(USER_ID_PREF, origNormandyUserID);
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that sending or canceling reports with
|
||||
* the Send and Cancel buttons work (as well as the Okay button)
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(10);
|
||||
|
||||
async function testCancel(menu, url, description) {
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
await rbs.clickCancel();
|
||||
ok(!rbs.opened, "clicking Cancel closes Report Broken Site");
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab1, AppMenu());
|
||||
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab2, ProtectionsPanel(), {
|
||||
url: "https://test.org/test/#fake",
|
||||
breakageCategory: "media",
|
||||
description: "test description",
|
||||
});
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
});
|
||||
|
||||
add_task(async function testCancelButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testCancel(AppMenu());
|
||||
await testCancel(ProtectionsPanel());
|
||||
await testCancel(HelpMenu());
|
||||
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testCancel(AppMenu());
|
||||
await testCancel(ProtectionsPanel());
|
||||
await testCancel(HelpMenu());
|
||||
|
||||
const win2 = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const tab3 = await openTab(REPORTABLE_PAGE_URL2, win2);
|
||||
|
||||
await testCancel(AppMenu(win2));
|
||||
await testCancel(ProtectionsPanel(win2));
|
||||
await testCancel(HelpMenu(win2));
|
||||
|
||||
closeTab(tab3);
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests that the send more info link appears only when its pref
|
||||
* is set to true, and that when clicked it will open a tab to
|
||||
* the webcompat.com endpoint and send the right data.
|
||||
*/
|
||||
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const VIDEO_URL = `${BASE_URL}/videotest.mp4`;
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(async function testSendMoreInfoPref() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await changeTab(gBrowser.selectedTab, REPORTABLE_PAGE_URL);
|
||||
|
||||
ensureSendMoreInfoDisabled();
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isSendMoreInfoHidden();
|
||||
await rbs.close();
|
||||
|
||||
ensureSendMoreInfoEnabled();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isSendMoreInfoShown();
|
||||
await rbs.close();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSendMoreInfo(tab, AppMenu());
|
||||
|
||||
await changeTab(tab, REPORTABLE_PAGE_URL2);
|
||||
|
||||
await testSendMoreInfo(tab, ProtectionsPanel(), {
|
||||
url: "https://override.com",
|
||||
description: "another",
|
||||
expectNoTabDetails: true,
|
||||
});
|
||||
|
||||
// also load a video to ensure system codec
|
||||
// information is loaded and properly sent
|
||||
const tab2 = await openTab(VIDEO_URL);
|
||||
await testSendMoreInfo(tab2, HelpMenu());
|
||||
closeTab(tab2);
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests of the expected tab key element focus order */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
async function ensureTabOrder(order, win = window) {
|
||||
const config = { window: win };
|
||||
for (let matches of order) {
|
||||
// We need to tab through all elements in each match array in any order
|
||||
if (!Array.isArray(matches)) {
|
||||
matches = [matches];
|
||||
}
|
||||
let matchesLeft = matches.length;
|
||||
while (matchesLeft--) {
|
||||
const target = await pressKeyAndGetFocus("VK_TAB", config);
|
||||
let foundMatch = false;
|
||||
for (const [i, selector] of matches.entries()) {
|
||||
foundMatch = selector && target.matches(selector);
|
||||
if (foundMatch) {
|
||||
matches[i] = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok(
|
||||
foundMatch,
|
||||
`Expected [${matches}] next, got id=${target.id}, class=${target.className}, ${target}`
|
||||
);
|
||||
if (!foundMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function ensureExpectedTabOrder(
|
||||
expectBackButton,
|
||||
expectReason,
|
||||
expectSendMoreInfo
|
||||
) {
|
||||
const { activeElement } = window.document;
|
||||
is(
|
||||
activeElement?.id,
|
||||
"report-broken-site-popup-url",
|
||||
"URL is already focused"
|
||||
);
|
||||
const order = [];
|
||||
if (expectReason) {
|
||||
order.push("#report-broken-site-popup-reason");
|
||||
}
|
||||
order.push("#report-broken-site-popup-description");
|
||||
if (expectSendMoreInfo) {
|
||||
order.push("#report-broken-site-popup-send-more-info-link");
|
||||
}
|
||||
// moz-button-groups swap the order of buttons to follow
|
||||
// platform conventions, so the order of send/cancel will vary.
|
||||
order.push([
|
||||
"#report-broken-site-popup-cancel-button",
|
||||
"#report-broken-site-popup-send-button",
|
||||
]);
|
||||
if (expectBackButton) {
|
||||
order.push(".subviewbutton-back");
|
||||
}
|
||||
order.push("#report-broken-site-popup-url"); // check that we've cycled back
|
||||
return ensureTabOrder(order);
|
||||
}
|
||||
|
||||
async function testTabOrder(menu) {
|
||||
ensureReasonDisabled();
|
||||
ensureSendMoreInfoDisabled();
|
||||
|
||||
const { showsBackButton } = menu;
|
||||
|
||||
let rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, false, false);
|
||||
await rbs.close();
|
||||
|
||||
ensureSendMoreInfoEnabled();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, false, true);
|
||||
await rbs.close();
|
||||
|
||||
ensureReasonOptional();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, true);
|
||||
await rbs.close();
|
||||
|
||||
ensureReasonRequired();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, true);
|
||||
await rbs.close();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.chooseReason("slow");
|
||||
await ensureExpectedTabOrder(showsBackButton, true, true);
|
||||
await rbs.clickCancel();
|
||||
|
||||
ensureSendMoreInfoDisabled();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, false);
|
||||
await rbs.close();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.chooseReason("slow");
|
||||
await ensureExpectedTabOrder(showsBackButton, true, false);
|
||||
await rbs.clickCancel();
|
||||
|
||||
ensureReasonOptional();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, false);
|
||||
await rbs.close();
|
||||
|
||||
ensureReasonDisabled();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, false, false);
|
||||
await rbs.close();
|
||||
}
|
||||
|
||||
add_task(async function testTabOrdering() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await testTabOrder(AppMenu());
|
||||
await testTabOrder(ProtectionsPanel());
|
||||
await testTabOrder(HelpMenu());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that Report Broken Site popups will be
|
||||
* reset to whichever tab the user is on as they change
|
||||
* between windows and tabs. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testResetsProperlyOnTabSwitch() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const badTab = await openTab("about:blank");
|
||||
const goodTab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
const goodTab2 = await openTab(REPORTABLE_PAGE_URL2);
|
||||
|
||||
const appMenu = AppMenu();
|
||||
const protPanel = ProtectionsPanel();
|
||||
|
||||
let rbs = await appMenu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
gBrowser.selectedTab = goodTab1;
|
||||
|
||||
rbs = await protPanel.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
gBrowser.selectedTab = badTab;
|
||||
await appMenu.open();
|
||||
appMenu.isReportBrokenSiteDisabled();
|
||||
await appMenu.close();
|
||||
|
||||
gBrowser.selectedTab = goodTab1;
|
||||
rbs = await protPanel.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
closeTab(badTab);
|
||||
closeTab(goodTab1);
|
||||
closeTab(goodTab2);
|
||||
});
|
||||
|
||||
add_task(async function testResetsProperlyOnWindowSwitch() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
const win2 = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL2, win2);
|
||||
|
||||
const appMenu1 = AppMenu();
|
||||
const appMenu2 = ProtectionsPanel(win2);
|
||||
|
||||
let rbs2 = await appMenu2.openReportBrokenSite();
|
||||
rbs2.isMainViewResetToCurrentTab();
|
||||
rbs2.close();
|
||||
|
||||
// flip back to tab1's window and ensure its URL pops up instead of tab2's URL
|
||||
await switchToWindow(window);
|
||||
isSelectedTab(window, tab1); // sanity check
|
||||
|
||||
let rbs1 = await appMenu1.openReportBrokenSite();
|
||||
rbs1.isMainViewResetToCurrentTab();
|
||||
rbs1.close();
|
||||
|
||||
// likewise flip back to tab2's window and ensure its URL pops up instead of tab1's URL
|
||||
await switchToWindow(win2);
|
||||
isSelectedTab(win2, tab2); // sanity check
|
||||
|
||||
rbs2 = await appMenu2.openReportBrokenSite();
|
||||
rbs2.isMainViewResetToCurrentTab();
|
||||
rbs2.close();
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests that when Report Broken Site is disabled, it will
|
||||
* send the user to webcompat.com when clicked and it the
|
||||
* relevant tab's report data.
|
||||
*/
|
||||
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const VIDEO_URL = `${BASE_URL}/videotest.mp4`;
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["test.wait300msAfterTabSwitch", true]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testWebcompatComFallbacks() {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testWebcompatComFallback(tab, AppMenu());
|
||||
|
||||
await changeTab(tab, REPORTABLE_PAGE_URL2);
|
||||
await testWebcompatComFallback(tab, ProtectionsPanel());
|
||||
|
||||
// also load a video to ensure system codec
|
||||
// information is loaded and properly sent
|
||||
const tab2 = await openTab(VIDEO_URL);
|
||||
await testWebcompatComFallback(tab2, HelpMenu());
|
||||
closeTab(tab2);
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<script>
|
||||
window.marfeel = 1;
|
||||
window.Mobify = { Tag: 1 };
|
||||
window.FastClick = 1;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- blocked tracking content -->
|
||||
<iframe src="https://trackertest.org/"></iframe>
|
||||
<!-- mixed active content -->
|
||||
<iframe src="http://tracking.example.org/browser/browser/base/content/test/protectionsUI/benignPage.html"></iframe>
|
||||
<!-- mixed display content -->
|
||||
<img src="http://example.com/tests/image/test/mochitest/blue.png"></img>
|
||||
</body>
|
||||
</html>
|
||||
897
src/zen/tests/mochitests/reportbrokensite/head.js
Normal file
897
src/zen/tests/mochitests/reportbrokensite/head.js
Normal file
@@ -0,0 +1,897 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { CustomizableUITestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/CustomizableUITestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const { EnterprisePolicyTesting, PoliciesPrefTracker } =
|
||||
ChromeUtils.importESModule(
|
||||
"resource://testing-common/EnterprisePolicyTesting.sys.mjs"
|
||||
);
|
||||
|
||||
const { UrlClassifierTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/UrlClassifierTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const { ReportBrokenSite } = ChromeUtils.importESModule(
|
||||
"moz-src:///browser/components/reportbrokensite/ReportBrokenSite.sys.mjs"
|
||||
);
|
||||
|
||||
const BASE_URL =
|
||||
"https://example.com/browser/browser/components/reportbrokensite/test/browser/";
|
||||
|
||||
const REPORTABLE_PAGE_URL = "https://example.com";
|
||||
|
||||
const REPORTABLE_PAGE_URL2 = REPORTABLE_PAGE_URL.replace(".com", ".org");
|
||||
|
||||
const REPORTABLE_PAGE_URL3 = `${BASE_URL}example_report_page.html`;
|
||||
|
||||
const NEW_REPORT_ENDPOINT_TEST_URL = `${BASE_URL}sendMoreInfoTestEndpoint.html`;
|
||||
|
||||
const PREFS = {
|
||||
DATAREPORTING_ENABLED: "datareporting.healthreport.uploadEnabled",
|
||||
REPORTER_ENABLED: "ui.new-webcompat-reporter.enabled",
|
||||
REASON: "ui.new-webcompat-reporter.reason-dropdown",
|
||||
SEND_MORE_INFO: "ui.new-webcompat-reporter.send-more-info-link",
|
||||
NEW_REPORT_ENDPOINT: "ui.new-webcompat-reporter.new-report-endpoint",
|
||||
TOUCH_EVENTS: "dom.w3c_touch_events.enabled",
|
||||
USE_ACCESSIBILITY_THEME: "ui.useAccessibilityTheme",
|
||||
};
|
||||
|
||||
function add_common_setup() {
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREFS.NEW_REPORT_ENDPOINT, NEW_REPORT_ENDPOINT_TEST_URL],
|
||||
|
||||
// set touch events to auto-detect, as the pref gets set to 1 somewhere
|
||||
// while tests are running, making hasTouchScreen checks unreliable.
|
||||
[PREFS.TOUCH_EVENTS, 2],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(function () {
|
||||
for (const prefName of Object.values(PREFS)) {
|
||||
Services.prefs.clearUserPref(prefName);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function areObjectsEqual(actual, expected, path = "") {
|
||||
if (typeof expected == "function") {
|
||||
try {
|
||||
const passes = expected(actual);
|
||||
if (!passes) {
|
||||
info(`${path} not pass check function: ${actual}`);
|
||||
}
|
||||
return passes;
|
||||
} catch (e) {
|
||||
info(`${path} threw exception:
|
||||
got: ${typeof actual}, ${actual}
|
||||
expected: ${typeof expected}, ${expected}
|
||||
exception: ${e.message}
|
||||
${e.stack}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof actual != typeof expected) {
|
||||
info(`${path} types do not match:
|
||||
got: ${typeof actual}, ${actual}
|
||||
expected: ${typeof expected}, ${expected}`);
|
||||
return false;
|
||||
}
|
||||
if (typeof actual != "object" || actual === null || expected === null) {
|
||||
if (actual !== expected) {
|
||||
info(`${path} does not match
|
||||
got: ${typeof actual}, ${actual}
|
||||
expected: ${typeof expected}, ${expected}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const prefix = path ? `${path}.` : path;
|
||||
for (const [key, val] of Object.entries(actual)) {
|
||||
if (!(key in expected)) {
|
||||
info(`Extra ${prefix}${key}: ${val}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let result = true;
|
||||
for (const [key, expectedVal] of Object.entries(expected)) {
|
||||
if (key in actual) {
|
||||
if (!areObjectsEqual(actual[key], expectedVal, `${prefix}${key}`)) {
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
info(`Missing ${prefix}${key} (${expectedVal})`);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function clickAndAwait(toClick, evt, target) {
|
||||
const menuPromise = BrowserTestUtils.waitForEvent(target, evt);
|
||||
EventUtils.synthesizeMouseAtCenter(toClick, {}, window);
|
||||
return menuPromise;
|
||||
}
|
||||
|
||||
async function openTab(url, win) {
|
||||
const options = {
|
||||
gBrowser:
|
||||
win?.gBrowser ||
|
||||
Services.wm.getMostRecentWindow("navigator:browser").gBrowser,
|
||||
url,
|
||||
};
|
||||
return BrowserTestUtils.openNewForegroundTab(options);
|
||||
}
|
||||
|
||||
async function changeTab(tab, url) {
|
||||
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
function closeTab(tab) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
function switchToWindow(win) {
|
||||
const promises = [
|
||||
BrowserTestUtils.waitForEvent(win, "focus"),
|
||||
BrowserTestUtils.waitForEvent(win, "activate"),
|
||||
];
|
||||
win.focus();
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function isSelectedTab(win, tab) {
|
||||
const selectedTab = win.document.querySelector(".tabbrowser-tab[selected]");
|
||||
is(selectedTab, tab);
|
||||
}
|
||||
|
||||
async function setupPolicyEngineWithJson(json, customSchema) {
|
||||
PoliciesPrefTracker.restoreDefaultValues();
|
||||
if (typeof json != "object") {
|
||||
let filePath = getTestFilePath(json ? json : "non-existing-file.json");
|
||||
return EnterprisePolicyTesting.setupPolicyEngineWithJson(
|
||||
filePath,
|
||||
customSchema
|
||||
);
|
||||
}
|
||||
return EnterprisePolicyTesting.setupPolicyEngineWithJson(json, customSchema);
|
||||
}
|
||||
|
||||
async function ensureReportBrokenSiteDisabledByPolicy() {
|
||||
await setupPolicyEngineWithJson({
|
||||
policies: {
|
||||
DisableFeedbackCommands: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
registerCleanupFunction(async function resetPolicies() {
|
||||
if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
|
||||
await setupPolicyEngineWithJson("");
|
||||
}
|
||||
EnterprisePolicyTesting.resetRunOnceState();
|
||||
PoliciesPrefTracker.restoreDefaultValues();
|
||||
PoliciesPrefTracker.stop();
|
||||
});
|
||||
|
||||
function ensureReportBrokenSitePreffedOn() {
|
||||
Services.prefs.setBoolPref(PREFS.DATAREPORTING_ENABLED, true);
|
||||
Services.prefs.setBoolPref(PREFS.REPORTER_ENABLED, true);
|
||||
ensureReasonDisabled();
|
||||
}
|
||||
|
||||
function ensureReportBrokenSitePreffedOff() {
|
||||
Services.prefs.setBoolPref(PREFS.REPORTER_ENABLED, false);
|
||||
}
|
||||
|
||||
function ensureSendMoreInfoEnabled() {
|
||||
Services.prefs.setBoolPref(PREFS.SEND_MORE_INFO, true);
|
||||
}
|
||||
|
||||
function ensureSendMoreInfoDisabled() {
|
||||
Services.prefs.setBoolPref(PREFS.SEND_MORE_INFO, false);
|
||||
}
|
||||
|
||||
function ensureReasonDisabled() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 0);
|
||||
}
|
||||
|
||||
function ensureReasonOptional() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 1);
|
||||
}
|
||||
|
||||
function ensureReasonRequired() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 2);
|
||||
}
|
||||
|
||||
function isMenuItemEnabled(menuItem, itemDesc) {
|
||||
ok(!menuItem.hidden, `${itemDesc} menu item is shown`);
|
||||
ok(!menuItem.disabled, `${itemDesc} menu item is enabled`);
|
||||
}
|
||||
|
||||
function isMenuItemHidden(menuItem, itemDesc) {
|
||||
ok(
|
||||
!menuItem || menuItem.hidden || !BrowserTestUtils.isVisible(menuItem),
|
||||
`${itemDesc} menu item is hidden`
|
||||
);
|
||||
}
|
||||
|
||||
function isMenuItemDisabled(menuItem, itemDesc) {
|
||||
ok(!menuItem.hidden, `${itemDesc} menu item is shown`);
|
||||
ok(menuItem.disabled, `${itemDesc} menu item is disabled`);
|
||||
}
|
||||
|
||||
function waitForWebcompatComTab(gBrowser) {
|
||||
return BrowserTestUtils.waitForNewTab(gBrowser, NEW_REPORT_ENDPOINT_TEST_URL);
|
||||
}
|
||||
|
||||
class ReportBrokenSiteHelper {
|
||||
sourceMenu = undefined;
|
||||
win = undefined;
|
||||
|
||||
constructor(sourceMenu) {
|
||||
this.sourceMenu = sourceMenu;
|
||||
this.win = sourceMenu.win;
|
||||
}
|
||||
|
||||
getViewNode(id) {
|
||||
return PanelMultiView.getViewNode(this.win.document, id);
|
||||
}
|
||||
|
||||
get mainView() {
|
||||
return this.getViewNode("report-broken-site-popup-mainView");
|
||||
}
|
||||
|
||||
get sentView() {
|
||||
return this.getViewNode("report-broken-site-popup-reportSentView");
|
||||
}
|
||||
|
||||
get openPanel() {
|
||||
return this.mainView?.closest("panel");
|
||||
}
|
||||
|
||||
get opened() {
|
||||
return this.openPanel?.hasAttribute("panelopen");
|
||||
}
|
||||
|
||||
async click(triggerMenuItem) {
|
||||
const window = triggerMenuItem.ownerGlobal;
|
||||
await EventUtils.synthesizeMouseAtCenter(triggerMenuItem, {}, window);
|
||||
}
|
||||
|
||||
async open(triggerMenuItem) {
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(
|
||||
this.mainView,
|
||||
"ViewShown"
|
||||
);
|
||||
const focusPromise = BrowserTestUtils.waitForEvent(this.URLInput, "focus");
|
||||
await this.click(triggerMenuItem);
|
||||
await shownPromise;
|
||||
await focusPromise;
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => this.URLInput.selectionStart === 0
|
||||
);
|
||||
}
|
||||
|
||||
async #assertClickAndViewChanges(button, view, newView, newFocus) {
|
||||
ok(view.closest("panel").hasAttribute("panelopen"), "Panel is open");
|
||||
ok(BrowserTestUtils.isVisible(button), "Button is visible");
|
||||
ok(!button.disabled, "Button is enabled");
|
||||
const promises = [];
|
||||
if (newView) {
|
||||
if (newView.nodeName == "panel") {
|
||||
promises.push(BrowserTestUtils.waitForEvent(newView, "popupshown"));
|
||||
} else {
|
||||
promises.push(BrowserTestUtils.waitForEvent(newView, "ViewShown"));
|
||||
}
|
||||
} else {
|
||||
promises.push(BrowserTestUtils.waitForEvent(view, "ViewHiding"));
|
||||
}
|
||||
if (newFocus) {
|
||||
promises.push(BrowserTestUtils.waitForEvent(newFocus, "focus"));
|
||||
}
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, this.win);
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async awaitReportSentViewOpened() {
|
||||
await Promise.all([
|
||||
BrowserTestUtils.waitForEvent(this.sentView, "ViewShown"),
|
||||
BrowserTestUtils.waitForEvent(this.okayButton, "focus"),
|
||||
]);
|
||||
}
|
||||
|
||||
async clickSend() {
|
||||
await this.#assertClickAndViewChanges(
|
||||
this.sendButton,
|
||||
this.mainView,
|
||||
this.sentView,
|
||||
this.okayButton
|
||||
);
|
||||
}
|
||||
|
||||
waitForSendMoreInfoTab() {
|
||||
return BrowserTestUtils.waitForNewTab(
|
||||
this.win.gBrowser,
|
||||
NEW_REPORT_ENDPOINT_TEST_URL
|
||||
);
|
||||
}
|
||||
|
||||
async clickSendMoreInfo() {
|
||||
const newTabPromise = waitForWebcompatComTab(this.win.gBrowser);
|
||||
EventUtils.synthesizeMouseAtCenter(this.sendMoreInfoLink, {}, this.win);
|
||||
const newTab = await newTabPromise;
|
||||
const receivedData = await SpecialPowers.spawn(
|
||||
newTab.linkedBrowser,
|
||||
[],
|
||||
async function () {
|
||||
await content.wrappedJSObject.messageArrived;
|
||||
return content.wrappedJSObject.message;
|
||||
}
|
||||
);
|
||||
this.win.gBrowser.removeCurrentTab();
|
||||
return receivedData;
|
||||
}
|
||||
|
||||
async clickCancel() {
|
||||
await this.#assertClickAndViewChanges(this.cancelButton, this.mainView);
|
||||
}
|
||||
|
||||
async clickOkay() {
|
||||
await this.#assertClickAndViewChanges(this.okayButton, this.sentView);
|
||||
}
|
||||
|
||||
async clickBack() {
|
||||
await this.#assertClickAndViewChanges(
|
||||
this.backButton,
|
||||
this.sourceMenu.popup
|
||||
);
|
||||
}
|
||||
|
||||
isBackButtonEnabled() {
|
||||
ok(BrowserTestUtils.isVisible(this.backButton), "Back button is visible");
|
||||
ok(!this.backButton.disabled, "Back button is enabled");
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.opened) {
|
||||
this.openPanel?.hidePopup(false);
|
||||
}
|
||||
this.sourceMenu?.close();
|
||||
}
|
||||
|
||||
// UI element getters
|
||||
get URLInput() {
|
||||
return this.getViewNode("report-broken-site-popup-url");
|
||||
}
|
||||
|
||||
get URLInvalidMessage() {
|
||||
return this.getViewNode("report-broken-site-popup-invalid-url-msg");
|
||||
}
|
||||
|
||||
get reasonInput() {
|
||||
return this.getViewNode("report-broken-site-popup-reason");
|
||||
}
|
||||
|
||||
get reasonDropdownPopup() {
|
||||
return this.win.document.getElementById("ContentSelectDropdown").menupopup;
|
||||
}
|
||||
|
||||
get reasonRequiredMessage() {
|
||||
return this.getViewNode("report-broken-site-popup-missing-reason-msg");
|
||||
}
|
||||
|
||||
get reasonLabelRequired() {
|
||||
return this.getViewNode("report-broken-site-popup-reason-label");
|
||||
}
|
||||
|
||||
get reasonLabelOptional() {
|
||||
return this.getViewNode("report-broken-site-popup-reason-optional-label");
|
||||
}
|
||||
|
||||
get descriptionTextarea() {
|
||||
return this.getViewNode("report-broken-site-popup-description");
|
||||
}
|
||||
|
||||
get sendMoreInfoLink() {
|
||||
return this.getViewNode("report-broken-site-popup-send-more-info-link");
|
||||
}
|
||||
|
||||
get backButton() {
|
||||
return this.mainView.querySelector(".subviewbutton-back");
|
||||
}
|
||||
|
||||
get sendButton() {
|
||||
return this.getViewNode("report-broken-site-popup-send-button");
|
||||
}
|
||||
|
||||
get cancelButton() {
|
||||
return this.getViewNode("report-broken-site-popup-cancel-button");
|
||||
}
|
||||
|
||||
get okayButton() {
|
||||
return this.getViewNode("report-broken-site-popup-okay-button");
|
||||
}
|
||||
|
||||
// Test helpers
|
||||
|
||||
#setInput(input, value) {
|
||||
input.value = value;
|
||||
input.dispatchEvent(
|
||||
new UIEvent("input", { bubbles: true, view: this.win })
|
||||
);
|
||||
}
|
||||
|
||||
setURL(value) {
|
||||
this.#setInput(this.URLInput, value);
|
||||
}
|
||||
|
||||
chooseReason(value) {
|
||||
const item = this.getViewNode(`report-broken-site-popup-reason-${value}`);
|
||||
this.reasonInput.selectedIndex = item.index;
|
||||
}
|
||||
|
||||
dismissDropdownPopup() {
|
||||
const popup = this.reasonDropdownPopup;
|
||||
const menuPromise = BrowserTestUtils.waitForPopupEvent(popup, "hidden");
|
||||
popup.hidePopup();
|
||||
return menuPromise;
|
||||
}
|
||||
|
||||
setDescription(value) {
|
||||
this.#setInput(this.descriptionTextarea, value);
|
||||
}
|
||||
|
||||
isURL(expected) {
|
||||
is(this.URLInput.value, expected);
|
||||
}
|
||||
|
||||
isURLInvalidMessageShown() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.URLInvalidMessage),
|
||||
"'Please enter a valid URL' message is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isURLInvalidMessageHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.URLInvalidMessage),
|
||||
"'Please enter a valid URL' message is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonNeededMessageShown() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonRequiredMessage),
|
||||
"'Please choose a reason' message is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonNeededMessageHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonRequiredMessage),
|
||||
"'Please choose a reason' message is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isSendButtonEnabled() {
|
||||
ok(BrowserTestUtils.isVisible(this.sendButton), "Send button is visible");
|
||||
ok(!this.sendButton.disabled, "Send button is enabled");
|
||||
}
|
||||
|
||||
isSendButtonDisabled() {
|
||||
ok(BrowserTestUtils.isVisible(this.sendButton), "Send button is visible");
|
||||
ok(this.sendButton.disabled, "Send button is disabled");
|
||||
}
|
||||
|
||||
isSendMoreInfoShown() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.sendMoreInfoLink),
|
||||
"send more info is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isSendMoreInfoHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.sendMoreInfoLink),
|
||||
"send more info is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isSendMoreInfoShownOrHiddenAppropriately() {
|
||||
if (Services.prefs.getBoolPref(PREFS.SEND_MORE_INFO)) {
|
||||
this.isSendMoreInfoShown();
|
||||
} else {
|
||||
this.isSendMoreInfoHidden();
|
||||
}
|
||||
}
|
||||
|
||||
isReasonHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonInput),
|
||||
"reason drop-down is hidden"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelOptional),
|
||||
"optional reason label is hidden"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelRequired),
|
||||
"required reason label is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonRequired() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonInput),
|
||||
"reason drop-down is shown"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelOptional),
|
||||
"optional reason label is hidden"
|
||||
);
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonLabelRequired),
|
||||
"required reason label is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonOptional() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonInput),
|
||||
"reason drop-down is shown"
|
||||
);
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonLabelOptional),
|
||||
"optional reason label is shown"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelRequired),
|
||||
"required reason label is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonShownOrHiddenAppropriately() {
|
||||
const pref = Services.prefs.getIntPref(PREFS.REASON);
|
||||
if (pref == 2) {
|
||||
this.isReasonOptional();
|
||||
} else if (pref == 1) {
|
||||
this.isReasonOptional();
|
||||
} else {
|
||||
this.isReasonHidden();
|
||||
}
|
||||
}
|
||||
|
||||
isDescription(expected) {
|
||||
return this.descriptionTextarea.value == expected;
|
||||
}
|
||||
|
||||
isMainViewResetToCurrentTab() {
|
||||
this.isURL(this.win.gBrowser.selectedBrowser.currentURI.spec);
|
||||
this.isDescription("");
|
||||
this.isReasonShownOrHiddenAppropriately();
|
||||
this.isSendMoreInfoShownOrHiddenAppropriately();
|
||||
}
|
||||
}
|
||||
|
||||
class MenuHelper {
|
||||
menuDescription = undefined;
|
||||
|
||||
win = undefined;
|
||||
|
||||
constructor(win = window) {
|
||||
this.win = win;
|
||||
}
|
||||
|
||||
getViewNode(id) {
|
||||
return PanelMultiView.getViewNode(this.win.document, id);
|
||||
}
|
||||
|
||||
get showsBackButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get reportBrokenSite() {
|
||||
throw new Error("Should be defined in derived class");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
throw new Error("Should be defined in derived class");
|
||||
}
|
||||
|
||||
get opened() {
|
||||
return this.popup?.hasAttribute("panelopen");
|
||||
}
|
||||
|
||||
async open() {}
|
||||
|
||||
async close() {}
|
||||
|
||||
isReportBrokenSiteDisabled() {
|
||||
return isMenuItemDisabled(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportBrokenSiteEnabled() {
|
||||
return isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportBrokenSiteHidden() {
|
||||
return isMenuItemHidden(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
async clickReportBrokenSiteAndAwaitWebCompatTabData() {
|
||||
const newTabPromise = waitForWebcompatComTab(this.win.gBrowser);
|
||||
await this.clickReportBrokenSite();
|
||||
const newTab = await newTabPromise;
|
||||
const receivedData = await SpecialPowers.spawn(
|
||||
newTab.linkedBrowser,
|
||||
[],
|
||||
async function () {
|
||||
await content.wrappedJSObject.messageArrived;
|
||||
return content.wrappedJSObject.message;
|
||||
}
|
||||
);
|
||||
|
||||
this.win.gBrowser.removeCurrentTab();
|
||||
return receivedData;
|
||||
}
|
||||
|
||||
async clickReportBrokenSite() {
|
||||
if (!this.opened) {
|
||||
await this.open();
|
||||
}
|
||||
isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
const rbs = new ReportBrokenSiteHelper(this);
|
||||
await rbs.click(this.reportBrokenSite);
|
||||
return rbs;
|
||||
}
|
||||
|
||||
async openReportBrokenSite() {
|
||||
if (!this.opened) {
|
||||
await this.open();
|
||||
}
|
||||
isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
const rbs = new ReportBrokenSiteHelper(this);
|
||||
await rbs.open(this.reportBrokenSite);
|
||||
return rbs;
|
||||
}
|
||||
|
||||
async openAndPrefillReportBrokenSite(url = null, description = "") {
|
||||
let rbs = await this.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
if (url) {
|
||||
rbs.setURL(url);
|
||||
}
|
||||
if (description) {
|
||||
rbs.setDescription(description);
|
||||
}
|
||||
return rbs;
|
||||
}
|
||||
}
|
||||
|
||||
class AppMenuHelper extends MenuHelper {
|
||||
menuDescription = "AppMenu";
|
||||
|
||||
get reportBrokenSite() {
|
||||
return this.getViewNode("appMenu-report-broken-site-button");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
return this.win.document.getElementById("appMenu-popup");
|
||||
}
|
||||
|
||||
async open() {
|
||||
await new CustomizableUITestUtils(this.win).openMainMenu();
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.opened) {
|
||||
await new CustomizableUITestUtils(this.win).hideMainMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HelpMenuHelper extends MenuHelper {
|
||||
menuDescription = "Help Menu";
|
||||
|
||||
get showsBackButton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
get reportBrokenSite() {
|
||||
return this.win.document.getElementById("help_reportBrokenSite");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
return this.getViewNode("PanelUI-helpView");
|
||||
}
|
||||
|
||||
get helpMenu() {
|
||||
return this.win.document.getElementById("menu_HelpPopup");
|
||||
}
|
||||
|
||||
async openReportBrokenSite() {
|
||||
// We can't actually open the Help menu properly in testing, so the best
|
||||
// we can do to open its Report Broken Site panel is to force its DOM to be
|
||||
// prepared, and then soft-click the Report Broken Site menuitem to open it.
|
||||
await this.open();
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(
|
||||
this.win,
|
||||
"ViewShown",
|
||||
true,
|
||||
e => e.target.classList.contains("report-broken-site-view")
|
||||
);
|
||||
this.reportBrokenSite.click();
|
||||
await shownPromise;
|
||||
return new ReportBrokenSiteHelper(this);
|
||||
}
|
||||
|
||||
async clickReportBrokenSite() {
|
||||
await this.open();
|
||||
this.reportBrokenSite.click();
|
||||
return new ReportBrokenSiteHelper(this);
|
||||
}
|
||||
|
||||
async open() {
|
||||
const { helpMenu } = this;
|
||||
const promise = BrowserTestUtils.waitForEvent(helpMenu, "popupshown");
|
||||
|
||||
// This event-faking method was copied from browser_title_case_menus.js.
|
||||
// We can't actually open the Help menu in testing, but this lets us
|
||||
// force its DOM to be properly built.
|
||||
helpMenu.dispatchEvent(new MouseEvent("popupshowing", { bubbles: true }));
|
||||
helpMenu.dispatchEvent(new MouseEvent("popupshown", { bubbles: true }));
|
||||
|
||||
await promise;
|
||||
}
|
||||
|
||||
async close() {
|
||||
const { helpMenu } = this;
|
||||
const promise = BrowserTestUtils.waitForPopupEvent(helpMenu, "hidden");
|
||||
|
||||
// (Also copied from browser_title_case_menus.js)
|
||||
// Just for good measure, we'll fire the popuphiding/popuphidden events
|
||||
// after we close the menupopups.
|
||||
helpMenu.dispatchEvent(new MouseEvent("popuphiding", { bubbles: true }));
|
||||
helpMenu.dispatchEvent(new MouseEvent("popuphidden", { bubbles: true }));
|
||||
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
|
||||
class ProtectionsPanelHelper extends MenuHelper {
|
||||
menuDescription = "Protections Panel";
|
||||
|
||||
get reportBrokenSite() {
|
||||
this.win.gProtectionsHandler._initializePopup();
|
||||
return this.getViewNode("protections-popup-report-broken-site-button");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
this.win.gProtectionsHandler._initializePopup();
|
||||
return this.win.document.getElementById("protections-popup");
|
||||
}
|
||||
|
||||
async open() {
|
||||
const promise = BrowserTestUtils.waitForEvent(
|
||||
this.win,
|
||||
"popupshown",
|
||||
true,
|
||||
e => e.target.id == "protections-popup"
|
||||
);
|
||||
this.win.gProtectionsHandler.showProtectionsPopup();
|
||||
await promise;
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.opened) {
|
||||
const popup = this.popup;
|
||||
const promise = BrowserTestUtils.waitForPopupEvent(popup, "hidden");
|
||||
PanelMultiView.hidePopup(popup, false);
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AppMenu(win = window) {
|
||||
return new AppMenuHelper(win);
|
||||
}
|
||||
|
||||
function HelpMenu(win = window) {
|
||||
return new HelpMenuHelper(win);
|
||||
}
|
||||
|
||||
function ProtectionsPanel(win = window) {
|
||||
return new ProtectionsPanelHelper(win);
|
||||
}
|
||||
|
||||
function pressKeyAndAwait(event, key, config = {}) {
|
||||
const win = config.window || window;
|
||||
if (!event.then) {
|
||||
event = BrowserTestUtils.waitForEvent(win, event, config.timeout || 200);
|
||||
}
|
||||
EventUtils.synthesizeKey(key, config, win);
|
||||
return event;
|
||||
}
|
||||
|
||||
async function pressKeyAndGetFocus(key, config = {}) {
|
||||
return (await pressKeyAndAwait("focus", key, config)).target;
|
||||
}
|
||||
|
||||
async function tabTo(match, win = window) {
|
||||
const config = { window: win };
|
||||
const { activeElement } = win.document;
|
||||
if (activeElement?.matches(match)) {
|
||||
return activeElement;
|
||||
}
|
||||
let initial = await pressKeyAndGetFocus("VK_TAB", config);
|
||||
let target = initial;
|
||||
do {
|
||||
if (target.matches(match)) {
|
||||
return target;
|
||||
}
|
||||
target = await pressKeyAndGetFocus("VK_TAB", config);
|
||||
} while (target && target !== initial);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function filterFrameworkDetectorFails(ping, expected) {
|
||||
// the framework detector's frame-script may fail to run in low memory or other
|
||||
// weird corner-cases, so we ignore the results in that case if they don't match.
|
||||
if (!areObjectsEqual(ping.frameworks, expected.frameworks)) {
|
||||
const { fastclick, mobify, marfeel } = ping.frameworks;
|
||||
if (!fastclick && !mobify && !marfeel) {
|
||||
console.info("Ignoring failure to get framework data");
|
||||
expected.frameworks = ping.frameworks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setupStrictETP() {
|
||||
await UrlClassifierTestUtils.addTestTrackers();
|
||||
registerCleanupFunction(() => {
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
});
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["security.mixed_content.block_active_content", true],
|
||||
["security.mixed_content.block_display_content", true],
|
||||
["security.mixed_content.upgrade_display_content", false],
|
||||
[
|
||||
"urlclassifier.trackingTable",
|
||||
"content-track-digest256,mochitest2-track-simple",
|
||||
],
|
||||
["browser.contentblocking.category", "strict"],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// copied from browser/base/content/test/protectionsUI/head.js
|
||||
function waitForContentBlockingEvent(numChanges = 1, win = null) {
|
||||
if (!win) {
|
||||
win = window;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
let n = 0;
|
||||
let listener = {
|
||||
onContentBlockingEvent(webProgress, request, event) {
|
||||
n = n + 1;
|
||||
info(
|
||||
`Received onContentBlockingEvent event: ${event} (${n} of ${numChanges})`
|
||||
);
|
||||
if (n >= numChanges) {
|
||||
win.gBrowser.removeProgressListener(listener);
|
||||
resolve(n);
|
||||
}
|
||||
},
|
||||
};
|
||||
win.gBrowser.addProgressListener(listener);
|
||||
});
|
||||
}
|
||||
330
src/zen/tests/mochitests/reportbrokensite/send.js
Normal file
330
src/zen/tests/mochitests/reportbrokensite/send.js
Normal file
@@ -0,0 +1,330 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Helper methods for testing sending reports with
|
||||
* the Report Broken Site feature.
|
||||
*/
|
||||
|
||||
/* import-globals-from head.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { Troubleshoot } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/Troubleshoot.sys.mjs"
|
||||
);
|
||||
|
||||
function getSysinfoProperty(propertyName, defaultValue) {
|
||||
try {
|
||||
return Services.sysinfo.getProperty(propertyName);
|
||||
} catch (e) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function securityStringToArray(str) {
|
||||
return str ? str.split(";") : null;
|
||||
}
|
||||
|
||||
function getExpectedGraphicsDevices(snapshot) {
|
||||
const { graphics } = snapshot;
|
||||
return [
|
||||
graphics.adapterDeviceID,
|
||||
graphics.adapterVendorID,
|
||||
graphics.adapterDeviceID2,
|
||||
graphics.adapterVendorID2,
|
||||
]
|
||||
.filter(i => i)
|
||||
.sort();
|
||||
}
|
||||
|
||||
function compareGraphicsDevices(expected, rawActual) {
|
||||
const actual = rawActual
|
||||
.map(({ deviceID, vendorID }) => [deviceID, vendorID])
|
||||
.flat()
|
||||
.filter(i => i)
|
||||
.sort();
|
||||
return areObjectsEqual(actual, expected);
|
||||
}
|
||||
|
||||
function getExpectedGraphicsDrivers(snapshot) {
|
||||
const { graphics } = snapshot;
|
||||
const expected = [];
|
||||
for (let i = 1; i < 3; ++i) {
|
||||
const version = graphics[`webgl${i}Version`];
|
||||
if (version && version != "-") {
|
||||
expected.push(graphics[`webgl${i}Renderer`]);
|
||||
expected.push(version);
|
||||
}
|
||||
}
|
||||
return expected.filter(i => i).sort();
|
||||
}
|
||||
|
||||
function compareGraphicsDrivers(expected, rawActual) {
|
||||
const actual = rawActual
|
||||
.map(({ renderer, version }) => [renderer, version])
|
||||
.flat()
|
||||
.filter(i => i)
|
||||
.sort();
|
||||
return areObjectsEqual(actual, expected);
|
||||
}
|
||||
|
||||
function getExpectedGraphicsFeatures(snapshot) {
|
||||
const expected = {};
|
||||
for (let { name, log, status } of snapshot.graphics.featureLog.features) {
|
||||
for (const item of log?.reverse() ?? []) {
|
||||
if (item.failureId && item.status == status) {
|
||||
status = `${status} (${item.message || item.failureId})`;
|
||||
}
|
||||
}
|
||||
expected[name] = status;
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
|
||||
async function getExpectedWebCompatInfo(tab, snapshot, fullAppData = false) {
|
||||
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
||||
|
||||
const { application, graphics, intl, securitySoftware } = snapshot;
|
||||
|
||||
const { fissionAutoStart, memorySizeBytes, updateChannel, userAgent } =
|
||||
application;
|
||||
|
||||
const app = {
|
||||
defaultLocales: intl.localeService.available,
|
||||
defaultUseragentString: userAgent,
|
||||
fissionEnabled: fissionAutoStart,
|
||||
};
|
||||
if (fullAppData) {
|
||||
app.applicationName = application.name;
|
||||
app.osArchitecture = getSysinfoProperty("arch", null);
|
||||
app.osName = getSysinfoProperty("name", null);
|
||||
app.osVersion = getSysinfoProperty("version", null);
|
||||
app.updateChannel = updateChannel;
|
||||
app.version = application.version;
|
||||
}
|
||||
|
||||
const hasTouchScreen = graphics.info.ApzTouchInput == 1;
|
||||
|
||||
const { registeredAntiVirus, registeredAntiSpyware, registeredFirewall } =
|
||||
securitySoftware;
|
||||
|
||||
const browserInfo = {
|
||||
addons: [],
|
||||
app,
|
||||
experiments: [],
|
||||
graphics: {
|
||||
devicesJson(actualStr) {
|
||||
const expected = getExpectedGraphicsDevices(snapshot);
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return compareGraphicsDevices(expected, JSON.parse(actualStr));
|
||||
},
|
||||
driversJson(actualStr) {
|
||||
const expected = getExpectedGraphicsDrivers(snapshot);
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return compareGraphicsDrivers(expected, JSON.parse(actualStr));
|
||||
},
|
||||
featuresJson(actualStr) {
|
||||
const expected = getExpectedGraphicsFeatures(snapshot);
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return areObjectsEqual(JSON.parse(actualStr), expected);
|
||||
},
|
||||
hasTouchScreen,
|
||||
monitorsJson(actualStr) {
|
||||
const expected = gfxInfo.getMonitors();
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return areObjectsEqual(JSON.parse(actualStr), expected);
|
||||
},
|
||||
},
|
||||
prefs: {
|
||||
cookieBehavior: Services.prefs.getIntPref(
|
||||
"network.cookie.cookieBehavior",
|
||||
-1
|
||||
),
|
||||
forcedAcceleratedLayers: Services.prefs.getBoolPref(
|
||||
"layers.acceleration.force-enabled",
|
||||
false
|
||||
),
|
||||
globalPrivacyControlEnabled: Services.prefs.getBoolPref(
|
||||
"privacy.globalprivacycontrol.enabled",
|
||||
false
|
||||
),
|
||||
installtriggerEnabled: Services.prefs.getBoolPref(
|
||||
"extensions.InstallTrigger.enabled",
|
||||
false
|
||||
),
|
||||
opaqueResponseBlocking: Services.prefs.getBoolPref(
|
||||
"browser.opaqueResponseBlocking",
|
||||
false
|
||||
),
|
||||
resistFingerprintingEnabled: Services.prefs.getBoolPref(
|
||||
"privacy.resistFingerprinting",
|
||||
false
|
||||
),
|
||||
softwareWebrender: Services.prefs.getBoolPref(
|
||||
"gfx.webrender.software",
|
||||
false
|
||||
),
|
||||
thirdPartyCookieBlockingEnabled: Services.prefs.getBoolPref(
|
||||
"network.cookie.cookieBehavior.optInPartitioning",
|
||||
false
|
||||
),
|
||||
thirdPartyCookieBlockingEnabledInPbm: Services.prefs.getBoolPref(
|
||||
"network.cookie.cookieBehavior.optInPartitioning.pbmode",
|
||||
false
|
||||
),
|
||||
},
|
||||
security: {
|
||||
antispyware: securityStringToArray(registeredAntiSpyware),
|
||||
antivirus: securityStringToArray(registeredAntiVirus),
|
||||
firewall: securityStringToArray(registeredFirewall),
|
||||
},
|
||||
system: {
|
||||
isTablet: getSysinfoProperty("tablet", false),
|
||||
memory: Math.round(memorySizeBytes / 1024 / 1024),
|
||||
},
|
||||
};
|
||||
|
||||
const tabInfo = await tab.linkedBrowser.ownerGlobal.SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[],
|
||||
async function () {
|
||||
return {
|
||||
devicePixelRatio: `${content.devicePixelRatio}`,
|
||||
antitracking: {
|
||||
blockList: "basic",
|
||||
isPrivateBrowsing: false,
|
||||
hasTrackingContentBlocked: false,
|
||||
hasMixedActiveContentBlocked: false,
|
||||
hasMixedDisplayContentBlocked: false,
|
||||
btpHasPurgedSite: false,
|
||||
etpCategory: "standard",
|
||||
},
|
||||
frameworks: {
|
||||
fastclick: false,
|
||||
marfeel: false,
|
||||
mobify: false,
|
||||
},
|
||||
languages: content.navigator.languages,
|
||||
useragentString: content.navigator.userAgent,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
browserInfo.graphics.devicePixelRatio = tabInfo.devicePixelRatio;
|
||||
delete tabInfo.devicePixelRatio;
|
||||
|
||||
return { browserInfo, tabInfo };
|
||||
}
|
||||
|
||||
function extractPingData(branch) {
|
||||
const data = {};
|
||||
for (const [name, value] of Object.entries(branch)) {
|
||||
data[name] = value.testGetValue();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function extractBrokenSiteReportFromGleanPing(Glean) {
|
||||
const ping = extractPingData(Glean.brokenSiteReport);
|
||||
ping.tabInfo = extractPingData(Glean.brokenSiteReportTabInfo);
|
||||
ping.tabInfo.antitracking = extractPingData(
|
||||
Glean.brokenSiteReportTabInfoAntitracking
|
||||
);
|
||||
ping.tabInfo.frameworks = extractPingData(
|
||||
Glean.brokenSiteReportTabInfoFrameworks
|
||||
);
|
||||
ping.browserInfo = {
|
||||
addons: Array.from(Glean.brokenSiteReportBrowserInfo.addons.testGetValue()),
|
||||
app: extractPingData(Glean.brokenSiteReportBrowserInfoApp),
|
||||
graphics: extractPingData(Glean.brokenSiteReportBrowserInfoGraphics),
|
||||
experiments: Array.from(
|
||||
Glean.brokenSiteReportBrowserInfo.experiments.testGetValue()
|
||||
),
|
||||
prefs: extractPingData(Glean.brokenSiteReportBrowserInfoPrefs),
|
||||
security: extractPingData(Glean.brokenSiteReportBrowserInfoSecurity),
|
||||
system: extractPingData(Glean.brokenSiteReportBrowserInfoSystem),
|
||||
};
|
||||
return ping;
|
||||
}
|
||||
|
||||
async function testSend(tab, menu, expectedOverrides = {}) {
|
||||
const url = expectedOverrides.url ?? menu.win.gBrowser.currentURI.spec;
|
||||
const description = expectedOverrides.description ?? "";
|
||||
const breakageCategory = expectedOverrides.breakageCategory ?? null;
|
||||
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
|
||||
const snapshot = await Troubleshoot.snapshot();
|
||||
const expected = await getExpectedWebCompatInfo(tab, snapshot);
|
||||
|
||||
expected.url = url;
|
||||
expected.description = description;
|
||||
expected.breakageCategory = breakageCategory;
|
||||
|
||||
if (expectedOverrides.addons) {
|
||||
expected.browserInfo.addons = expectedOverrides.addons;
|
||||
}
|
||||
|
||||
if (expectedOverrides.experiments) {
|
||||
expected.browserInfo.experiments = expectedOverrides.experiments;
|
||||
}
|
||||
|
||||
if (expectedOverrides.antitracking) {
|
||||
expected.tabInfo.antitracking = expectedOverrides.antitracking;
|
||||
}
|
||||
|
||||
if (expectedOverrides.frameworks) {
|
||||
expected.tabInfo.frameworks = expectedOverrides.frameworks;
|
||||
}
|
||||
|
||||
if (breakageCategory) {
|
||||
rbs.chooseReason(breakageCategory);
|
||||
}
|
||||
|
||||
Services.fog.testResetFOG();
|
||||
await GleanPings.brokenSiteReport.testSubmission(
|
||||
() => {
|
||||
const ping = extractBrokenSiteReportFromGleanPing(Glean);
|
||||
|
||||
// sanity checks
|
||||
const { browserInfo, tabInfo } = ping;
|
||||
ok(ping.url?.length, "Got a URL");
|
||||
ok(
|
||||
["basic", "strict"].includes(tabInfo.antitracking.blockList),
|
||||
"Got a blockList"
|
||||
);
|
||||
ok(tabInfo.useragentString?.length, "Got a final UA string");
|
||||
ok(
|
||||
browserInfo.app.defaultUseragentString?.length,
|
||||
"Got a default UA string"
|
||||
);
|
||||
|
||||
filterFrameworkDetectorFails(ping.tabInfo, expected.tabInfo);
|
||||
|
||||
ok(areObjectsEqual(ping, expected), "ping matches expectations");
|
||||
},
|
||||
() => rbs.clickSend()
|
||||
);
|
||||
|
||||
await rbs.clickOkay();
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let ready;
|
||||
window.wrtReady = new Promise(r => ready = r);
|
||||
|
||||
let arrived;
|
||||
window.messageArrived = new Promise(r => arrived = r);
|
||||
|
||||
window.addEventListener("message", e => {
|
||||
window.message = e.data;
|
||||
arrived();
|
||||
});
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
setTimeout(ready, 100);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
305
src/zen/tests/mochitests/reportbrokensite/send_more_info.js
Normal file
305
src/zen/tests/mochitests/reportbrokensite/send_more_info.js
Normal file
@@ -0,0 +1,305 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Helper methods for testing the "send more info" link
|
||||
* of the Report Broken Site feature.
|
||||
*/
|
||||
|
||||
/* import-globals-from head.js */
|
||||
/* import-globals-from send.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send.js",
|
||||
this
|
||||
);
|
||||
|
||||
async function reformatExpectedWebCompatInfo(tab, overrides) {
|
||||
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
||||
const snapshot = await Troubleshoot.snapshot();
|
||||
const expected = await getExpectedWebCompatInfo(tab, snapshot, true);
|
||||
const { browserInfo, tabInfo } = expected;
|
||||
const { app, graphics, prefs, security } = browserInfo;
|
||||
const {
|
||||
applicationName,
|
||||
defaultUseragentString,
|
||||
fissionEnabled,
|
||||
osArchitecture,
|
||||
osName,
|
||||
osVersion,
|
||||
updateChannel,
|
||||
version,
|
||||
} = app;
|
||||
const { devicePixelRatio, hasTouchScreen } = graphics;
|
||||
const { antitracking, languages, useragentString } = tabInfo;
|
||||
|
||||
const addons = overrides.addons || [];
|
||||
const experiments = overrides.experiments || [];
|
||||
const atOverrides = overrides.antitracking;
|
||||
const blockList = atOverrides?.blockList ?? antitracking.blockList;
|
||||
const hasMixedActiveContentBlocked =
|
||||
atOverrides?.hasMixedActiveContentBlocked ??
|
||||
antitracking.hasMixedActiveContentBlocked;
|
||||
const hasMixedDisplayContentBlocked =
|
||||
atOverrides?.hasMixedDisplayContentBlocked ??
|
||||
antitracking.hasMixedDisplayContentBlocked;
|
||||
const hasTrackingContentBlocked =
|
||||
atOverrides?.hasTrackingContentBlocked ??
|
||||
antitracking.hasTrackingContentBlocked;
|
||||
const isPrivateBrowsing =
|
||||
atOverrides?.isPrivateBrowsing ?? antitracking.isPrivateBrowsing;
|
||||
const btpHasPurgedSite =
|
||||
atOverrides?.btpHasPurgedSite ?? antitracking.btpHasPurgedSite;
|
||||
const etpCategory = atOverrides?.etpCategory ?? antitracking.etpCategory;
|
||||
|
||||
const extra_labels = [];
|
||||
const frameworks = overrides.frameworks ?? {
|
||||
fastclick: false,
|
||||
mobify: false,
|
||||
marfeel: false,
|
||||
};
|
||||
|
||||
// ignore the console log unless explicily testing for it.
|
||||
const consoleLog = overrides.consoleLog ?? (() => true);
|
||||
|
||||
const finalPrefs = {};
|
||||
for (const [key, pref] of Object.entries({
|
||||
cookieBehavior: "network.cookie.cookieBehavior",
|
||||
forcedAcceleratedLayers: "layers.acceleration.force-enabled",
|
||||
globalPrivacyControlEnabled: "privacy.globalprivacycontrol.enabled",
|
||||
installtriggerEnabled: "extensions.InstallTrigger.enabled",
|
||||
opaqueResponseBlocking: "browser.opaqueResponseBlocking",
|
||||
resistFingerprintingEnabled: "privacy.resistFingerprinting",
|
||||
softwareWebrender: "gfx.webrender.software",
|
||||
thirdPartyCookieBlockingEnabled:
|
||||
"network.cookie.cookieBehavior.optInPartitioning",
|
||||
thirdPartyCookieBlockingEnabledInPbm:
|
||||
"network.cookie.cookieBehavior.optInPartitioning.pbmode",
|
||||
})) {
|
||||
if (key in prefs) {
|
||||
finalPrefs[pref] = prefs[key];
|
||||
}
|
||||
}
|
||||
|
||||
const reformatted = {
|
||||
blockList,
|
||||
details: {
|
||||
additionalData: {
|
||||
addons,
|
||||
applicationName,
|
||||
blockList,
|
||||
buildId: snapshot.application.buildID,
|
||||
devicePixelRatio: parseInt(devicePixelRatio),
|
||||
experiments,
|
||||
finalUserAgent: useragentString,
|
||||
fissionEnabled,
|
||||
gfxData: {
|
||||
devices(actual) {
|
||||
const devices = getExpectedGraphicsDevices(snapshot);
|
||||
return compareGraphicsDevices(devices, actual);
|
||||
},
|
||||
drivers(actual) {
|
||||
const drvs = getExpectedGraphicsDrivers(snapshot);
|
||||
return compareGraphicsDrivers(drvs, actual);
|
||||
},
|
||||
features(actual) {
|
||||
const features = getExpectedGraphicsFeatures(snapshot);
|
||||
return areObjectsEqual(actual, features);
|
||||
},
|
||||
hasTouchScreen,
|
||||
monitors(actual) {
|
||||
return areObjectsEqual(actual, gfxInfo.getMonitors());
|
||||
},
|
||||
},
|
||||
hasMixedActiveContentBlocked,
|
||||
hasMixedDisplayContentBlocked,
|
||||
hasTrackingContentBlocked,
|
||||
btpHasPurgedSite,
|
||||
isPB: isPrivateBrowsing,
|
||||
etpCategory,
|
||||
languages,
|
||||
locales: snapshot.intl.localeService.available,
|
||||
memoryMB: browserInfo.system.memory,
|
||||
osArchitecture,
|
||||
osName,
|
||||
osVersion,
|
||||
prefs: finalPrefs,
|
||||
version,
|
||||
},
|
||||
blockList,
|
||||
channel: updateChannel,
|
||||
consoleLog,
|
||||
defaultUserAgent: defaultUseragentString,
|
||||
frameworks,
|
||||
hasTouchScreen,
|
||||
"gfx.webrender.software": prefs.softwareWebrender,
|
||||
"mixed active content blocked": hasMixedActiveContentBlocked,
|
||||
"mixed passive content blocked": hasMixedDisplayContentBlocked,
|
||||
"tracking content blocked": hasTrackingContentBlocked
|
||||
? `true (${blockList})`
|
||||
: "false",
|
||||
"btp has purged site": btpHasPurgedSite,
|
||||
},
|
||||
extra_labels,
|
||||
src: "desktop-reporter",
|
||||
utm_campaign: "report-broken-site",
|
||||
utm_source: "desktop-reporter",
|
||||
};
|
||||
|
||||
const { gfxData } = reformatted.details.additionalData;
|
||||
for (const optional of [
|
||||
"direct2DEnabled",
|
||||
"directWriteEnabled",
|
||||
"directWriteVersion",
|
||||
"clearTypeParameters",
|
||||
"targetFrameRate",
|
||||
]) {
|
||||
if (optional in snapshot.graphics) {
|
||||
gfxData[optional] = snapshot.graphics[optional];
|
||||
}
|
||||
}
|
||||
|
||||
// We only care about this pref on Linux right now on webcompat.com.
|
||||
if (AppConstants.platform != "linux") {
|
||||
delete finalPrefs["layers.acceleration.force-enabled"];
|
||||
} else {
|
||||
reformatted.details["layers.acceleration.force-enabled"] =
|
||||
finalPrefs["layers.acceleration.force-enabled"];
|
||||
}
|
||||
|
||||
// Only bother adding the security key if it has any data
|
||||
if (Object.values(security).filter(e => e).length) {
|
||||
reformatted.details.additionalData.sec = security;
|
||||
}
|
||||
|
||||
const expectedCodecs = snapshot.media.codecSupportInfo
|
||||
.replaceAll(" NONE", "")
|
||||
.split("\n")
|
||||
.sort()
|
||||
.join("\n");
|
||||
if (expectedCodecs) {
|
||||
reformatted.details.additionalData.gfxData.codecSupport = rawActual => {
|
||||
const actual = Object.entries(rawActual)
|
||||
.map(
|
||||
([
|
||||
name,
|
||||
{ hardwareDecode, softwareDecode, hardwareEncode, softwareEncode },
|
||||
]) =>
|
||||
(
|
||||
`${name} ` +
|
||||
`${softwareDecode ? "SWDEC " : ""}` +
|
||||
`${hardwareDecode ? "HWDEC " : ""}` +
|
||||
`${softwareEncode ? "SWENC " : ""}` +
|
||||
`${hardwareEncode ? "HWENC " : ""}`
|
||||
).trim()
|
||||
)
|
||||
.sort()
|
||||
.join("\n");
|
||||
return areObjectsEqual(actual, expectedCodecs);
|
||||
};
|
||||
}
|
||||
|
||||
if (blockList != "basic") {
|
||||
extra_labels.push(`type-tracking-protection-${blockList}`);
|
||||
}
|
||||
|
||||
if (overrides.expectNoTabDetails) {
|
||||
delete reformatted.details.frameworks;
|
||||
delete reformatted.details.consoleLog;
|
||||
delete reformatted.details["mixed active content blocked"];
|
||||
delete reformatted.details["mixed passive content blocked"];
|
||||
delete reformatted.details["tracking content blocked"];
|
||||
delete reformatted.details["btp has purged site"];
|
||||
} else {
|
||||
const { fastclick, mobify, marfeel } = frameworks;
|
||||
if (fastclick) {
|
||||
extra_labels.push("type-fastclick");
|
||||
reformatted.details.fastclick = true;
|
||||
}
|
||||
if (mobify) {
|
||||
extra_labels.push("type-mobify");
|
||||
reformatted.details.mobify = true;
|
||||
}
|
||||
if (marfeel) {
|
||||
extra_labels.push("type-marfeel");
|
||||
reformatted.details.marfeel = true;
|
||||
}
|
||||
}
|
||||
|
||||
extra_labels.sort();
|
||||
|
||||
return reformatted;
|
||||
}
|
||||
|
||||
async function testSendMoreInfo(tab, menu, expectedOverrides = {}) {
|
||||
const url = expectedOverrides.url ?? menu.win.gBrowser.currentURI.spec;
|
||||
const description = expectedOverrides.description ?? "";
|
||||
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
|
||||
const receivedData = await rbs.clickSendMoreInfo();
|
||||
await checkWebcompatComPayload(
|
||||
tab,
|
||||
url,
|
||||
description,
|
||||
expectedOverrides,
|
||||
receivedData
|
||||
);
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
|
||||
async function testWebcompatComFallback(tab, menu) {
|
||||
const url = menu.win.gBrowser.currentURI.spec;
|
||||
const receivedData =
|
||||
await menu.clickReportBrokenSiteAndAwaitWebCompatTabData();
|
||||
await checkWebcompatComPayload(tab, url, "", {}, receivedData);
|
||||
menu.close();
|
||||
}
|
||||
|
||||
async function checkWebcompatComPayload(
|
||||
tab,
|
||||
url,
|
||||
description,
|
||||
expectedOverrides,
|
||||
receivedData
|
||||
) {
|
||||
const expected = await reformatExpectedWebCompatInfo(tab, expectedOverrides);
|
||||
expected.url = url;
|
||||
expected.description = description;
|
||||
|
||||
// sanity checks
|
||||
const { message } = receivedData;
|
||||
const { details } = message;
|
||||
const { additionalData } = details;
|
||||
ok(message.url?.length, "Got a URL");
|
||||
ok(["basic", "strict"].includes(details.blockList), "Got a blockList");
|
||||
ok(additionalData.applicationName?.length, "Got an app name");
|
||||
ok(additionalData.osArchitecture?.length, "Got an OS arch");
|
||||
ok(additionalData.osName?.length, "Got an OS name");
|
||||
ok(additionalData.osVersion?.length, "Got an OS version");
|
||||
ok(additionalData.version?.length, "Got an app version");
|
||||
ok(details.channel?.length, "Got an app channel");
|
||||
ok(details.defaultUserAgent?.length, "Got a default UA string");
|
||||
ok(additionalData.finalUserAgent?.length, "Got a final UA string");
|
||||
|
||||
// If we're sending any tab-specific data (which includes console logs),
|
||||
// check that there is also a valid screenshot.
|
||||
if ("consoleLog" in details) {
|
||||
const isScreenshotValid = await new Promise(done => {
|
||||
var image = new Image();
|
||||
image.onload = () => done(image.width > 0);
|
||||
image.onerror = () => done(false);
|
||||
image.src = receivedData.screenshot;
|
||||
});
|
||||
ok(isScreenshotValid, "Got a valid screenshot");
|
||||
}
|
||||
|
||||
filterFrameworkDetectorFails(message.details, expected.details);
|
||||
|
||||
ok(areObjectsEqual(message, expected), "sent info matches expectations");
|
||||
}
|
||||
14
src/zen/tests/mochitests/safebrowsing/browser.toml
Normal file
14
src/zen/tests/mochitests/safebrowsing/browser.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[DEFAULT]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"empty_file.html",
|
||||
]
|
||||
|
||||
["browser_bug400731.js"]
|
||||
|
||||
["browser_bug415846.js"]
|
||||
skip-if = ["true"] # Bug 1248632
|
||||
|
||||
["browser_mixedcontent_aboutblocked.js"]
|
||||
|
||||
["browser_whitelisted.js"]
|
||||
65
src/zen/tests/mochitests/safebrowsing/browser_bug400731.js
Normal file
65
src/zen/tests/mochitests/safebrowsing/browser_bug400731.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Check presence of the "Ignore this warning" button */
|
||||
|
||||
function checkWarningState() {
|
||||
return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
return !!content.document.getElementById("ignore_warning_link");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function testMalware() {
|
||||
await new Promise(resolve => waitForDBInit(resolve));
|
||||
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
|
||||
|
||||
const url = "http://www.itisatrap.org/firefox/its-an-attack.html";
|
||||
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
false,
|
||||
url,
|
||||
true
|
||||
);
|
||||
|
||||
let buttonPresent = await checkWarningState();
|
||||
ok(buttonPresent, "Ignore warning link should be present for malware");
|
||||
});
|
||||
|
||||
add_task(async function testUnwanted() {
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.allowOverride", false);
|
||||
|
||||
// Now launch the unwanted software test
|
||||
const url = "http://www.itisatrap.org/firefox/unwanted.html";
|
||||
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
false,
|
||||
url,
|
||||
true
|
||||
);
|
||||
|
||||
// Confirm that "Ignore this warning" is visible - bug 422410
|
||||
let buttonPresent = await checkWarningState();
|
||||
ok(
|
||||
!buttonPresent,
|
||||
"Ignore warning link should be missing for unwanted software"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testPhishing() {
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.allowOverride", true);
|
||||
|
||||
// Now launch the phishing test
|
||||
const url = "http://www.itisatrap.org/firefox/its-a-trap.html";
|
||||
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
false,
|
||||
url,
|
||||
true
|
||||
);
|
||||
|
||||
let buttonPresent = await checkWarningState();
|
||||
ok(buttonPresent, "Ignore warning link should be present for phishing");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
98
src/zen/tests/mochitests/safebrowsing/browser_bug415846.js
Normal file
98
src/zen/tests/mochitests/safebrowsing/browser_bug415846.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/* Check for the correct behaviour of the report web forgery/not a web forgery
|
||||
menu items.
|
||||
|
||||
Mac makes this astonishingly painful to test since their help menu is special magic,
|
||||
but we can at least test it on the other platforms.*/
|
||||
|
||||
const NORMAL_PAGE = "http://example.com";
|
||||
const PHISH_PAGE = "http://www.itisatrap.org/firefox/its-a-trap.html";
|
||||
|
||||
/**
|
||||
* Opens a new tab and browses to some URL, tests for the existence
|
||||
* of the phishing menu items, and then runs a test function to check
|
||||
* the state of the menu once opened. This function will take care of
|
||||
* opening and closing the menu.
|
||||
*
|
||||
* @param url (string)
|
||||
* The URL to browse the tab to.
|
||||
* @param testFn (function)
|
||||
* The function to run once the menu has been opened. This
|
||||
* function will be passed the "reportMenu" and "errorMenu"
|
||||
* DOM nodes as arguments, in that order. This function
|
||||
* should not yield anything.
|
||||
* @returns Promise
|
||||
*/
|
||||
function check_menu_at_page(url, testFn) {
|
||||
return BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
},
|
||||
async function (browser) {
|
||||
// We don't get load events when the DocShell redirects to error
|
||||
// pages, but we do get DOMContentLoaded, so we'll wait for that.
|
||||
let dclPromise = SpecialPowers.spawn(browser, [], async function () {
|
||||
await ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", false);
|
||||
});
|
||||
BrowserTestUtils.startLoadingURIString(browser, url);
|
||||
await dclPromise;
|
||||
|
||||
let menu = document.getElementById("menu_HelpPopup");
|
||||
ok(menu, "Help menu should exist");
|
||||
|
||||
let reportMenu = document.getElementById(
|
||||
"menu_HelpPopup_reportPhishingtoolmenu"
|
||||
);
|
||||
ok(reportMenu, "Report phishing menu item should exist");
|
||||
|
||||
let errorMenu = document.getElementById(
|
||||
"menu_HelpPopup_reportPhishingErrortoolmenu"
|
||||
);
|
||||
ok(errorMenu, "Report phishing error menu item should exist");
|
||||
|
||||
let menuOpen = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||
menu.openPopup(null, "", 0, 0, false, null);
|
||||
await menuOpen;
|
||||
|
||||
testFn(reportMenu, errorMenu);
|
||||
|
||||
let menuClose = BrowserTestUtils.waitForEvent(menu, "popuphidden");
|
||||
menu.hidePopup();
|
||||
await menuClose;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we show the "Report this page" menu item at a normal
|
||||
* page.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await check_menu_at_page(NORMAL_PAGE, (reportMenu, errorMenu) => {
|
||||
ok(
|
||||
!reportMenu.hidden,
|
||||
"Report phishing menu should be visible on normal sites"
|
||||
);
|
||||
ok(
|
||||
errorMenu.hidden,
|
||||
"Report error menu item should be hidden on normal sites"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that we show the "Report this page is okay" menu item at
|
||||
* a reported attack site.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await check_menu_at_page(PHISH_PAGE, (reportMenu, errorMenu) => {
|
||||
ok(
|
||||
reportMenu.hidden,
|
||||
"Report phishing menu should be hidden on phishing sites"
|
||||
);
|
||||
ok(
|
||||
!errorMenu.hidden,
|
||||
"Report error menu item should be visible on phishing sites"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const SECURE_CONTAINER_URL =
|
||||
"https://example.com/browser/browser/components/safebrowsing/content/test/empty_file.html";
|
||||
|
||||
add_task(async function testNormalBrowsing() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.safebrowsing.only_top_level", false]],
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
SECURE_CONTAINER_URL,
|
||||
async function (browser) {
|
||||
// Before we load the phish url, we have to make sure the hard-coded
|
||||
// black list has been added to the database.
|
||||
await new Promise(resolve => waitForDBInit(resolve));
|
||||
|
||||
let promise = new Promise(resolve => {
|
||||
// Register listener before loading phish URL.
|
||||
let removeFunc = BrowserTestUtils.addContentEventListener(
|
||||
browser,
|
||||
"AboutBlockedLoaded",
|
||||
() => {
|
||||
removeFunc();
|
||||
resolve();
|
||||
},
|
||||
{ wantUntrusted: true }
|
||||
);
|
||||
});
|
||||
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[PHISH_URL],
|
||||
async function (aPhishUrl) {
|
||||
// Create an iframe which is going to load a phish url.
|
||||
let iframe = content.document.createElement("iframe");
|
||||
iframe.src = aPhishUrl;
|
||||
content.document.body.appendChild(iframe);
|
||||
}
|
||||
);
|
||||
|
||||
await promise;
|
||||
ok(true, "about:blocked is successfully loaded!");
|
||||
}
|
||||
);
|
||||
});
|
||||
46
src/zen/tests/mochitests/safebrowsing/browser_whitelisted.js
Normal file
46
src/zen/tests/mochitests/safebrowsing/browser_whitelisted.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/* Ensure that hostnames in the whitelisted pref are not blocked. */
|
||||
|
||||
const PREF_WHITELISTED_HOSTNAMES = "urlclassifier.skipHostnames";
|
||||
const TEST_PAGE = "http://www.itisatrap.org/firefox/its-an-attack.html";
|
||||
var tabbrowser = null;
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
tabbrowser = null;
|
||||
Services.prefs.clearUserPref(PREF_WHITELISTED_HOSTNAMES);
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
||||
function testBlockedPage() {
|
||||
info("Non-whitelisted pages must be blocked");
|
||||
ok(true, "about:blocked was shown");
|
||||
}
|
||||
|
||||
function testWhitelistedPage(window) {
|
||||
info("Whitelisted pages must be skipped");
|
||||
var getmeout_button = window.document.getElementById("getMeOutButton");
|
||||
var ignorewarning_button = window.document.getElementById(
|
||||
"ignoreWarningButton"
|
||||
);
|
||||
ok(!getmeout_button, "GetMeOut button not present");
|
||||
ok(!ignorewarning_button, "IgnoreWarning button not present");
|
||||
}
|
||||
|
||||
add_task(async function testNormalBrowsing() {
|
||||
tabbrowser = gBrowser;
|
||||
let tab = (tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser));
|
||||
|
||||
info("Load a test page that's whitelisted");
|
||||
Services.prefs.setCharPref(
|
||||
PREF_WHITELISTED_HOSTNAMES,
|
||||
"example.com,www.ItIsaTrap.org,example.net"
|
||||
);
|
||||
await promiseTabLoadEvent(tab, TEST_PAGE, "load");
|
||||
testWhitelistedPage(tab.ownerGlobal);
|
||||
|
||||
info("Load a test page that's no longer whitelisted");
|
||||
Services.prefs.setCharPref(PREF_WHITELISTED_HOSTNAMES, "");
|
||||
await promiseTabLoadEvent(tab, TEST_PAGE, "AboutBlockedLoaded");
|
||||
testBlockedPage(tab.ownerGlobal);
|
||||
});
|
||||
1
src/zen/tests/mochitests/safebrowsing/empty_file.html
Normal file
1
src/zen/tests/mochitests/safebrowsing/empty_file.html
Normal file
@@ -0,0 +1 @@
|
||||
<html><body></body></html>
|
||||
103
src/zen/tests/mochitests/safebrowsing/head.js
Normal file
103
src/zen/tests/mochitests/safebrowsing/head.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// This url must sync with the table, url in SafeBrowsing.sys.mjs addMozEntries
|
||||
const PHISH_TABLE = "moztest-phish-simple";
|
||||
const PHISH_URL = "https://www.itisatrap.org/firefox/its-a-trap.html";
|
||||
|
||||
/**
|
||||
* Waits for a load (or custom) event to finish in a given tab. If provided
|
||||
* load an uri into the tab.
|
||||
*
|
||||
* @param tab
|
||||
* The tab to load into.
|
||||
* @param [optional] url
|
||||
* The url to load, or the current url.
|
||||
* @param [optional] event
|
||||
* The load event type to wait for. Defaults to "load".
|
||||
* @return {Promise} resolved when the event is handled.
|
||||
* @resolves to the received event
|
||||
* @rejects if a valid load event is not received within a meaningful interval
|
||||
*/
|
||||
function promiseTabLoadEvent(tab, url, eventType = "load") {
|
||||
info(`Wait tab event: ${eventType}`);
|
||||
|
||||
function handle(loadedUrl) {
|
||||
if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
|
||||
info(`Skipping spurious load event for ${loadedUrl}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
info("Tab event received: load");
|
||||
return true;
|
||||
}
|
||||
|
||||
let loaded;
|
||||
if (eventType === "load") {
|
||||
loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
|
||||
} else {
|
||||
// No need to use handle.
|
||||
loaded = BrowserTestUtils.waitForContentEvent(
|
||||
tab.linkedBrowser,
|
||||
eventType,
|
||||
true,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if (url) {
|
||||
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url);
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// This function is mostly ported from classifierCommon.js
|
||||
// under toolkit/components/url-classifier/tests/mochitest.
|
||||
function waitForDBInit(callback) {
|
||||
// Since there are two cases that may trigger the callback,
|
||||
// we have to carefully avoid multiple callbacks and observer
|
||||
// leaking.
|
||||
let didCallback = false;
|
||||
function callbackOnce() {
|
||||
if (!didCallback) {
|
||||
Services.obs.removeObserver(obsFunc, "mozentries-update-finished");
|
||||
callback();
|
||||
}
|
||||
didCallback = true;
|
||||
}
|
||||
|
||||
// The first part: listen to internal event.
|
||||
function obsFunc() {
|
||||
ok(true, "Received internal event!");
|
||||
callbackOnce();
|
||||
}
|
||||
Services.obs.addObserver(obsFunc, "mozentries-update-finished");
|
||||
|
||||
// The second part: we might have missed the event. Just do
|
||||
// an internal database lookup to confirm if the url has been
|
||||
// added.
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipal(
|
||||
Services.io.newURI(PHISH_URL),
|
||||
{}
|
||||
);
|
||||
|
||||
let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
|
||||
Ci.nsIUrlClassifierDBService
|
||||
);
|
||||
dbService.lookup(principal, PHISH_TABLE, value => {
|
||||
if (value === PHISH_TABLE) {
|
||||
ok(true, "DB lookup success!");
|
||||
callbackOnce();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Services.prefs.setCharPref(
|
||||
"urlclassifier.malwareTable",
|
||||
"moztest-malware-simple,moztest-unwanted-simple,moztest-harmful-simple"
|
||||
);
|
||||
Services.prefs.setCharPref("urlclassifier.phishTable", "moztest-phish-simple");
|
||||
Services.prefs.setCharPref(
|
||||
"urlclassifier.blockedTable",
|
||||
"moztest-block-simple"
|
||||
);
|
||||
SafeBrowsing.init();
|
||||
101
src/zen/tests/mochitests/shell/browser.toml
Normal file
101
src/zen/tests/mochitests/shell/browser.toml
Normal file
@@ -0,0 +1,101 @@
|
||||
[DEFAULT]
|
||||
|
||||
["browser_1119088.js"]
|
||||
support-files = ["mac_desktop_image.py"]
|
||||
run-if = ["os == 'mac'"]
|
||||
tags = "os_integration"
|
||||
skip-if = ["os == 'mac' && os_version == '14.70' && processor == 'x86_64'"] # Bug 1869703
|
||||
|
||||
["browser_420786.js"]
|
||||
run-if = ["os == 'linux'"]
|
||||
|
||||
["browser_633221.js"]
|
||||
run-if = ["os == 'linux'"]
|
||||
|
||||
["browser_createWindowsShortcut.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
|
||||
["browser_doesAppNeedPin.js"]
|
||||
|
||||
["browser_headless_screenshot_1.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
tags = "os_integration"
|
||||
|
||||
["browser_headless_screenshot_2.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_3.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_4.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_cross_origin.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless_cross_origin.html",
|
||||
"headless_iframe.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_redirect.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
"headless_redirect.html",
|
||||
"headless_redirect.html^headers^",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_processAUMID.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
|
||||
["browser_setDefaultBrowser.js"]
|
||||
tags = "os_integration"
|
||||
|
||||
["browser_setDefaultPDFHandler.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
tags = "os_integration"
|
||||
|
||||
["browser_setDesktopBackgroundPreview.js"]
|
||||
tags = "os_integration"
|
||||
179
src/zen/tests/mochitests/shell/browser_1119088.js
Normal file
179
src/zen/tests/mochitests/shell/browser_1119088.js
Normal file
@@ -0,0 +1,179 @@
|
||||
// Where we save the desktop background to (~/Pictures).
|
||||
const NS_OSX_PICTURE_DOCUMENTS_DIR = "Pct";
|
||||
|
||||
// Paths used to run the CLI command (python script) that is used to
|
||||
// 1) check the desktop background image matches what we set it to via
|
||||
// nsIShellService::setDesktopBackground() and
|
||||
// 2) revert the desktop background image to the OS default
|
||||
|
||||
let kPythonPath = "/usr/bin/python";
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("macosx", 23.0)) {
|
||||
kPythonPath = "/usr/local/bin/python3";
|
||||
}
|
||||
|
||||
const kDesktopCheckerScriptPath =
|
||||
"browser/browser/components/shell/test/mac_desktop_image.py";
|
||||
const kDefaultBackgroundImage_10_14 =
|
||||
"/Library/Desktop Pictures/Solid Colors/Teal.png";
|
||||
const kDefaultBackgroundImage_10_15 =
|
||||
"/System/Library/Desktop Pictures/Solid Colors/Teal.png";
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
|
||||
});
|
||||
|
||||
function getPythonExecutableFile() {
|
||||
let python = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
info(`Using python at location ${kPythonPath}`);
|
||||
python.initWithPath(kPythonPath);
|
||||
return python;
|
||||
}
|
||||
|
||||
function createProcess() {
|
||||
return Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
}
|
||||
|
||||
// Use a CLI command to set the desktop background to |imagePath|. Returns the
|
||||
// exit code of the CLI command which reflects whether or not the background
|
||||
// image was successfully set. Returns 0 on success.
|
||||
function setDesktopBackgroundCLI(imagePath) {
|
||||
let setBackgroundProcess = createProcess();
|
||||
setBackgroundProcess.init(getPythonExecutableFile());
|
||||
let args = [
|
||||
kDesktopCheckerScriptPath,
|
||||
"--verbose",
|
||||
"--set-background-image",
|
||||
imagePath,
|
||||
];
|
||||
setBackgroundProcess.run(true, args, args.length);
|
||||
return setBackgroundProcess.exitValue;
|
||||
}
|
||||
|
||||
// Check the desktop background is |imagePath| using a CLI command.
|
||||
// Returns the exit code of the CLI command which reflects whether or not
|
||||
// the provided image path matches the path of the current desktop background
|
||||
// image. A return value of 0 indicates success/match.
|
||||
function checkDesktopBackgroundCLI(imagePath) {
|
||||
let checkBackgroundProcess = createProcess();
|
||||
checkBackgroundProcess.init(getPythonExecutableFile());
|
||||
let args = [
|
||||
kDesktopCheckerScriptPath,
|
||||
"--verbose",
|
||||
"--check-background-image",
|
||||
imagePath,
|
||||
];
|
||||
checkBackgroundProcess.run(true, args, args.length);
|
||||
return checkBackgroundProcess.exitValue;
|
||||
}
|
||||
|
||||
// Use the python script to set/check the desktop background is |imagePath|
|
||||
function setAndCheckDesktopBackgroundCLI(imagePath) {
|
||||
Assert.ok(FileUtils.File(imagePath).exists(), `${imagePath} exists`);
|
||||
|
||||
let setExitCode = setDesktopBackgroundCLI(imagePath);
|
||||
Assert.equal(setExitCode, 0, `Setting background via CLI to ${imagePath}`);
|
||||
|
||||
let checkExitCode = checkDesktopBackgroundCLI(imagePath);
|
||||
Assert.equal(checkExitCode, 0, `Checking background via CLI is ${imagePath}`);
|
||||
}
|
||||
|
||||
// Restore the automation default background image. i.e., the default used
|
||||
// in the automated test environment, not the OS default.
|
||||
function restoreDefaultBackground() {
|
||||
let defaultBackgroundPath;
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("macosx", 19)) {
|
||||
defaultBackgroundPath = kDefaultBackgroundImage_10_15;
|
||||
} else {
|
||||
defaultBackgroundPath = kDefaultBackgroundImage_10_14;
|
||||
}
|
||||
setAndCheckDesktopBackgroundCLI(defaultBackgroundPath);
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["test.wait300msAfterTabSwitch", true]],
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests "Set As Desktop Background" platform implementation on macOS.
|
||||
*
|
||||
* Sets the desktop background image to the browser logo from the about:logo
|
||||
* page and verifies it was set successfully. Setting the desktop background
|
||||
* (which uses the nsIShellService::setDesktopBackground() interface method)
|
||||
* downloads the image to ~/Pictures using a unique file name and sets the
|
||||
* desktop background to the downloaded file leaving the download in place.
|
||||
* After setDesktopBackground() is called, the test uses a python script to
|
||||
* validate that the current desktop background is in fact set to the
|
||||
* downloaded logo.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:logo",
|
||||
},
|
||||
async () => {
|
||||
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(
|
||||
Ci.nsIDirectoryServiceProvider
|
||||
);
|
||||
let uuidGenerator = Services.uuid;
|
||||
let shellSvc = Cc["@mozilla.org/browser/shell-service;1"].getService(
|
||||
Ci.nsIShellService
|
||||
);
|
||||
|
||||
// Ensure we are starting with the default background. Log a
|
||||
// failure if we can not set the background to the default, but
|
||||
// ignore the case where the background is not already set as that
|
||||
// that may be due to a previous test failure.
|
||||
restoreDefaultBackground();
|
||||
|
||||
// Generate a UUID (with non-alphanumberic characters removed) to build
|
||||
// up a filename for the desktop background. Use a UUID to distinguish
|
||||
// between runs so we won't be confused by images that were not properly
|
||||
// cleaned up after previous runs.
|
||||
let uuid = uuidGenerator.generateUUID().toString().replace(/\W/g, "");
|
||||
|
||||
// Set the background image path to be $HOME/Pictures/<UUID>.png.
|
||||
// nsIShellService.setDesktopBackground() downloads the image to this
|
||||
// path and then sets it as the desktop background image, leaving the
|
||||
// image in place.
|
||||
let backgroundImage = dirSvc.getFile(NS_OSX_PICTURE_DOCUMENTS_DIR, {});
|
||||
backgroundImage.append(uuid + ".png");
|
||||
if (backgroundImage.exists()) {
|
||||
backgroundImage.remove(false);
|
||||
}
|
||||
|
||||
// For simplicity, we're going to reach in and access the image on the
|
||||
// page directly, which means the page shouldn't be running in a remote
|
||||
// browser. Thankfully, about:logo runs in the parent process for now.
|
||||
Assert.ok(
|
||||
!gBrowser.selectedBrowser.isRemoteBrowser,
|
||||
"image can be accessed synchronously from the parent process"
|
||||
);
|
||||
let image = gBrowser.selectedBrowser.contentDocument.images[0];
|
||||
|
||||
info(`Setting/saving desktop background to ${backgroundImage.path}`);
|
||||
|
||||
// Saves the file in ~/Pictures
|
||||
shellSvc.setDesktopBackground(image, 0, backgroundImage.leafName);
|
||||
|
||||
await BrowserTestUtils.waitForCondition(() => backgroundImage.exists());
|
||||
info(`${backgroundImage.path} downloaded`);
|
||||
Assert.ok(
|
||||
FileUtils.File(backgroundImage.path).exists(),
|
||||
`${backgroundImage.path} exists`
|
||||
);
|
||||
|
||||
// Check that the desktop background image is the image we set above.
|
||||
let exitCode = checkDesktopBackgroundCLI(backgroundImage.path);
|
||||
Assert.equal(exitCode, 0, `background should be ${backgroundImage.path}`);
|
||||
|
||||
// Restore the background image to the Mac default.
|
||||
restoreDefaultBackground();
|
||||
|
||||
// We no longer need the downloaded image.
|
||||
backgroundImage.remove(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
101
src/zen/tests/mochitests/shell/browser_420786.js
Normal file
101
src/zen/tests/mochitests/shell/browser_420786.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const DG_BACKGROUND = "/desktop/gnome/background";
|
||||
const DG_IMAGE_KEY = DG_BACKGROUND + "/picture_filename";
|
||||
const DG_OPTION_KEY = DG_BACKGROUND + "/picture_options";
|
||||
const DG_DRAW_BG_KEY = DG_BACKGROUND + "/draw_background";
|
||||
|
||||
const GS_BG_SCHEMA = "org.gnome.desktop.background";
|
||||
const GS_IMAGE_KEY = "picture-uri";
|
||||
const GS_OPTION_KEY = "picture-options";
|
||||
const GS_DRAW_BG_KEY = "draw-background";
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:logo",
|
||||
},
|
||||
() => {
|
||||
var brandName = Services.strings
|
||||
.createBundle("chrome://branding/locale/brand.properties")
|
||||
.GetStringFromName("brandShortName");
|
||||
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(
|
||||
Ci.nsIDirectoryServiceProvider
|
||||
);
|
||||
var homeDir = dirSvc.getFile("Home", {});
|
||||
|
||||
var wpFile = homeDir.clone();
|
||||
wpFile.append(brandName + "_wallpaper.png");
|
||||
|
||||
// Backup the existing wallpaper so that this test doesn't change the user's
|
||||
// settings.
|
||||
var wpFileBackup = homeDir.clone();
|
||||
wpFileBackup.append(brandName + "_wallpaper.png.backup");
|
||||
|
||||
if (wpFileBackup.exists()) {
|
||||
wpFileBackup.remove(false);
|
||||
}
|
||||
|
||||
if (wpFile.exists()) {
|
||||
wpFile.copyTo(null, wpFileBackup.leafName);
|
||||
}
|
||||
|
||||
var shell = Cc["@mozilla.org/browser/shell-service;1"]
|
||||
.getService(Ci.nsIShellService)
|
||||
.QueryInterface(Ci.nsIGNOMEShellService);
|
||||
|
||||
// For simplicity, we're going to reach in and access the image on the
|
||||
// page directly, which means the page shouldn't be running in a remote
|
||||
// browser. Thankfully, about:logo runs in the parent process for now.
|
||||
Assert.ok(
|
||||
!gBrowser.selectedBrowser.isRemoteBrowser,
|
||||
"image can be accessed synchronously from the parent process"
|
||||
);
|
||||
|
||||
var image = content.document.images[0];
|
||||
|
||||
let checkWallpaper, restoreSettings;
|
||||
try {
|
||||
const prevImage = shell.getGSettingsString(GS_BG_SCHEMA, GS_IMAGE_KEY);
|
||||
const prevOption = shell.getGSettingsString(
|
||||
GS_BG_SCHEMA,
|
||||
GS_OPTION_KEY
|
||||
);
|
||||
|
||||
checkWallpaper = function (position, expectedGSettingsPosition) {
|
||||
shell.setDesktopBackground(image, position, "");
|
||||
ok(wpFile.exists(), "Wallpaper was written to disk");
|
||||
is(
|
||||
shell.getGSettingsString(GS_BG_SCHEMA, GS_IMAGE_KEY),
|
||||
encodeURI("file://" + wpFile.path),
|
||||
"Wallpaper file GSettings key is correct"
|
||||
);
|
||||
is(
|
||||
shell.getGSettingsString(GS_BG_SCHEMA, GS_OPTION_KEY),
|
||||
expectedGSettingsPosition,
|
||||
"Wallpaper position GSettings key is correct"
|
||||
);
|
||||
};
|
||||
|
||||
restoreSettings = function () {
|
||||
shell.setGSettingsString(GS_BG_SCHEMA, GS_IMAGE_KEY, prevImage);
|
||||
shell.setGSettingsString(GS_BG_SCHEMA, GS_OPTION_KEY, prevOption);
|
||||
};
|
||||
} catch (e) {}
|
||||
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_TILE, "wallpaper");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_STRETCH, "stretched");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_CENTER, "centered");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "zoom");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_FIT, "scaled");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_SPAN, "spanned");
|
||||
|
||||
restoreSettings();
|
||||
|
||||
// Restore files
|
||||
if (wpFileBackup.exists()) {
|
||||
wpFileBackup.moveTo(null, wpFile.leafName);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
11
src/zen/tests/mochitests/shell/browser_633221.js
Normal file
11
src/zen/tests/mochitests/shell/browser_633221.js
Normal file
@@ -0,0 +1,11 @@
|
||||
function test() {
|
||||
ShellService.setDefaultBrowser(false);
|
||||
ok(
|
||||
ShellService.isDefaultBrowser(true, false),
|
||||
"we got here and are the default browser"
|
||||
);
|
||||
ok(
|
||||
ShellService.isDefaultBrowser(true, true),
|
||||
"we got here and are the default browser"
|
||||
);
|
||||
}
|
||||
218
src/zen/tests/mochitests/shell/browser_createWindowsShortcut.js
Normal file
218
src/zen/tests/mochitests/shell/browser_createWindowsShortcut.js
Normal file
@@ -0,0 +1,218 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs",
|
||||
MockRegistrar: "resource://testing-common/MockRegistrar.sys.mjs",
|
||||
});
|
||||
|
||||
const gBase = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
gBase.append("CreateWindowsShortcut");
|
||||
createDirectory(gBase);
|
||||
|
||||
const gTmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
||||
|
||||
const gDirectoryServiceProvider = {
|
||||
getFile(prop, persistent) {
|
||||
persistent.value = false;
|
||||
|
||||
// We only expect a narrow range of calls.
|
||||
let folder = gBase.clone();
|
||||
switch (prop) {
|
||||
case "Progs":
|
||||
folder.append("Programs");
|
||||
break;
|
||||
case "Desk":
|
||||
folder.append("Desktop");
|
||||
break;
|
||||
case "UpdRootD":
|
||||
// We really want DataRoot, but UpdateSubdir is what we usually get.
|
||||
folder.append("DataRoot");
|
||||
folder.append("UpdateDir");
|
||||
folder.append("UpdateSubdir");
|
||||
break;
|
||||
case "ProfD":
|
||||
// Used by test infrastructure.
|
||||
folder = folder.parent;
|
||||
break;
|
||||
case "TmpD":
|
||||
// Used by FileTestUtils.
|
||||
folder = gTmpDir;
|
||||
break;
|
||||
default:
|
||||
console.error(`Access to unexpected directory '${prop}'`);
|
||||
return Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
createDirectory(folder);
|
||||
return folder;
|
||||
},
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
|
||||
};
|
||||
|
||||
add_setup(() => {
|
||||
Services.dirsvc
|
||||
.QueryInterface(Ci.nsIDirectoryService)
|
||||
.registerProvider(gDirectoryServiceProvider);
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
gBase.remove(true);
|
||||
Services.dirsvc
|
||||
.QueryInterface(Ci.nsIDirectoryService)
|
||||
.unregisterProvider(gDirectoryServiceProvider);
|
||||
});
|
||||
|
||||
add_task(async function test_CreateWindowsShortcut() {
|
||||
const DEST = "browser_createWindowsShortcut_TestFile.lnk";
|
||||
|
||||
const file = FileTestUtils.getTempFile("program.exe");
|
||||
const iconPath = FileTestUtils.getTempFile("program.ico");
|
||||
|
||||
let shortcut;
|
||||
|
||||
const defaults = {
|
||||
shellService: Cc["@mozilla.org/toolkit/shell-service;1"].getService(),
|
||||
targetFile: file,
|
||||
iconFile: iconPath,
|
||||
description: "made by browser_createWindowsShortcut.js",
|
||||
aumid: "TESTTEST",
|
||||
};
|
||||
|
||||
shortcut = Services.dirsvc.get("Progs", Ci.nsIFile);
|
||||
shortcut.append(DEST);
|
||||
await testShortcut({
|
||||
shortcutFile: shortcut,
|
||||
relativePath: DEST,
|
||||
specialFolder: "Programs",
|
||||
logHeader: "STARTMENU",
|
||||
...defaults,
|
||||
});
|
||||
|
||||
let subdir = Services.dirsvc.get("Progs", Ci.nsIFile);
|
||||
subdir.append("Shortcut Test");
|
||||
tryRemove(subdir);
|
||||
|
||||
shortcut = subdir.clone();
|
||||
shortcut.append(DEST);
|
||||
await testShortcut({
|
||||
shortcutFile: shortcut,
|
||||
relativePath: "Shortcut Test\\" + DEST,
|
||||
specialFolder: "Programs",
|
||||
logHeader: "STARTMENU",
|
||||
...defaults,
|
||||
});
|
||||
tryRemove(subdir);
|
||||
|
||||
shortcut = Services.dirsvc.get("Desk", Ci.nsIFile);
|
||||
shortcut.append(DEST);
|
||||
await testShortcut({
|
||||
shortcutFile: shortcut,
|
||||
relativePath: DEST,
|
||||
specialFolder: "Desktop",
|
||||
logHeader: "DESKTOP",
|
||||
...defaults,
|
||||
});
|
||||
});
|
||||
|
||||
async function testShortcut({
|
||||
shortcutFile,
|
||||
relativePath,
|
||||
specialFolder,
|
||||
logHeader,
|
||||
|
||||
// Generally provided by the defaults.
|
||||
shellService,
|
||||
targetFile,
|
||||
iconFile,
|
||||
description,
|
||||
aumid,
|
||||
}) {
|
||||
// If it already exists, remove it.
|
||||
tryRemove(shortcutFile);
|
||||
|
||||
await shellService.createShortcut(
|
||||
targetFile,
|
||||
[],
|
||||
description,
|
||||
iconFile,
|
||||
0,
|
||||
aumid,
|
||||
specialFolder,
|
||||
relativePath
|
||||
);
|
||||
ok(
|
||||
shortcutFile.exists(),
|
||||
`${specialFolder}\\${relativePath}: Shortcut should exist`
|
||||
);
|
||||
ok(
|
||||
queryShortcutLog(relativePath, logHeader),
|
||||
`${specialFolder}\\${relativePath}: Shortcut log entry was added`
|
||||
);
|
||||
await shellService.deleteShortcut(specialFolder, relativePath);
|
||||
ok(
|
||||
!shortcutFile.exists(),
|
||||
`${specialFolder}\\${relativePath}: Shortcut does not exist after deleting`
|
||||
);
|
||||
ok(
|
||||
!queryShortcutLog(relativePath, logHeader),
|
||||
`${specialFolder}\\${relativePath}: Shortcut log entry was removed`
|
||||
);
|
||||
}
|
||||
|
||||
function queryShortcutLog(aShortcutName, aSection) {
|
||||
const parserFactory = Cc[
|
||||
"@mozilla.org/xpcom/ini-parser-factory;1"
|
||||
].createInstance(Ci.nsIINIParserFactory);
|
||||
|
||||
const dir = Services.dirsvc.get("UpdRootD", Ci.nsIFile).parent.parent;
|
||||
const enumerator = dir.directoryEntries;
|
||||
|
||||
for (const file of enumerator) {
|
||||
// We don't know the user's SID from JS-land, so just look at all of them.
|
||||
if (!file.path.match(/[^_]+_S[^_]*_shortcuts.ini/)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parser = parserFactory.createINIParser(file);
|
||||
parser.QueryInterface(Ci.nsIINIParser);
|
||||
parser.QueryInterface(Ci.nsIINIParserWriter);
|
||||
|
||||
for (let i = 0; ; i++) {
|
||||
try {
|
||||
let string = parser.getString(aSection, `Shortcut${i}`);
|
||||
if (string == aShortcutName) {
|
||||
enumerator.close();
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
// The key didn't exist, stop here.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enumerator.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
function createDirectory(aFolder) {
|
||||
try {
|
||||
aFolder.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
|
||||
} catch (e) {
|
||||
if (e.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryRemove(file) {
|
||||
try {
|
||||
file.remove(false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
54
src/zen/tests/mochitests/shell/browser_doesAppNeedPin.js
Normal file
54
src/zen/tests/mochitests/shell/browser_doesAppNeedPin.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusTestUtils: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
});
|
||||
|
||||
let defaultValue;
|
||||
add_task(async function default_need() {
|
||||
defaultValue = await ShellService.doesAppNeedPin();
|
||||
|
||||
Assert.notStrictEqual(
|
||||
defaultValue,
|
||||
undefined,
|
||||
"Got a default app need pin value"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function remote_disable() {
|
||||
if (defaultValue === false) {
|
||||
info("Default pin already false, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: { disablePin: true, enabled: true },
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
await ShellService.doesAppNeedPin(),
|
||||
false,
|
||||
"Pinning disabled via nimbus"
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function restore_default() {
|
||||
if (defaultValue === undefined) {
|
||||
info("No default pin value set, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.equal(
|
||||
await ShellService.doesAppNeedPin(),
|
||||
defaultValue,
|
||||
"Pinning restored to original"
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
// Test all four basic variations of the "screenshot" argument
|
||||
// when a file path is specified.
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
`-screenshot=${screenshotPath}`,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"--screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
`--screenshot=${screenshotPath}`,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
|
||||
// Test when the requested URL redirects
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless_redirect.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
|
||||
// Test with additional command options
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
"-attach-console",
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-attach-console",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
"-headless",
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
add_task(async function () {
|
||||
const cwdScreenshotPath = PathUtils.join(
|
||||
Services.dirsvc.get("CurWorkD", Ci.nsIFile).path,
|
||||
"screenshot.png"
|
||||
);
|
||||
|
||||
// Test variations of the "screenshot" argument when a file path
|
||||
// isn't specified.
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-screenshot",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"--screenshot",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"--screenshot",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
|
||||
// Test with additional command options
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"--screenshot",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-attach-console",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
const cwdScreenshotPath = PathUtils.join(
|
||||
Services.dirsvc.get("CurWorkD", Ci.nsIFile).path,
|
||||
"screenshot.png"
|
||||
);
|
||||
|
||||
// Test invalid URL arguments (either no argument or too many arguments).
|
||||
await testFileCreationNegative(["-screenshot"], cwdScreenshotPath);
|
||||
await testFileCreationNegative(
|
||||
[
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"http://mochi.test:8888/headless.html",
|
||||
"-screenshot",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
|
||||
// Test all four basic variations of the "window-size" argument.
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size",
|
||||
"800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size=800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"--window-size",
|
||||
"800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"--window-size=800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
const cwdScreenshotPath = PathUtils.join(
|
||||
Services.dirsvc.get("CurWorkD", Ci.nsIFile).path,
|
||||
"screenshot.png"
|
||||
);
|
||||
// Test other variations of the "window-size" argument.
|
||||
await testWindowSizePositive(800, 600);
|
||||
await testWindowSizePositive(1234);
|
||||
await testFileCreationNegative(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size",
|
||||
"hello",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationNegative(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size",
|
||||
"800,",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
// Test cross origin iframes work.
|
||||
await testGreen(
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless_cross_origin.html",
|
||||
screenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
// Test when the requested URL redirects
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless_redirect.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
});
|
||||
27
src/zen/tests/mochitests/shell/browser_processAUMID.js
Normal file
27
src/zen/tests/mochitests/shell/browser_processAUMID.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Bug 1950734 tracks how calling PinCurrentAppToTaskbarWin11
|
||||
* on MSIX may cause the process AUMID to be unnecessarily changed.
|
||||
* This test verifies that the behaviour will no longer happen
|
||||
*/
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs",
|
||||
});
|
||||
|
||||
add_task(async function test_processAUMID() {
|
||||
let processAUMID = ShellService.checkCurrentProcessAUMIDForTesting();
|
||||
|
||||
// This function will trigger the relevant code paths that
|
||||
// incorrectly changes the process AUMID on MSIX, prior to
|
||||
// Bug 1950734 being fixed
|
||||
await ShellService.checkPinCurrentAppToTaskbarAsync(false);
|
||||
|
||||
is(
|
||||
processAUMID,
|
||||
ShellService.checkCurrentProcessAUMIDForTesting(),
|
||||
"The process AUMID should not be changed"
|
||||
);
|
||||
});
|
||||
239
src/zen/tests/mochitests/shell/browser_setDefaultBrowser.js
Normal file
239
src/zen/tests/mochitests/shell/browser_setDefaultBrowser.js
Normal file
@@ -0,0 +1,239 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
|
||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusTestUtils: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
sinon: "resource://testing-common/Sinon.sys.mjs",
|
||||
});
|
||||
|
||||
const setDefaultBrowserUserChoiceStub = async () => {
|
||||
throw Components.Exception("", Cr.NS_ERROR_WDBA_NO_PROGID);
|
||||
};
|
||||
|
||||
const defaultAgentStub = sinon
|
||||
.stub(ShellService, "defaultAgent")
|
||||
.value({ setDefaultBrowserUserChoiceAsync: setDefaultBrowserUserChoiceStub });
|
||||
|
||||
const _userChoiceImpossibleTelemetryResultStub = sinon
|
||||
.stub(ShellService, "_userChoiceImpossibleTelemetryResult")
|
||||
.callsFake(() => null);
|
||||
|
||||
const userChoiceStub = sinon
|
||||
.stub(ShellService, "setAsDefaultUserChoice")
|
||||
.resolves();
|
||||
const setDefaultStub = sinon.stub();
|
||||
const shellStub = sinon
|
||||
.stub(ShellService, "shellService")
|
||||
.value({ setDefaultBrowser: setDefaultStub });
|
||||
|
||||
const sendTriggerStub = sinon.stub(ASRouter, "sendTriggerMessage");
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
let defaultUserChoice;
|
||||
add_task(async function need_user_choice() {
|
||||
await ShellService.setDefaultBrowser();
|
||||
defaultUserChoice = userChoiceStub.called;
|
||||
|
||||
Assert.notStrictEqual(
|
||||
defaultUserChoice,
|
||||
undefined,
|
||||
"Decided which default browser method to use"
|
||||
);
|
||||
Assert.equal(
|
||||
setDefaultStub.notCalled,
|
||||
defaultUserChoice,
|
||||
"Only one default behavior was used"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function remote_disable() {
|
||||
if (defaultUserChoice === false) {
|
||||
info("Default behavior already not user choice, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
userChoiceStub.resetHistory();
|
||||
setDefaultStub.resetHistory();
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: false,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(
|
||||
userChoiceStub.notCalled,
|
||||
"Set default with user choice disabled via nimbus"
|
||||
);
|
||||
Assert.ok(setDefaultStub.called, "Used plain set default instead");
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function restore_default() {
|
||||
if (defaultUserChoice === undefined) {
|
||||
info("No default user choice behavior set, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
userChoiceStub.resetHistory();
|
||||
setDefaultStub.resetHistory();
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.equal(
|
||||
userChoiceStub.called,
|
||||
defaultUserChoice,
|
||||
"Set default with user choice restored to original"
|
||||
);
|
||||
Assert.equal(
|
||||
setDefaultStub.notCalled,
|
||||
defaultUserChoice,
|
||||
"Plain set default behavior restored to original"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function ensure_fallback() {
|
||||
if (AppConstants.platform != "win") {
|
||||
info("Nothing to test on non-Windows");
|
||||
return;
|
||||
}
|
||||
|
||||
let userChoicePromise = Promise.resolve();
|
||||
userChoiceStub.callsFake(function (...args) {
|
||||
return (userChoicePromise = userChoiceStub.wrappedMethod.apply(this, args));
|
||||
});
|
||||
userChoiceStub.resetHistory();
|
||||
setDefaultStub.resetHistory();
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandler: false,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(userChoiceStub.called, "Set default with user choice called");
|
||||
|
||||
let message = "";
|
||||
await userChoicePromise.catch(err => (message = err.message || ""));
|
||||
|
||||
Assert.ok(
|
||||
message.includes("ErrExeProgID"),
|
||||
"Set default with user choice threw an expected error"
|
||||
);
|
||||
Assert.ok(setDefaultStub.called, "Fallbacked to plain set default");
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
async function setUpNotificationTests(guidanceEnabled, oneClick) {
|
||||
sinon.reset();
|
||||
const experimentCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultGuidanceNotifications: guidanceEnabled,
|
||||
setDefaultBrowserUserChoice: oneClick,
|
||||
setDefaultBrowserUserChoiceRegRename: oneClick,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
const doCleanup = async () => {
|
||||
await experimentCleanup();
|
||||
sinon.reset();
|
||||
};
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
return doCleanup;
|
||||
}
|
||||
|
||||
add_task(
|
||||
async function show_notification_when_set_to_default_guidance_enabled_and_one_click_disabled() {
|
||||
if (!AppConstants.isPlatformAndVersionAtLeast("win", 10)) {
|
||||
info("Nothing to test on non-Windows or older Windows versions");
|
||||
return;
|
||||
}
|
||||
const doCleanup = await setUpNotificationTests(
|
||||
true, // guidance enabled
|
||||
false // one-click disabled
|
||||
);
|
||||
|
||||
Assert.ok(setDefaultStub.called, "Fallback method used to set default");
|
||||
|
||||
Assert.equal(
|
||||
sendTriggerStub.firstCall.args[0].id,
|
||||
"deeplinkedToWindowsSettingsUI",
|
||||
`Set to default guidance message trigger was sent`
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function do_not_show_notification_when_set_to_default_guidance_disabled_and_one_click_enabled() {
|
||||
if (!AppConstants.isPlatformAndVersionAtLeast("win", 10)) {
|
||||
info("Nothing to test on non-Windows or older Windows versions");
|
||||
return;
|
||||
}
|
||||
|
||||
const doCleanup = await setUpNotificationTests(
|
||||
false, // guidance disabled
|
||||
true // one-click enabled
|
||||
);
|
||||
|
||||
Assert.ok(setDefaultStub.notCalled, "Fallback method not called");
|
||||
|
||||
Assert.equal(
|
||||
sendTriggerStub.callCount,
|
||||
0,
|
||||
`Set to default guidance message trigger was not sent`
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function do_not_show_notification_when_set_to_default_guidance_enabled_and_one_click_enabled() {
|
||||
if (!AppConstants.isPlatformAndVersionAtLeast("win", 10)) {
|
||||
info("Nothing to test on non-Windows or older Windows versions");
|
||||
return;
|
||||
}
|
||||
|
||||
const doCleanup = await setUpNotificationTests(
|
||||
true, // guidance enabled
|
||||
true // one-click enabled
|
||||
);
|
||||
|
||||
Assert.ok(setDefaultStub.notCalled, "Fallback method not called");
|
||||
Assert.equal(
|
||||
sendTriggerStub.callCount,
|
||||
0,
|
||||
`Set to default guidance message trigger was not sent`
|
||||
);
|
||||
await doCleanup();
|
||||
}
|
||||
);
|
||||
279
src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js
Normal file
279
src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js
Normal file
@@ -0,0 +1,279 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusTestUtils: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
sinon: "resource://testing-common/Sinon.sys.mjs",
|
||||
});
|
||||
|
||||
const setDefaultBrowserUserChoiceStub = sinon.stub();
|
||||
const setDefaultExtensionHandlersUserChoiceStub = sinon
|
||||
.stub()
|
||||
.callsFake(() => Promise.resolve());
|
||||
|
||||
const defaultAgentStub = sinon.stub(ShellService, "defaultAgent").value({
|
||||
setDefaultBrowserUserChoiceAsync: setDefaultBrowserUserChoiceStub,
|
||||
setDefaultExtensionHandlersUserChoice:
|
||||
setDefaultExtensionHandlersUserChoiceStub,
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"XreDirProvider",
|
||||
"@mozilla.org/xre/directory-provider;1",
|
||||
"nsIXREDirProvider"
|
||||
);
|
||||
|
||||
const _userChoiceImpossibleTelemetryResultStub = sinon
|
||||
.stub(ShellService, "_userChoiceImpossibleTelemetryResult")
|
||||
.callsFake(() => null);
|
||||
|
||||
// Ensure we don't fall back to a real implementation.
|
||||
const setDefaultStub = sinon.stub();
|
||||
// We'll dynamically update this as needed during the tests.
|
||||
const queryCurrentDefaultHandlerForStub = sinon.stub();
|
||||
const shellStub = sinon.stub(ShellService, "shellService").value({
|
||||
setDefaultBrowser: setDefaultStub,
|
||||
queryCurrentDefaultHandlerFor: queryCurrentDefaultHandlerForStub,
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
defaultAgentStub.restore();
|
||||
_userChoiceImpossibleTelemetryResultStub.restore();
|
||||
shellStub.restore();
|
||||
});
|
||||
|
||||
add_task(async function ready() {
|
||||
await ExperimentAPI.ready();
|
||||
});
|
||||
|
||||
// Everything here is Windows.
|
||||
Assert.equal(AppConstants.platform, "win", "Platform is Windows");
|
||||
|
||||
add_task(async function remoteEnableWithPDF() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandlerOnlyReplaceBrowsers: false,
|
||||
setDefaultPDFHandler: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
true
|
||||
);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(setDefaultBrowserUserChoiceStub.firstCall.args, [
|
||||
aumi,
|
||||
[".pdf", "FirefoxPDF"],
|
||||
]);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function remoteEnableWithPDF_testOnlyReplaceBrowsers() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandlerOnlyReplaceBrowsers: true,
|
||||
setDefaultPDFHandler: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable(
|
||||
"setDefaultPDFHandlerOnlyReplaceBrowsers"
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
|
||||
// We'll take the default from a missing association or a known browser.
|
||||
for (let progId of ["", "MSEdgePDF"]) {
|
||||
queryCurrentDefaultHandlerForStub.callsFake(() => progId);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(
|
||||
setDefaultBrowserUserChoiceStub.firstCall.args,
|
||||
[aumi, [".pdf", "FirefoxPDF"]],
|
||||
`Will take default from missing association or known browser with ProgID '${progId}'`
|
||||
);
|
||||
}
|
||||
|
||||
// But not from a non-browser.
|
||||
queryCurrentDefaultHandlerForStub.callsFake(() => "Acrobat.Document.DC");
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(
|
||||
setDefaultBrowserUserChoiceStub.firstCall.args,
|
||||
[aumi, []],
|
||||
`Will not take default from non-browser`
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function remoteEnableWithoutPDF() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandler: false,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
false
|
||||
);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(setDefaultBrowserUserChoiceStub.firstCall.args, [aumi, []]);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function remoteDisable() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: false,
|
||||
setDefaultPDFHandler: true,
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
false
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
true
|
||||
);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.notCalled);
|
||||
Assert.ok(setDefaultStub.called);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
const expectedArguments = [aumi, [".pdf", "FirefoxPDF"]];
|
||||
|
||||
try {
|
||||
const pdfHandlerResult = { registered: true, knownBrowser: true };
|
||||
sandbox
|
||||
.stub(ShellService, "getDefaultPDFHandler")
|
||||
.returns(pdfHandlerResult);
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
pdfHandlerResult.knownBrowser = false;
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.notCalled,
|
||||
"Did not call default browser agent"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
} finally {
|
||||
sandbox.restore();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check whether the preview image for setDesktopBackground is rendered
|
||||
* correctly, without stretching
|
||||
*/
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["test.wait300msAfterTabSwitch", true]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:logo",
|
||||
},
|
||||
async () => {
|
||||
const dialogLoad = BrowserTestUtils.domWindowOpened(null, async win => {
|
||||
await BrowserTestUtils.waitForEvent(win, "load");
|
||||
Assert.equal(
|
||||
win.document.documentElement.getAttribute("windowtype"),
|
||||
"Shell:SetDesktopBackground",
|
||||
"Opened correct window"
|
||||
);
|
||||
return true;
|
||||
});
|
||||
|
||||
const image = content.document.images[0];
|
||||
EventUtils.synthesizeMouseAtCenter(image, { type: "contextmenu" });
|
||||
|
||||
const menu = document.getElementById("contentAreaContextMenu");
|
||||
await BrowserTestUtils.waitForPopupEvent(menu, "shown");
|
||||
const menuClosed = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
|
||||
|
||||
const menuItem = document.getElementById("context-setDesktopBackground");
|
||||
try {
|
||||
menu.activateItem(menuItem);
|
||||
} catch (ex) {
|
||||
ok(
|
||||
menuItem.hidden,
|
||||
"should only fail to activate when menu item is hidden"
|
||||
);
|
||||
ok(
|
||||
!ShellService.canSetDesktopBackground,
|
||||
"Should only hide when not able to set the desktop background"
|
||||
);
|
||||
is(
|
||||
AppConstants.platform,
|
||||
"linux",
|
||||
"Should always be able to set desktop background on non-linux platforms"
|
||||
);
|
||||
todo(false, "Skipping test on this configuration");
|
||||
|
||||
menu.hidePopup();
|
||||
await menuClosed;
|
||||
return;
|
||||
}
|
||||
|
||||
await menuClosed;
|
||||
|
||||
const win = await dialogLoad;
|
||||
|
||||
/* setDesktopBackground.js does a setTimeout to wait for correct
|
||||
dimensions. If we don't wait here we could read the preview dimensions
|
||||
before they're changed to match the screen */
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
const canvas = win.document.getElementById("screen");
|
||||
const screenRatio = screen.width / screen.height;
|
||||
const previewRatio = canvas.clientWidth / canvas.clientHeight;
|
||||
|
||||
info(`Screen dimensions are ${screen.width}x${screen.height}`);
|
||||
info(`Screen's raw ratio is ${screenRatio}`);
|
||||
info(
|
||||
`Preview dimensions are ${canvas.clientWidth}x${canvas.clientHeight}`
|
||||
);
|
||||
info(`Preview's raw ratio is ${previewRatio}`);
|
||||
|
||||
Assert.ok(
|
||||
previewRatio < screenRatio + 0.01 && previewRatio > screenRatio - 0.01,
|
||||
"Preview's aspect ratio is within ±.01 of screen's"
|
||||
);
|
||||
|
||||
win.close();
|
||||
|
||||
await menuClosed;
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Windows11LimitedAccessFeatures.h"
|
||||
#include "WinUtils.h"
|
||||
|
||||
TEST(LimitedAccessFeature, VerifyGeneratedInfo)
|
||||
{
|
||||
// If running on MSIX we have no guarantee that the
|
||||
// generated LAF info will match the known values.
|
||||
if (mozilla::widget::WinUtils::HasPackageIdentity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LimitedAccessFeatureInfo knownLafInfo = {
|
||||
// Win11LimitedAccessFeatureType::Taskbar
|
||||
"Win11LimitedAccessFeatureType::Taskbar"_ns, // debugName
|
||||
u"com.microsoft.windows.taskbar.pin"_ns, // feature
|
||||
u"kRFiWpEK5uS6PMJZKmR7MQ=="_ns, // token
|
||||
u"pcsmm0jrprpb2 has registered their use of "_ns // attestation
|
||||
u"com.microsoft.windows.taskbar.pin with Microsoft and agrees to the "_ns
|
||||
u"terms "_ns
|
||||
u"of use."_ns};
|
||||
|
||||
auto generatedLafInfoResult = GenerateLimitedAccessFeatureInfo(
|
||||
"Win11LimitedAccessFeatureType::Taskbar"_ns,
|
||||
u"com.microsoft.windows.taskbar.pin"_ns);
|
||||
ASSERT_TRUE(generatedLafInfoResult.isOk());
|
||||
LimitedAccessFeatureInfo generatedLafInfo = generatedLafInfoResult.unwrap();
|
||||
|
||||
// Check for equality between generated values and known good values
|
||||
ASSERT_TRUE(knownLafInfo.debugName.Equals(generatedLafInfo.debugName));
|
||||
ASSERT_TRUE(knownLafInfo.feature.Equals(generatedLafInfo.feature));
|
||||
ASSERT_TRUE(knownLafInfo.token.Equals(generatedLafInfo.token));
|
||||
ASSERT_TRUE(knownLafInfo.attestation.Equals(generatedLafInfo.attestation));
|
||||
}
|
||||
54
src/zen/tests/mochitests/shell/gtest/ShellLinkTests.cpp
Normal file
54
src/zen/tests/mochitests/shell/gtest/ShellLinkTests.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsWindowsShellServiceInternal.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
TEST(ShellLink, NarrowCharacterArguments)
|
||||
{
|
||||
nsCOMPtr<nsIFile> exe;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(exe));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
RefPtr<IShellLinkW> link;
|
||||
rv = CreateShellLinkObject(exe, {u"test"_ns}, u"test"_ns, exe, 0, u"aumid"_ns,
|
||||
getter_AddRefs(link));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_TRUE(link != nullptr);
|
||||
|
||||
std::wstring testArgs = L"\"test\" ";
|
||||
|
||||
wchar_t resultArgs[sizeof(testArgs)];
|
||||
HRESULT hr = link->GetArguments(resultArgs, sizeof(resultArgs));
|
||||
ASSERT_TRUE(SUCCEEDED(hr));
|
||||
|
||||
ASSERT_TRUE(testArgs == resultArgs);
|
||||
}
|
||||
|
||||
TEST(ShellLink, WideCharacterArguments)
|
||||
{
|
||||
nsCOMPtr<nsIFile> exe;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(exe));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
RefPtr<IShellLinkW> link;
|
||||
rv = CreateShellLinkObject(exe, {u"Test\\テスト用アカウント\\Test"_ns},
|
||||
u"test"_ns, exe, 0, u"aumid"_ns,
|
||||
getter_AddRefs(link));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_TRUE(link != nullptr);
|
||||
|
||||
std::wstring testArgs = L"\"Test\\テスト用アカウント\\Test\" ";
|
||||
|
||||
wchar_t resultArgs[sizeof(testArgs)];
|
||||
HRESULT hr = link->GetArguments(resultArgs, sizeof(resultArgs));
|
||||
ASSERT_TRUE(SUCCEEDED(hr));
|
||||
|
||||
ASSERT_TRUE(testArgs == resultArgs);
|
||||
}
|
||||
15
src/zen/tests/mochitests/shell/gtest/moz.build
Normal file
15
src/zen/tests/mochitests/shell/gtest/moz.build
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
|
||||
LOCAL_INCLUDES += ["/browser/components/shell"]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"LimitedAccessFeatureTests.cpp",
|
||||
"ShellLinkTests.cpp",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
||||
159
src/zen/tests/mochitests/shell/head.js
Normal file
159
src/zen/tests/mochitests/shell/head.js
Normal file
@@ -0,0 +1,159 @@
|
||||
"use strict";
|
||||
|
||||
const { Subprocess } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/Subprocess.sys.mjs"
|
||||
);
|
||||
|
||||
const TEMP_DIR = Services.dirsvc.get("TmpD", Ci.nsIFile).path;
|
||||
|
||||
const screenshotPath = PathUtils.join(TEMP_DIR, "headless_test_screenshot.png");
|
||||
|
||||
async function runFirefox(args) {
|
||||
const XRE_EXECUTABLE_FILE = "XREExeF";
|
||||
const firefoxExe = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).path;
|
||||
const NS_APP_PREFS_50_FILE = "PrefF";
|
||||
const mochiPrefsFile = Services.dirsvc.get(NS_APP_PREFS_50_FILE, Ci.nsIFile);
|
||||
const mochiPrefsPath = mochiPrefsFile.path;
|
||||
const mochiPrefsName = mochiPrefsFile.leafName;
|
||||
const profilePath = PathUtils.join(
|
||||
TEMP_DIR,
|
||||
"headless_test_screenshot_profile"
|
||||
);
|
||||
const prefsPath = PathUtils.join(profilePath, mochiPrefsName);
|
||||
const firefoxArgs = ["-profile", profilePath];
|
||||
|
||||
await IOUtils.makeDirectory(profilePath);
|
||||
await IOUtils.copy(mochiPrefsPath, prefsPath);
|
||||
let proc = await Subprocess.call({
|
||||
command: firefoxExe,
|
||||
arguments: firefoxArgs.concat(args),
|
||||
// Disable leak detection to avoid intermittent failure bug 1331152.
|
||||
environmentAppend: true,
|
||||
environment: {
|
||||
ASAN_OPTIONS:
|
||||
"detect_leaks=0:quarantine_size=50331648:malloc_context_size=5",
|
||||
// Don't enable Marionette.
|
||||
MOZ_MARIONETTE: null,
|
||||
},
|
||||
});
|
||||
let stdout;
|
||||
while ((stdout = await proc.stdout.readString())) {
|
||||
dump(`>>> ${stdout}\n`);
|
||||
}
|
||||
let { exitCode } = await proc.wait();
|
||||
is(exitCode, 0, "Firefox process should exit with code 0");
|
||||
await IOUtils.remove(profilePath, { recursive: true });
|
||||
}
|
||||
|
||||
async function testFileCreationPositive(args, path) {
|
||||
await runFirefox(args);
|
||||
|
||||
let saved = IOUtils.exists(path);
|
||||
ok(saved, "A screenshot should be saved as " + path);
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
let info = await IOUtils.stat(path);
|
||||
Assert.greater(info.size, 0, "Screenshot should not be an empty file");
|
||||
await IOUtils.remove(path);
|
||||
}
|
||||
|
||||
async function testFileCreationNegative(args, path) {
|
||||
await runFirefox(args);
|
||||
|
||||
let saved = await IOUtils.exists(path);
|
||||
ok(!saved, "A screenshot should not be saved");
|
||||
await IOUtils.remove(path);
|
||||
}
|
||||
|
||||
async function testWindowSizePositive(width, height) {
|
||||
let size = String(width);
|
||||
if (height) {
|
||||
size += "," + height;
|
||||
}
|
||||
|
||||
await runFirefox([
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
"-window-size",
|
||||
size,
|
||||
]);
|
||||
|
||||
let saved = await IOUtils.exists(screenshotPath);
|
||||
ok(saved, "A screenshot should be saved in the tmp directory");
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = await IOUtils.read(screenshotPath);
|
||||
await new Promise(resolve => {
|
||||
let blob = new Blob([data], { type: "image/png" });
|
||||
let reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
let screenshot = new Image();
|
||||
screenshot.onload = function () {
|
||||
is(
|
||||
screenshot.width,
|
||||
width,
|
||||
"Screenshot should be " + width + " pixels wide"
|
||||
);
|
||||
if (height) {
|
||||
is(
|
||||
screenshot.height,
|
||||
height,
|
||||
"Screenshot should be " + height + " pixels tall"
|
||||
);
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
screenshot.src = reader.result;
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
await IOUtils.remove(screenshotPath);
|
||||
}
|
||||
|
||||
async function testGreen(url, path) {
|
||||
await runFirefox(["-url", url, `--screenshot=${path}`]);
|
||||
|
||||
let saved = await IOUtils.exists(path);
|
||||
ok(saved, "A screenshot should be saved in the tmp directory");
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = await IOUtils.read(path);
|
||||
let image = await new Promise(resolve => {
|
||||
let blob = new Blob([data], { type: "image/png" });
|
||||
let reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
let screenshot = new Image();
|
||||
screenshot.onload = function () {
|
||||
resolve(screenshot);
|
||||
};
|
||||
screenshot.src = reader.result;
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(image, 0, 0);
|
||||
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
let rgba = imageData.data;
|
||||
|
||||
let found = false;
|
||||
for (let i = 0; i < rgba.length; i += 4) {
|
||||
if (rgba[i] === 0 && rgba[i + 1] === 255 && rgba[i + 2] === 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok(found, "There should be a green pixel in the screenshot.");
|
||||
|
||||
await IOUtils.remove(path);
|
||||
}
|
||||
6
src/zen/tests/mochitests/shell/headless.html
Normal file
6
src/zen/tests/mochitests/shell/headless.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></head>
|
||||
<body style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 255)">
|
||||
Hi
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></head>
|
||||
<body>
|
||||
<iframe width="300" height="200" src="http://example.com/browser/browser/components/shell/test/headless_iframe.html"></iframe>
|
||||
Hi
|
||||
</body>
|
||||
</html>
|
||||
6
src/zen/tests/mochitests/shell/headless_iframe.html
Normal file
6
src/zen/tests/mochitests/shell/headless_iframe.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></head>
|
||||
<body style="background-color: rgb(0, 255, 0);">
|
||||
Hi
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,2 @@
|
||||
HTTP 302 Moved Temporarily
|
||||
Location: headless.html
|
||||
168
src/zen/tests/mochitests/shell/mac_desktop_image.py
Executable file
168
src/zen/tests/mochitests/shell/mac_desktop_image.py
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/python
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"""
|
||||
mac_desktop_image.py
|
||||
|
||||
Mac-specific utility to get/set the desktop background image or check that
|
||||
the current background image path matches a provided path.
|
||||
|
||||
Depends on Objective-C python binding imports which are in the python import
|
||||
paths by default when using macOS's /usr/bin/python.
|
||||
|
||||
Includes generous amount of logging to aid debugging for use in automated tests.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
#
|
||||
# These Objective-C bindings imports are included in the import path by default
|
||||
# for the Mac-bundled python installed in /usr/bin/python. They're needed to
|
||||
# call the Objective-C API's to set and retrieve the current desktop background
|
||||
# image.
|
||||
#
|
||||
from AppKit import NSScreen, NSWorkspace
|
||||
from Cocoa import NSURL
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Utility to print, set, or "
|
||||
+ "check the path to image being used as "
|
||||
+ "the desktop background image. By "
|
||||
+ "default, prints the path to the "
|
||||
+ "current desktop background image."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="print verbose debugging information",
|
||||
default=False,
|
||||
)
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"-s",
|
||||
"--set-background-image",
|
||||
dest="newBackgroundImagePath",
|
||||
required=False,
|
||||
help="path to the new background image to set. A zero "
|
||||
+ "exit code indicates no errors occurred.",
|
||||
default=None,
|
||||
)
|
||||
group.add_argument(
|
||||
"-c",
|
||||
"--check-background-image",
|
||||
dest="checkBackgroundImagePath",
|
||||
required=False,
|
||||
help="check if the provided background image path "
|
||||
+ "matches the provided path. A zero exit code "
|
||||
+ "indicates the paths match.",
|
||||
default=None,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Using logging for verbose output
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.CRITICAL)
|
||||
logger = logging.getLogger("desktopImage")
|
||||
|
||||
# Print what we're going to do
|
||||
if args.checkBackgroundImagePath is not None:
|
||||
logger.debug(
|
||||
"checking provided desktop image %s matches current "
|
||||
"image" % args.checkBackgroundImagePath
|
||||
)
|
||||
elif args.newBackgroundImagePath is not None:
|
||||
logger.debug("setting image to %s " % args.newBackgroundImagePath)
|
||||
else:
|
||||
logger.debug("retrieving desktop image path")
|
||||
|
||||
focussedScreen = NSScreen.mainScreen()
|
||||
if not focussedScreen:
|
||||
raise RuntimeError("mainScreen error")
|
||||
|
||||
ws = NSWorkspace.sharedWorkspace()
|
||||
if not ws:
|
||||
raise RuntimeError("sharedWorkspace error")
|
||||
|
||||
# If we're just checking the image path, check it and then return.
|
||||
# A successful exit code (0) indicates the paths match.
|
||||
if args.checkBackgroundImagePath is not None:
|
||||
# Get existing desktop image path and resolve it
|
||||
existingImageURL = getCurrentDesktopImageURL(focussedScreen, ws, logger)
|
||||
existingImagePath = existingImageURL.path()
|
||||
existingImagePathReal = os.path.realpath(existingImagePath)
|
||||
logger.debug("existing desktop image: %s" % existingImagePath)
|
||||
logger.debug("existing desktop image realpath: %s" % existingImagePath)
|
||||
|
||||
# Resolve the path we're going to check
|
||||
checkImagePathReal = os.path.realpath(args.checkBackgroundImagePath)
|
||||
logger.debug("check desktop image: %s" % args.checkBackgroundImagePath)
|
||||
logger.debug("check desktop image realpath: %s" % checkImagePathReal)
|
||||
|
||||
if existingImagePathReal == checkImagePathReal:
|
||||
print("desktop image path matches provided path")
|
||||
return True
|
||||
|
||||
print("desktop image path does NOT match provided path")
|
||||
return False
|
||||
|
||||
# Log the current desktop image
|
||||
if args.verbose:
|
||||
existingImageURL = getCurrentDesktopImageURL(focussedScreen, ws, logger)
|
||||
logger.debug("existing desktop image: %s" % existingImageURL.path())
|
||||
|
||||
# Set the desktop image
|
||||
if args.newBackgroundImagePath is not None:
|
||||
newImagePath = args.newBackgroundImagePath
|
||||
if not os.path.exists(newImagePath):
|
||||
logger.critical("%s does not exist" % newImagePath)
|
||||
return False
|
||||
if not os.access(newImagePath, os.R_OK):
|
||||
logger.critical("%s is not readable" % newImagePath)
|
||||
return False
|
||||
|
||||
logger.debug("new desktop image to set: %s" % newImagePath)
|
||||
newImageURL = NSURL.fileURLWithPath_(newImagePath)
|
||||
logger.debug("new desktop image URL to set: %s" % newImageURL)
|
||||
|
||||
status = False
|
||||
(status, error) = ws.setDesktopImageURL_forScreen_options_error_(
|
||||
newImageURL, focussedScreen, None, None
|
||||
)
|
||||
if not status:
|
||||
raise RuntimeError("setDesktopImageURL error")
|
||||
|
||||
# Print the current desktop image
|
||||
imageURL = getCurrentDesktopImageURL(focussedScreen, ws, logger)
|
||||
imagePath = imageURL.path()
|
||||
imagePathReal = os.path.realpath(imagePath)
|
||||
logger.debug("updated desktop image URL: %s" % imageURL)
|
||||
logger.debug("updated desktop image path: %s" % imagePath)
|
||||
logger.debug("updated desktop image path (resolved): %s" % imagePathReal)
|
||||
print(imagePathReal)
|
||||
return True
|
||||
|
||||
|
||||
def getCurrentDesktopImageURL(focussedScreen, workspace, logger):
|
||||
imageURL = workspace.desktopImageURLForScreen_(focussedScreen)
|
||||
if not imageURL:
|
||||
raise RuntimeError("desktopImageURLForScreen returned invalid URL")
|
||||
if not imageURL.isFileURL():
|
||||
logger.warning("desktop image URL is not a file URL")
|
||||
return imageURL
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not main():
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test the macOS ShowSecurityPreferences shell service method.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
|
||||
const { AppConstants } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
|
||||
function killSystemPreferences() {
|
||||
let killallFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
killallFile.initWithPath("/usr/bin/killall");
|
||||
let sysPrefsArg = ["System Preferences"];
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("macosx", 22)) {
|
||||
sysPrefsArg = ["System Settings"];
|
||||
}
|
||||
let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
process.init(killallFile);
|
||||
process.run(true, sysPrefsArg, 1);
|
||||
return process.exitValue;
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
info("Ensure System Preferences isn't already running");
|
||||
killSystemPreferences();
|
||||
});
|
||||
|
||||
add_task(async function test_prefsOpen() {
|
||||
let shellSvc = Cc["@mozilla.org/browser/shell-service;1"].getService(
|
||||
Ci.nsIMacShellService
|
||||
);
|
||||
shellSvc.showSecurityPreferences("Privacy_AllFiles");
|
||||
|
||||
equal(killSystemPreferences(), 0, "Ensure System Preferences was started");
|
||||
});
|
||||
6
src/zen/tests/mochitests/shell/unit/xpcshell.toml
Normal file
6
src/zen/tests/mochitests/shell/unit/xpcshell.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
firefox-appdir = "browser"
|
||||
tags = "os_integration"
|
||||
|
||||
["test_macOS_showSecurityPreferences.js"]
|
||||
run-if = ["os == 'mac'"]
|
||||
17
src/zen/tests/mochitests/tooltiptext/browser.toml
Normal file
17
src/zen/tests/mochitests/tooltiptext/browser.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[DEFAULT]
|
||||
|
||||
["browser_bug329212.js"]
|
||||
support-files = ["title_test.svg"]
|
||||
|
||||
["browser_bug331772_xul_tooltiptext_in_html.js"]
|
||||
support-files = ["xul_tooltiptext.xhtml"]
|
||||
|
||||
["browser_bug561623.js"]
|
||||
|
||||
["browser_bug581947.js"]
|
||||
|
||||
["browser_input_file_tooltips.js"]
|
||||
|
||||
["browser_nac_tooltip.js"]
|
||||
|
||||
["browser_shadow_dom_tooltip.js"]
|
||||
48
src/zen/tests/mochitests/tooltiptext/browser_bug329212.js
Normal file
48
src/zen/tests/mochitests/tooltiptext/browser_bug329212.js
Normal file
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "http://mochi.test:8888/browser/toolkit/components/tooltiptext/tests/title_test.svg",
|
||||
},
|
||||
async function (browser) {
|
||||
await SpecialPowers.spawn(browser, [""], function () {
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
function checkElement(id, expectedTooltipText) {
|
||||
let el = content.document.getElementById(id);
|
||||
let textObj = {};
|
||||
let shouldHaveTooltip = expectedTooltipText !== null;
|
||||
is(
|
||||
tttp.getNodeText(el, textObj, {}),
|
||||
shouldHaveTooltip,
|
||||
"element " +
|
||||
id +
|
||||
" should " +
|
||||
(shouldHaveTooltip ? "" : "not ") +
|
||||
"have a tooltip"
|
||||
);
|
||||
if (shouldHaveTooltip) {
|
||||
is(
|
||||
textObj.value,
|
||||
expectedTooltipText,
|
||||
"element " + id + " should have the right tooltip text"
|
||||
);
|
||||
}
|
||||
}
|
||||
checkElement("svg1", "This is a non-root SVG element title");
|
||||
checkElement("text1", "\n\n\n This is a title\n\n ");
|
||||
checkElement("text2", null);
|
||||
checkElement("text3", null);
|
||||
checkElement("link1", "\n This is a title\n ");
|
||||
checkElement("text4", "\n This is a title\n ");
|
||||
checkElement("link2", null);
|
||||
checkElement("link3", "This is an xlink:title attribute");
|
||||
checkElement("link4", "This is an xlink:title attribute");
|
||||
checkElement("text5", null);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Tests that the tooltiptext attribute is used for XUL elements in an HTML doc.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await SpecialPowers.pushPermissions([
|
||||
{ type: "allowXULXBL", allow: true, context: "http://mochi.test:8888" },
|
||||
]);
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "http://mochi.test:8888/browser/toolkit/components/tooltiptext/tests/xul_tooltiptext.xhtml",
|
||||
},
|
||||
async function (browser) {
|
||||
await SpecialPowers.spawn(browser, [""], function () {
|
||||
let textObj = {};
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
let xulToolbarButton =
|
||||
content.document.getElementById("xulToolbarButton");
|
||||
ok(
|
||||
tttp.getNodeText(xulToolbarButton, textObj, {}),
|
||||
"should get tooltiptext"
|
||||
);
|
||||
is(textObj.value, "XUL tooltiptext");
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
33
src/zen/tests/mochitests/tooltiptext/browser_bug561623.js
Normal file
33
src/zen/tests/mochitests/tooltiptext/browser_bug561623.js
Normal file
@@ -0,0 +1,33 @@
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "data:text/html,<!DOCTYPE html><html><body><input id='i'></body></html>",
|
||||
},
|
||||
async function (browser) {
|
||||
await SpecialPowers.spawn(browser, [""], function () {
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
let i = content.document.getElementById("i");
|
||||
|
||||
ok(
|
||||
!tttp.getNodeText(i, {}, {}),
|
||||
"No tooltip should be shown when @title is null"
|
||||
);
|
||||
|
||||
i.title = "foo";
|
||||
ok(
|
||||
tttp.getNodeText(i, {}, {}),
|
||||
"A tooltip should be shown when @title is not the empty string"
|
||||
);
|
||||
|
||||
i.pattern = "bar";
|
||||
ok(
|
||||
tttp.getNodeText(i, {}, {}),
|
||||
"A tooltip should be shown when @title is not the empty string"
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
107
src/zen/tests/mochitests/tooltiptext/browser_bug581947.js
Normal file
107
src/zen/tests/mochitests/tooltiptext/browser_bug581947.js
Normal file
@@ -0,0 +1,107 @@
|
||||
function check(aBrowser, aElementName, aBarred, aType) {
|
||||
return SpecialPowers.spawn(
|
||||
aBrowser,
|
||||
[[aElementName, aBarred, aType]],
|
||||
async function ([aElementName, aBarred, aType]) {
|
||||
let e = content.document.createElement(aElementName);
|
||||
let contentElement = content.document.getElementById("content");
|
||||
contentElement.appendChild(e);
|
||||
|
||||
if (aType) {
|
||||
e.type = aType;
|
||||
}
|
||||
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown when the element is valid"
|
||||
);
|
||||
|
||||
e.setCustomValidity("foo");
|
||||
if (aBarred) {
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown when the element is barred from constraint validation"
|
||||
);
|
||||
} else {
|
||||
ok(
|
||||
tttp.getNodeText(e, {}, {}),
|
||||
e.tagName + " A tooltip should be shown when the element isn't valid"
|
||||
);
|
||||
}
|
||||
|
||||
e.setAttribute("title", "");
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown if the title attribute is set"
|
||||
);
|
||||
|
||||
e.removeAttribute("title");
|
||||
contentElement.setAttribute("novalidate", "");
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown if the novalidate attribute is set on the form owner"
|
||||
);
|
||||
contentElement.removeAttribute("novalidate");
|
||||
|
||||
e.remove();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function todo_check(aBrowser, aElementName, aBarred) {
|
||||
return SpecialPowers.spawn(
|
||||
aBrowser,
|
||||
[[aElementName, aBarred]],
|
||||
async function ([aElementName]) {
|
||||
let e = content.document.createElement(aElementName);
|
||||
let contentElement = content.document.getElementById("content");
|
||||
contentElement.appendChild(e);
|
||||
|
||||
let caught = false;
|
||||
try {
|
||||
e.setCustomValidity("foo");
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
|
||||
todo(!caught, "setCustomValidity should exist for " + aElementName);
|
||||
|
||||
e.remove();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "data:text/html,<!DOCTYPE html><html><body><form id='content'></form></body></html>",
|
||||
},
|
||||
async function (browser) {
|
||||
let testData = [
|
||||
/* element name, barred */
|
||||
["input", false, null],
|
||||
["textarea", false, null],
|
||||
["button", true, "button"],
|
||||
["button", false, "submit"],
|
||||
["select", false, null],
|
||||
["output", true, null],
|
||||
["fieldset", true, null],
|
||||
["object", true, null],
|
||||
];
|
||||
|
||||
for (let data of testData) {
|
||||
await check(browser, data[0], data[1], data[2]);
|
||||
}
|
||||
|
||||
let todo_testData = [["keygen", "false"]];
|
||||
|
||||
for (let data of todo_testData) {
|
||||
await todo_check(browser, data[0], data[1]);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
let tempFile;
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({ set: [["ui.tooltip.delay_ms", 0]] });
|
||||
tempFile = createTempFile();
|
||||
registerCleanupFunction(function () {
|
||||
tempFile.remove(true);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_singlefile_selected() {
|
||||
await do_test({ value: true, result: "testfile_bug1251809" });
|
||||
});
|
||||
|
||||
add_task(async function test_title_set() {
|
||||
await do_test({ title: "foo", result: "foo" });
|
||||
});
|
||||
|
||||
add_task(async function test_nofile_selected() {
|
||||
await do_test({ result: "No file selected." });
|
||||
});
|
||||
|
||||
add_task(async function test_multipleset_nofile_selected() {
|
||||
await do_test({ multiple: true, result: "No files selected." });
|
||||
});
|
||||
|
||||
add_task(async function test_requiredset() {
|
||||
await do_test({ required: true, result: "Please select a file." });
|
||||
});
|
||||
|
||||
async function do_test(test) {
|
||||
info(`starting test ${JSON.stringify(test)}`);
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating input field");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [test], async function (test) {
|
||||
let doc = content.document;
|
||||
let input = doc.createElement("input");
|
||||
doc.body.appendChild(input);
|
||||
input.id = "test_input";
|
||||
input.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
input.type = "file";
|
||||
if (test.title) {
|
||||
input.setAttribute("title", test.title);
|
||||
}
|
||||
if (test.multiple) {
|
||||
input.multiple = true;
|
||||
}
|
||||
if (test.required) {
|
||||
input.required = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (test.value) {
|
||||
info("Creating mock filepicker to select files");
|
||||
let MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window.browsingContext);
|
||||
MockFilePicker.returnValue = MockFilePicker.returnOK;
|
||||
MockFilePicker.displayDirectory = FileUtils.getDir("TmpD", []);
|
||||
MockFilePicker.setFiles([tempFile]);
|
||||
MockFilePicker.afterOpenCallback = MockFilePicker.cleanup;
|
||||
|
||||
try {
|
||||
// Open the File Picker dialog (MockFilePicker) to select
|
||||
// the files for the test.
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#test_input",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
info("Waiting for the input to have the requisite files");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let input = content.document.querySelector("#test_input");
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => input.files.length,
|
||||
"The input should have at least one file selected"
|
||||
);
|
||||
info(`The input has ${input.files.length} file(s) selected.`);
|
||||
});
|
||||
} catch (e) {}
|
||||
} else {
|
||||
info("No real file selection required.");
|
||||
}
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
test.result,
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
function createTempFile() {
|
||||
let file = FileUtils.getDir("TmpD", []);
|
||||
file.append("testfile_bug1251809");
|
||||
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
|
||||
return file;
|
||||
}
|
||||
66
src/zen/tests/mochitests/tooltiptext/browser_nac_tooltip.js
Normal file
66
src/zen/tests/mochitests/tooltiptext/browser_nac_tooltip.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({ set: [["ui.tooltip.delay_ms", 0]] });
|
||||
});
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "data:text/html,<!DOCTYPE html>",
|
||||
},
|
||||
async function (browser) {
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(browser, 300, 300);
|
||||
|
||||
await SpecialPowers.spawn(browser, [], function () {
|
||||
let widget = content.document.insertAnonymousContent();
|
||||
widget.root.innerHTML = `<button style="pointer-events: auto; position: absolute; width: 200px; height: 200px;" title="foo">bar</button>`;
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
|
||||
let text = {};
|
||||
let dir = {};
|
||||
ok(
|
||||
tttp.getNodeText(widget.root.querySelector("button"), text, dir),
|
||||
"A tooltip should be shown for NAC"
|
||||
);
|
||||
is(text.value, "foo", "Tooltip text should be correct");
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(browser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(browser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"foo",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,166 @@
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({ set: [["ui.tooltip.delay_ms", 0]] });
|
||||
});
|
||||
|
||||
add_task(async function test_title_in_shadow_dom() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating host");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let doc = content.document;
|
||||
let host = doc.createElement("div");
|
||||
doc.body.appendChild(host);
|
||||
host.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
var sr = host.attachShadow({ mode: "closed" });
|
||||
sr.innerHTML =
|
||||
"<div title='shadow' style='width: 200px; height: 200px;'>shadow</div>";
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"shadow",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_title_in_light_dom() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating host");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let doc = content.document;
|
||||
let host = doc.createElement("div");
|
||||
host.title = "light";
|
||||
doc.body.appendChild(host);
|
||||
host.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
var sr = host.attachShadow({ mode: "closed" });
|
||||
sr.innerHTML = "<div style='width: 200px; height: 200px;'>shadow</div>";
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"light",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_title_through_slot() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating host");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let doc = content.document;
|
||||
let host = doc.createElement("div");
|
||||
host.title = "light";
|
||||
host.innerHTML = "<div style='width: 200px; height: 200px;'>light</div>";
|
||||
doc.body.appendChild(host);
|
||||
host.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
var sr = host.attachShadow({ mode: "closed" });
|
||||
sr.innerHTML =
|
||||
"<div title='shadow' style='width: 200px; height: 200px;'><slot></slot></div>";
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"shadow",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
59
src/zen/tests/mochitests/tooltiptext/title_test.svg
Normal file
59
src/zen/tests/mochitests/tooltiptext/title_test.svg
Normal file
@@ -0,0 +1,59 @@
|
||||
<svg width="640px" height="480px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>This is a root SVG element's title</title>
|
||||
<foreignObject>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg1">
|
||||
<title>This is a non-root SVG element title</title>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
</foreignObject>
|
||||
<text id="text1" x="10px" y="32px" font-size="24px">
|
||||
This contains only <title>
|
||||
<title>
|
||||
|
||||
|
||||
This is a title
|
||||
|
||||
</title>
|
||||
</text>
|
||||
<text id="text2" x="10px" y="96px" font-size="24px">
|
||||
This contains only <desc>
|
||||
<desc>This is a desc</desc>
|
||||
</text>
|
||||
<text id="text3" x="10px" y="128px" font-size="24px" title="ignored for SVG">
|
||||
This contains nothing.
|
||||
</text>
|
||||
<a id="link1" href="#">
|
||||
This link contains <title>
|
||||
<title>
|
||||
This is a title
|
||||
</title>
|
||||
<text id="text4" x="10px" y="192px" font-size="24px">
|
||||
</text>
|
||||
</a>
|
||||
<a id="link2" href="#">
|
||||
<text x="10px" y="192px" font-size="24px">
|
||||
This text contains <title>
|
||||
<title>
|
||||
This is a title
|
||||
</title>
|
||||
</text>
|
||||
</a>
|
||||
<a id="link3" href="#" xlink:title="This is an xlink:title attribute">
|
||||
<text x="10px" y="224px" font-size="24px">
|
||||
This link contains <title> & xlink:title attr.
|
||||
<title>This is a title</title>
|
||||
</text>
|
||||
</a>
|
||||
<a id="link4" href="#" xlink:title="This is an xlink:title attribute">
|
||||
<text x="10px" y="256px" font-size="24px">
|
||||
This link contains xlink:title attr.
|
||||
</text>
|
||||
</a>
|
||||
<text id="text5" x="10px" y="160px" font-size="24px"
|
||||
xlink:title="This is an xlink:title attribute but it isn't on a link" >
|
||||
This contains nothing.
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
12
src/zen/tests/mochitests/tooltiptext/xul_tooltiptext.xhtml
Normal file
12
src/zen/tests/mochitests/tooltiptext/xul_tooltiptext.xhtml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<xul:toolbox xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<toolbar>
|
||||
<toolbarbutton id="xulToolbarButton"
|
||||
tooltiptext="XUL tooltiptext"
|
||||
title="XUL title"/>
|
||||
</toolbar>
|
||||
</xul:toolbox>
|
||||
</html>
|
||||
285
src/zen/tests/mochitests/translations/browser.toml
Normal file
285
src/zen/tests/mochitests/translations/browser.toml
Normal file
@@ -0,0 +1,285 @@
|
||||
[DEFAULT]
|
||||
subsuite = "translations"
|
||||
support-files = [
|
||||
"head.js",
|
||||
"!/toolkit/components/translations/tests/browser/shared-head.js",
|
||||
"!/toolkit/components/translations/tests/browser/translations-test.mjs",
|
||||
]
|
||||
|
||||
["browser_translations_about_preferences_manage_downloaded_languages.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_always_translate_languages.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_download_languages_all_ui.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_download_languages_error_ui.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_download_languages_ui.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_never_translate_languages.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_never_translate_sites.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_ui.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_ui_keyboard_a11y.js"]
|
||||
|
||||
["browser_translations_about_preferences_settings_ui_tab.js"]
|
||||
|
||||
["browser_translations_e2e_full_page_translate_with_lexical_shortlist.js"]
|
||||
|
||||
["browser_translations_e2e_full_page_translate_without_lexical_shortlist.js"]
|
||||
|
||||
["browser_translations_full_page_intersection_content_eager.js"]
|
||||
|
||||
["browser_translations_full_page_intersection_find_bar.js"]
|
||||
|
||||
["browser_translations_full_page_intersection_find_bar_move_tab_to_new_window.js"]
|
||||
|
||||
["browser_translations_full_page_intersection_find_bar_multi_tab.js"]
|
||||
|
||||
["browser_translations_full_page_intersection_lazy.js"]
|
||||
|
||||
["browser_translations_full_page_intersection_mutations_content_eager.js"]
|
||||
|
||||
["browser_translations_full_page_intersection_mutations_lazy.js"]
|
||||
|
||||
["browser_translations_full_page_language_id_behavior.js"]
|
||||
|
||||
["browser_translations_full_page_move_tab_to_new_window.js"]
|
||||
|
||||
["browser_translations_full_page_moz_extension.js"]
|
||||
|
||||
["browser_translations_full_page_multiple_windows.js"]
|
||||
skip-if = ["os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && ccov"] # Bug 1893021
|
||||
|
||||
["browser_translations_full_page_panel_a11y_focus.js"]
|
||||
|
||||
["browser_translations_full_page_panel_always_translate_language_bad_data.js"]
|
||||
|
||||
["browser_translations_full_page_panel_always_translate_language_basic.js"]
|
||||
|
||||
["browser_translations_full_page_panel_always_translate_language_manual.js"]
|
||||
|
||||
["browser_translations_full_page_panel_always_translate_language_restore.js"]
|
||||
|
||||
["browser_translations_full_page_panel_app_menu_never_translate_language.js"]
|
||||
|
||||
["browser_translations_full_page_panel_app_menu_never_translate_site.js"]
|
||||
|
||||
["browser_translations_full_page_panel_auto_translate_error_view.js"]
|
||||
|
||||
["browser_translations_full_page_panel_auto_translate_revisit_view.js"]
|
||||
|
||||
["browser_translations_full_page_panel_basics.js"]
|
||||
|
||||
["browser_translations_full_page_panel_button.js"]
|
||||
|
||||
["browser_translations_full_page_panel_cancel.js"]
|
||||
|
||||
["browser_translations_full_page_panel_change_app_locale.js"]
|
||||
|
||||
["browser_translations_full_page_panel_close_panel_never_translate_language_with_translations_active.js"]
|
||||
|
||||
["browser_translations_full_page_panel_close_panel_never_translate_language_with_translations_inactive.js"]
|
||||
|
||||
["browser_translations_full_page_panel_close_panel_never_translate_site.js"]
|
||||
|
||||
["browser_translations_full_page_panel_engine_destroy.js"]
|
||||
|
||||
["browser_translations_full_page_panel_engine_destroy_pending.js"]
|
||||
skip-if = ["os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && ccov"] # Bug 1893021
|
||||
|
||||
["browser_translations_full_page_panel_engine_unsupported.js"]
|
||||
|
||||
["browser_translations_full_page_panel_firstrun.js"]
|
||||
|
||||
["browser_translations_full_page_panel_firstrun_revisit.js"]
|
||||
|
||||
["browser_translations_full_page_panel_flip_lexical_shortlist.js"]
|
||||
|
||||
["browser_translations_full_page_panel_fuzzing.js"]
|
||||
skip-if = ["true"]
|
||||
|
||||
["browser_translations_full_page_panel_gear.js"]
|
||||
|
||||
["browser_translations_full_page_panel_init_failure.js"]
|
||||
|
||||
["browser_translations_full_page_panel_modify_available_language_models.js"]
|
||||
|
||||
["browser_translations_full_page_panel_never_translate_language.js"]
|
||||
|
||||
["browser_translations_full_page_panel_never_translate_site_auto.js"]
|
||||
|
||||
["browser_translations_full_page_panel_never_translate_site_basic.js"]
|
||||
|
||||
["browser_translations_full_page_panel_never_translate_site_manual.js"]
|
||||
|
||||
["browser_translations_full_page_panel_retry.js"]
|
||||
|
||||
["browser_translations_full_page_panel_script_tags.js"]
|
||||
|
||||
["browser_translations_full_page_panel_settings_unsupported_lang.js"]
|
||||
|
||||
["browser_translations_full_page_panel_switch_languages.js"]
|
||||
|
||||
["browser_translations_full_page_panel_switch_tabs_before_engine_ready.js"]
|
||||
|
||||
["browser_translations_full_page_panel_target_language_persists_on_reopen.js"]
|
||||
|
||||
["browser_translations_full_page_panel_unsupported_lang.js"]
|
||||
|
||||
["browser_translations_full_page_panel_weblanguage_differs_from_app.js"]
|
||||
|
||||
["browser_translations_full_page_reader_mode.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_auto_translate.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_basics.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_open_panel.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_panel_auto_offer.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_panel_auto_offer_settings.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_retranslate.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_switch_languages.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_translation_failure.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_translation_request.js"]
|
||||
|
||||
["browser_translations_full_page_telemetry_unsupported_lang.js"]
|
||||
|
||||
["browser_translations_recent_language_memory_auto_translate.js"]
|
||||
|
||||
["browser_translations_recent_language_memory_full_page_and_select.js"]
|
||||
|
||||
["browser_translations_recent_language_memory_full_page_and_select_multi_window.js"]
|
||||
skip-if = ["os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && ccov"] # Bug 1972458
|
||||
|
||||
["browser_translations_recent_language_memory_full_page_multi_window.js"]
|
||||
|
||||
["browser_translations_recent_language_memory_full_page_multi_window_multi_tab.js"]
|
||||
|
||||
["browser_translations_recent_language_memory_full_page_navigate.js"]
|
||||
|
||||
["browser_translations_recent_language_memory_full_page_retranslate.js"]
|
||||
|
||||
["browser_translations_select_context_menu_engine_unsupported.js"]
|
||||
|
||||
["browser_translations_select_context_menu_feature_disabled.js"]
|
||||
|
||||
["browser_translations_select_context_menu_preferred_app_locales.js"]
|
||||
|
||||
["browser_translations_select_context_menu_preferred_language_edge_cases.js"]
|
||||
|
||||
["browser_translations_select_context_menu_preferred_web_languages.js"]
|
||||
|
||||
["browser_translations_select_context_menu_with_full_page_translations_active.js"]
|
||||
|
||||
["browser_translations_select_context_menu_with_hyperlink.js"]
|
||||
|
||||
["browser_translations_select_context_menu_with_no_text_selected.js"]
|
||||
|
||||
["browser_translations_select_context_menu_with_text_selected.js"]
|
||||
|
||||
["browser_translations_select_panel_a11y_utils.js"]
|
||||
|
||||
["browser_translations_select_panel_change_app_locale.js"]
|
||||
|
||||
["browser_translations_select_panel_close_on_new_tab.js"]
|
||||
|
||||
["browser_translations_select_panel_copy_button.js"]
|
||||
|
||||
["browser_translations_select_panel_engine_cache.js"]
|
||||
|
||||
["browser_translations_select_panel_fallback_to_doc_language.js"]
|
||||
|
||||
["browser_translations_select_panel_flip_lexical_shortlist.js"]
|
||||
|
||||
["browser_translations_select_panel_init_failure.js"]
|
||||
|
||||
["browser_translations_select_panel_modify_available_language_models.js"]
|
||||
|
||||
["browser_translations_select_panel_pdf.js"]
|
||||
|
||||
["browser_translations_select_panel_reader_mode.js"]
|
||||
|
||||
["browser_translations_select_panel_retranslate_on_change_language_directly.js"]
|
||||
|
||||
["browser_translations_select_panel_retranslate_on_change_language_from_dropdown_menu.js"]
|
||||
|
||||
["browser_translations_select_panel_script_tags.js"]
|
||||
|
||||
["browser_translations_select_panel_select_current_language_directly.js"]
|
||||
|
||||
["browser_translations_select_panel_select_current_language_from_dropdown_menu.js"]
|
||||
|
||||
["browser_translations_select_panel_select_same_from_and_to_languages_directly.js"]
|
||||
|
||||
["browser_translations_select_panel_select_same_from_and_to_languages_from_dropdown_menu.js"]
|
||||
|
||||
["browser_translations_select_panel_settings_menu.js"]
|
||||
|
||||
["browser_translations_select_panel_translate_full_page_button.js"]
|
||||
|
||||
["browser_translations_select_panel_translate_on_change_language_directly.js"]
|
||||
|
||||
["browser_translations_select_panel_translate_on_change_language_from_dropdown_menu.js"]
|
||||
|
||||
["browser_translations_select_panel_translate_on_change_language_multiple_times_directly.js"]
|
||||
|
||||
["browser_translations_select_panel_translate_on_change_language_multiple_times_from_dropdown_menu.js"]
|
||||
|
||||
["browser_translations_select_panel_translate_on_open.js"]
|
||||
|
||||
["browser_translations_select_panel_translation_failure_after_unsupported_language.js"]
|
||||
|
||||
["browser_translations_select_panel_translation_failure_on_open.js"]
|
||||
|
||||
["browser_translations_select_panel_translation_failure_on_retranslate.js"]
|
||||
|
||||
["browser_translations_select_panel_unsupported_language.js"]
|
||||
|
||||
["browser_translations_select_telemetry_change_both_languages_together.js"]
|
||||
|
||||
["browser_translations_select_telemetry_change_languages_multiple_times.js"]
|
||||
|
||||
["browser_translations_select_telemetry_init_failure_ui_then_cancel.js"]
|
||||
|
||||
["browser_translations_select_telemetry_init_failure_ui_then_succeed.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_cancel_button.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_copy_button.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_done_button.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_settings_button.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_translate_button.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_translate_full_page_button.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_try_again_button_init_failure.js"]
|
||||
|
||||
["browser_translations_select_telemetry_keypresses_try_again_button_translation_failure.js"]
|
||||
|
||||
["browser_translations_select_telemetry_multi_page.js"]
|
||||
|
||||
["browser_translations_select_telemetry_primary_ui.js"]
|
||||
|
||||
["browser_translations_select_telemetry_settings_menu.js"]
|
||||
|
||||
["browser_translations_select_telemetry_translation_failure_ui_then_cancel.js"]
|
||||
|
||||
["browser_translations_select_telemetry_translation_failure_ui_then_succeed.js"]
|
||||
|
||||
["browser_translations_select_telemetry_translation_failure_with_full_page_translations_active.js"]
|
||||
|
||||
["browser_translations_select_telemetry_translation_success_with_full_page_translations_active.js"]
|
||||
|
||||
["browser_translations_select_telemetry_unsupported_language_ui.js"]
|
||||
@@ -0,0 +1,225 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function getFrenchModels() {
|
||||
return languageModelNames([
|
||||
{ fromLang: "fr", toLang: "en" },
|
||||
{ fromLang: "en", toLang: "fr" },
|
||||
]);
|
||||
}
|
||||
|
||||
add_task(async function test_about_preferences_manage_languages() {
|
||||
await testWithAndWithoutLexicalShortlist(async lexicalShortlistPrefs => {
|
||||
const {
|
||||
cleanup,
|
||||
remoteClients,
|
||||
elements: {
|
||||
downloadAllLabel,
|
||||
downloadAll,
|
||||
deleteAll,
|
||||
frenchLabel,
|
||||
frenchDownload,
|
||||
frenchDelete,
|
||||
spanishLabel,
|
||||
spanishDownload,
|
||||
spanishDelete,
|
||||
ukrainianLabel,
|
||||
ukrainianDownload,
|
||||
ukrainianDelete,
|
||||
},
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [
|
||||
["browser.translations.newSettingsUI.enable", false],
|
||||
...lexicalShortlistPrefs,
|
||||
],
|
||||
});
|
||||
|
||||
is(
|
||||
downloadAllLabel.getAttribute("data-l10n-id"),
|
||||
"translations-manage-download-description",
|
||||
"The first row is all of the languages."
|
||||
);
|
||||
is(frenchLabel.textContent, "French", "There is a French row.");
|
||||
is(spanishLabel.textContent, "Spanish", "There is a Spanish row.");
|
||||
is(ukrainianLabel.textContent, "Ukrainian", "There is a Ukrainian row.");
|
||||
|
||||
await ensureVisibility({
|
||||
message: "Everything starts out as available to download",
|
||||
visible: {
|
||||
downloadAll,
|
||||
frenchDownload,
|
||||
spanishDownload,
|
||||
ukrainianDownload,
|
||||
},
|
||||
hidden: { deleteAll, frenchDelete, spanishDelete, ukrainianDelete },
|
||||
});
|
||||
|
||||
click(frenchDownload, "Downloading French");
|
||||
|
||||
const frenchModels = getFrenchModels();
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
frenchModels.length
|
||||
),
|
||||
frenchModels,
|
||||
"French models were downloaded."
|
||||
);
|
||||
|
||||
await ensureVisibility({
|
||||
message: "French can now be deleted, and delete all is available.",
|
||||
visible: {
|
||||
downloadAll,
|
||||
deleteAll,
|
||||
frenchDelete,
|
||||
spanishDownload,
|
||||
ukrainianDownload,
|
||||
},
|
||||
hidden: { frenchDownload, spanishDelete, ukrainianDelete },
|
||||
});
|
||||
|
||||
click(frenchDelete, "Deleting French");
|
||||
|
||||
await ensureVisibility({
|
||||
message: "Everything can be downloaded.",
|
||||
visible: {
|
||||
downloadAll,
|
||||
frenchDownload,
|
||||
spanishDownload,
|
||||
ukrainianDownload,
|
||||
},
|
||||
hidden: { deleteAll, frenchDelete, spanishDelete, ukrainianDelete },
|
||||
});
|
||||
|
||||
click(downloadAll, "Downloading all languages.");
|
||||
|
||||
const allModels = languageModelNames(LANGUAGE_PAIRS);
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
allModels.length
|
||||
),
|
||||
allModels,
|
||||
"All models were downloaded."
|
||||
);
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationsWasm.resolvePendingDownloads(1),
|
||||
["bergamot-translator"],
|
||||
"Wasm was downloaded."
|
||||
);
|
||||
|
||||
await ensureVisibility({
|
||||
message: "Everything can be deleted.",
|
||||
visible: { deleteAll, frenchDelete, spanishDelete, ukrainianDelete },
|
||||
hidden: {
|
||||
downloadAll,
|
||||
frenchDownload,
|
||||
spanishDownload,
|
||||
ukrainianDownload,
|
||||
},
|
||||
});
|
||||
|
||||
click(deleteAll, "Deleting all languages.");
|
||||
|
||||
await ensureVisibility({
|
||||
message: "Everything can be downloaded again",
|
||||
visible: {
|
||||
downloadAll,
|
||||
frenchDownload,
|
||||
spanishDownload,
|
||||
ukrainianDownload,
|
||||
},
|
||||
hidden: { deleteAll, frenchDelete, spanishDelete, ukrainianDelete },
|
||||
});
|
||||
|
||||
click(frenchDownload, "Downloading French.");
|
||||
click(spanishDownload, "Downloading Spanish.");
|
||||
click(ukrainianDownload, "Downloading Ukrainian.");
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
allModels.length
|
||||
),
|
||||
allModels,
|
||||
"All models were downloaded again."
|
||||
);
|
||||
|
||||
remoteClients.translationsWasm.assertNoNewDownloads();
|
||||
|
||||
await ensureVisibility({
|
||||
message: "Everything is downloaded again.",
|
||||
visible: { deleteAll, frenchDelete, spanishDelete, ukrainianDelete },
|
||||
hidden: {
|
||||
downloadAll,
|
||||
frenchDownload,
|
||||
spanishDownload,
|
||||
ukrainianDownload,
|
||||
},
|
||||
});
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_about_preferences_download_reject() {
|
||||
const {
|
||||
cleanup,
|
||||
remoteClients,
|
||||
elements: { document, frenchDownload },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", false]],
|
||||
});
|
||||
|
||||
click(frenchDownload, "Downloading French");
|
||||
|
||||
is(
|
||||
maybeGetByL10nId("translations-manage-error-download", document),
|
||||
null,
|
||||
"No error messages are present."
|
||||
);
|
||||
|
||||
const failureErrors = await captureTranslationsError(() =>
|
||||
remoteClients.translationModels.rejectPendingDownloads(
|
||||
getFrenchModels().length
|
||||
)
|
||||
);
|
||||
|
||||
ok(
|
||||
!!failureErrors.length,
|
||||
`The errors for download should have been reported, found ${failureErrors.length} errors`
|
||||
);
|
||||
for (const { error } of failureErrors) {
|
||||
is(
|
||||
error?.message,
|
||||
"Failed to download file.",
|
||||
"The error reported was a download error."
|
||||
);
|
||||
}
|
||||
|
||||
await waitForCondition(
|
||||
() => maybeGetByL10nId("translations-manage-error-download", document),
|
||||
"The error message is now visible."
|
||||
);
|
||||
|
||||
click(frenchDownload, "Attempting to download French again", document);
|
||||
is(
|
||||
maybeGetByL10nId("translations-manage-error-download", document),
|
||||
null,
|
||||
"The error message is hidden again."
|
||||
);
|
||||
|
||||
const successErrors = await captureTranslationsError(() =>
|
||||
remoteClients.translationModels.resolvePendingDownloads(
|
||||
getFrenchModels().length
|
||||
)
|
||||
);
|
||||
|
||||
is(
|
||||
successErrors.length,
|
||||
0,
|
||||
"Expected no errors downloading French the second time"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(
|
||||
async function test_about_preferences_always_translate_language_settings() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", false]],
|
||||
});
|
||||
|
||||
info("Ensuring the list of always-translate languages is empty");
|
||||
is(
|
||||
getAlwaysTranslateLanguagesFromPref().length,
|
||||
0,
|
||||
"The list of always-translate languages is empty"
|
||||
);
|
||||
|
||||
info("Adding two languages to the alwaysTranslateLanguages pref");
|
||||
Services.prefs.setCharPref(ALWAYS_TRANSLATE_LANGS_PREF, "fr,de");
|
||||
|
||||
const dialogWindow = await waitForOpenDialogWindow(
|
||||
"chrome://browser/content/preferences/dialogs/translations.xhtml",
|
||||
() => {
|
||||
click(
|
||||
settingsButton,
|
||||
"Opening the about:preferences Translations Settings"
|
||||
);
|
||||
}
|
||||
);
|
||||
let tree = dialogWindow.document.getElementById(
|
||||
"alwaysTranslateLanguagesTree"
|
||||
);
|
||||
let remove = dialogWindow.document.getElementById(
|
||||
"removeAlwaysTranslateLanguage"
|
||||
);
|
||||
let removeAll = dialogWindow.document.getElementById(
|
||||
"removeAllAlwaysTranslateLanguages"
|
||||
);
|
||||
|
||||
is(
|
||||
tree.view.rowCount,
|
||||
2,
|
||||
"The always-translate languages list has 2 items"
|
||||
);
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Languages' button is enabled");
|
||||
|
||||
info("Selecting the first always-translate language.");
|
||||
tree.view.selection.select(0);
|
||||
ok(!remove.disabled, "The 'Remove Language' button is enabled");
|
||||
|
||||
click(remove, "Clicking the remove-language button");
|
||||
is(
|
||||
tree.view.rowCount,
|
||||
1,
|
||||
"The always-translate languages list now contains 1 item"
|
||||
);
|
||||
is(
|
||||
getAlwaysTranslateLanguagesFromPref().length,
|
||||
1,
|
||||
"One language tag in the pref"
|
||||
);
|
||||
|
||||
info("Removing all languages from the alwaysTranslateLanguages pref");
|
||||
Services.prefs.setCharPref(ALWAYS_TRANSLATE_LANGS_PREF, "");
|
||||
is(tree.view.rowCount, 0, "The always-translate languages list is empty");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Languages' button is disabled");
|
||||
|
||||
info("Adding more languages to the alwaysTranslateLanguages pref");
|
||||
Services.prefs.setCharPref(ALWAYS_TRANSLATE_LANGS_PREF, "fr,en,es");
|
||||
is(
|
||||
tree.view.rowCount,
|
||||
3,
|
||||
"The always-translate languages list has 3 items"
|
||||
);
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Languages' button is enabled");
|
||||
|
||||
click(removeAll, "Clicking the remove-all languages button");
|
||||
is(tree.view.rowCount, 0, "The always-translate languages list is empty");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Languages' button is disabled");
|
||||
is(
|
||||
getAlwaysTranslateLanguagesFromPref().length,
|
||||
0,
|
||||
"There are no languages in the alwaysTranslateLanguages pref"
|
||||
);
|
||||
|
||||
await waitForCloseDialogWindow(dialogWindow);
|
||||
await cleanup();
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,159 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_translations_settings_download_languages_all() {
|
||||
const {
|
||||
cleanup,
|
||||
remoteClients,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
const frenchModels = languageModelNames([
|
||||
{ fromLang: "fr", toLang: "en" },
|
||||
{ fromLang: "en", toLang: "fr" },
|
||||
]);
|
||||
|
||||
const spanishModels = languageModelNames([
|
||||
{ fromLang: "es", toLang: "en" },
|
||||
{ fromLang: "en", toLang: "es" },
|
||||
]);
|
||||
|
||||
const ukrainianModels = languageModelNames([
|
||||
{ fromLang: "uk", toLang: "en" },
|
||||
{ fromLang: "en", toLang: "uk" },
|
||||
]);
|
||||
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
const { downloadLanguageList } =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
info(
|
||||
"Install each language French, Spanish and Ukrainian and check if All language state changes to 'all language downloaded' by changing the all language button icon to 'remove icon'"
|
||||
);
|
||||
|
||||
info("Download French language model.");
|
||||
let langFr = Array.from(downloadLanguageList.querySelectorAll("label")).find(
|
||||
el => el.getAttribute("value") === "fr"
|
||||
);
|
||||
|
||||
let clickButton = BrowserTestUtils.waitForEvent(
|
||||
langFr.parentNode.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langFr.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
frenchModels.length
|
||||
),
|
||||
frenchModels,
|
||||
"French models were downloaded."
|
||||
);
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langFr,
|
||||
"translations-settings-remove-icon",
|
||||
"Delete icon is visible for French language hence downloaded"
|
||||
);
|
||||
|
||||
info("Download Spanish language model.");
|
||||
|
||||
let langEs = Array.from(downloadLanguageList.querySelectorAll("label")).find(
|
||||
el => el.getAttribute("value") === "es"
|
||||
);
|
||||
|
||||
clickButton = BrowserTestUtils.waitForEvent(
|
||||
langEs.parentNode.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langEs.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
spanishModels.length
|
||||
),
|
||||
spanishModels,
|
||||
"Spanish models were downloaded."
|
||||
);
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langEs,
|
||||
"translations-settings-remove-icon",
|
||||
"Delete icon is visible for Spanish language hence downloaded"
|
||||
);
|
||||
|
||||
info("Download Ukrainian language model.");
|
||||
|
||||
let langUk = Array.from(downloadLanguageList.querySelectorAll("label")).find(
|
||||
el => el.getAttribute("value") === "uk"
|
||||
);
|
||||
|
||||
clickButton = BrowserTestUtils.waitForEvent(
|
||||
langUk.parentNode.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langUk.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
ukrainianModels.length
|
||||
),
|
||||
ukrainianModels,
|
||||
"Ukrainian models were downloaded."
|
||||
);
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langUk,
|
||||
"translations-settings-remove-icon",
|
||||
"Delete icon is visible for Ukranian language hence downloaded."
|
||||
);
|
||||
|
||||
// Download "All languages" is the first child
|
||||
let langAll = downloadLanguageList.children[0];
|
||||
|
||||
ok(
|
||||
langAll
|
||||
.querySelector("moz-button")
|
||||
.classList.contains("translations-settings-remove-icon"),
|
||||
"Delete icon is visible for All Languages after all individual language models were downloaded."
|
||||
);
|
||||
|
||||
info(
|
||||
"Remove one language ensure that All Languages change state changes to 'removed' to indicate that all languages are not downloaded."
|
||||
);
|
||||
|
||||
info("Remove Spanish language model.");
|
||||
langEs.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langEs,
|
||||
"translations-settings-download-icon",
|
||||
"Download icon is visible for Spanish language hence removed"
|
||||
);
|
||||
|
||||
ok(
|
||||
langAll
|
||||
.querySelector("moz-button")
|
||||
.classList.contains("translations-settings-download-icon"),
|
||||
"Download icon is visible for all languages i.e. all languages are not downloaded since one language, Spanish was removed."
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
@@ -0,0 +1,173 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(
|
||||
async function test_translations_settings_download_languages_error_handling() {
|
||||
const {
|
||||
cleanup,
|
||||
remoteClients,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
const frenchModels = languageModelNames([
|
||||
{ fromLang: "fr", toLang: "en" },
|
||||
{ fromLang: "en", toLang: "fr" },
|
||||
]);
|
||||
|
||||
const spanishModels = languageModelNames([
|
||||
{ fromLang: "es", toLang: "en" },
|
||||
{ fromLang: "en", toLang: "es" },
|
||||
]);
|
||||
|
||||
const allModels = languageModelNames(LANGUAGE_PAIRS);
|
||||
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
const { downloadLanguageList } =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
info("Test French language model for download error");
|
||||
|
||||
let langFr = Array.from(
|
||||
downloadLanguageList.querySelectorAll("label")
|
||||
).find(el => el.getAttribute("value") === "fr");
|
||||
|
||||
let clickButton = BrowserTestUtils.waitForEvent(
|
||||
langFr.parentNode.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langFr.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
await captureTranslationsError(() =>
|
||||
remoteClients.translationModels.rejectPendingDownloads(
|
||||
frenchModels.length
|
||||
)
|
||||
);
|
||||
|
||||
const errorElement = gBrowser.selectedBrowser.contentDocument.querySelector(
|
||||
".translations-settings-language-error"
|
||||
);
|
||||
|
||||
assertVisibility({
|
||||
message: "Moz-message-bar with error message is visible",
|
||||
visible: { errorElement },
|
||||
});
|
||||
is(
|
||||
document.l10n.getAttributes(errorElement).id,
|
||||
"translations-settings-language-download-error",
|
||||
"Error message correctly shows download error"
|
||||
);
|
||||
is(
|
||||
document.l10n.getAttributes(errorElement).args.name,
|
||||
"French",
|
||||
"Error message correctly shows download error for French language"
|
||||
);
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langFr,
|
||||
"translations-settings-download-icon",
|
||||
"Download icon is visible on French button"
|
||||
);
|
||||
|
||||
remoteClients.translationsWasm.assertNoNewDownloads();
|
||||
|
||||
info("Download Spanish language model successfully.");
|
||||
|
||||
let langEs = Array.from(
|
||||
downloadLanguageList.querySelectorAll("label")
|
||||
).find(el => el.getAttribute("value") === "es");
|
||||
|
||||
clickButton = BrowserTestUtils.waitForEvent(
|
||||
langEs.parentNode.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langEs.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
const errorElementEs =
|
||||
gBrowser.selectedBrowser.contentDocument.querySelector(
|
||||
".translations-settings-language-error"
|
||||
);
|
||||
|
||||
ok(
|
||||
!errorElementEs,
|
||||
"Previous error is remove when new action occured, i.e. click download Spanish button"
|
||||
);
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
spanishModels.length
|
||||
),
|
||||
spanishModels,
|
||||
"Spanish models were downloaded."
|
||||
);
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langEs,
|
||||
"translations-settings-remove-icon",
|
||||
"Delete icon is visible for Spanish language hence downloaded"
|
||||
);
|
||||
|
||||
info("Test All language models download error");
|
||||
// Download "All languages" is the first child
|
||||
let langAll = downloadLanguageList.children[0];
|
||||
|
||||
let clickButtonAll = BrowserTestUtils.waitForEvent(
|
||||
langAll.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langAll.querySelector("moz-button").click();
|
||||
await clickButtonAll;
|
||||
|
||||
await captureTranslationsError(() =>
|
||||
remoteClients.translationModels.rejectPendingDownloads(allModels.length)
|
||||
);
|
||||
|
||||
await captureTranslationsError(() =>
|
||||
remoteClients.translationsWasm.rejectPendingDownloads(allModels.length)
|
||||
);
|
||||
|
||||
remoteClients.translationsWasm.assertNoNewDownloads();
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langAll,
|
||||
"translations-settings-download-icon",
|
||||
"Download icon is visible for 'all languages'"
|
||||
);
|
||||
|
||||
const errorElementAll =
|
||||
gBrowser.selectedBrowser.contentDocument.querySelector(
|
||||
".translations-settings-language-error"
|
||||
);
|
||||
|
||||
assertVisibility({
|
||||
message: "Moz-message-bar with error message is visible",
|
||||
visible: { errorElementAll },
|
||||
});
|
||||
is(
|
||||
document.l10n.getAttributes(errorElementAll).id,
|
||||
"translations-settings-language-download-error",
|
||||
"Error message correctly shows download error"
|
||||
);
|
||||
is(
|
||||
document.l10n.getAttributes(errorElementAll).args.name,
|
||||
"all",
|
||||
"Error message correctly shows download error for all language"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,117 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_translations_settings_download_languages() {
|
||||
await testWithAndWithoutLexicalShortlist(async lexicalShortlistPrefs => {
|
||||
const {
|
||||
cleanup,
|
||||
remoteClients,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [
|
||||
["browser.translations.newSettingsUI.enable", true],
|
||||
...lexicalShortlistPrefs,
|
||||
],
|
||||
});
|
||||
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
const { downloadLanguageList } =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
info("Test French language model install and uninstall function.");
|
||||
|
||||
let langFr = Array.from(
|
||||
downloadLanguageList.querySelectorAll("label")
|
||||
).find(el => el.getAttribute("value") === "fr");
|
||||
|
||||
let clickButton = BrowserTestUtils.waitForEvent(
|
||||
langFr.parentNode.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langFr.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
const frenchModels = languageModelNames([
|
||||
{ fromLang: "fr", toLang: "en" },
|
||||
{ fromLang: "en", toLang: "fr" },
|
||||
]);
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
frenchModels.length
|
||||
),
|
||||
frenchModels,
|
||||
"French models were downloaded."
|
||||
);
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langFr,
|
||||
"translations-settings-remove-icon",
|
||||
"Delete icon is visible on French button."
|
||||
);
|
||||
|
||||
langFr.parentNode.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langFr,
|
||||
"translations-settings-download-icon",
|
||||
"Download icon is visible on French Button."
|
||||
);
|
||||
|
||||
info("Test 'All language' models install and uninstall function");
|
||||
|
||||
// Download "All languages" is the first child
|
||||
let langAll = downloadLanguageList.children[0];
|
||||
|
||||
let clickButtonAll = BrowserTestUtils.waitForEvent(
|
||||
langAll.querySelector("moz-button"),
|
||||
"click"
|
||||
);
|
||||
langAll.querySelector("moz-button").click();
|
||||
await clickButtonAll;
|
||||
|
||||
const allModels = languageModelNames(LANGUAGE_PAIRS);
|
||||
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationModels.resolvePendingDownloads(
|
||||
allModels.length
|
||||
),
|
||||
allModels,
|
||||
"All models were downloaded."
|
||||
);
|
||||
Assert.deepEqual(
|
||||
await remoteClients.translationsWasm.resolvePendingDownloads(1),
|
||||
["bergamot-translator"],
|
||||
"Wasm was downloaded."
|
||||
);
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langAll,
|
||||
"translations-settings-remove-icon",
|
||||
"Delete icon is visible on 'All languages' button"
|
||||
);
|
||||
|
||||
langAll.querySelector("moz-button").click();
|
||||
await clickButton;
|
||||
|
||||
await TranslationsSettingsTestUtils.downaloadButtonClick(
|
||||
langAll,
|
||||
"translations-settings-download-icon",
|
||||
"Download icon is visible on 'All Language' button."
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(
|
||||
async function test_about_preferences_never_translate_language_settings() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", false]],
|
||||
});
|
||||
|
||||
info("Ensuring the list of never-translate languages is empty");
|
||||
is(
|
||||
getNeverTranslateLanguagesFromPref().length,
|
||||
0,
|
||||
"The list of never-translate languages is empty"
|
||||
);
|
||||
|
||||
info("Adding two languages to the neverTranslateLanguages pref");
|
||||
Services.prefs.setCharPref(NEVER_TRANSLATE_LANGS_PREF, "fr,de");
|
||||
|
||||
const dialogWindow = await waitForOpenDialogWindow(
|
||||
"chrome://browser/content/preferences/dialogs/translations.xhtml",
|
||||
() => {
|
||||
click(
|
||||
settingsButton,
|
||||
"Opening the about:preferences Translations Settings"
|
||||
);
|
||||
}
|
||||
);
|
||||
let tree = dialogWindow.document.getElementById(
|
||||
"neverTranslateLanguagesTree"
|
||||
);
|
||||
let remove = dialogWindow.document.getElementById(
|
||||
"removeNeverTranslateLanguage"
|
||||
);
|
||||
let removeAll = dialogWindow.document.getElementById(
|
||||
"removeAllNeverTranslateLanguages"
|
||||
);
|
||||
|
||||
is(tree.view.rowCount, 2, "The never-translate languages list has 2 items");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Languages' button is enabled");
|
||||
|
||||
info("Selecting the first never-translate language.");
|
||||
tree.view.selection.select(0);
|
||||
ok(!remove.disabled, "The 'Remove Language' button is enabled");
|
||||
|
||||
click(remove, "Clicking the remove-language button");
|
||||
is(
|
||||
tree.view.rowCount,
|
||||
1,
|
||||
"The never-translate languages list now contains 1 item"
|
||||
);
|
||||
is(
|
||||
getNeverTranslateLanguagesFromPref().length,
|
||||
1,
|
||||
"One language tag in the pref"
|
||||
);
|
||||
|
||||
info("Removing all languages from the neverTranslateLanguages pref");
|
||||
Services.prefs.setCharPref(NEVER_TRANSLATE_LANGS_PREF, "");
|
||||
is(tree.view.rowCount, 0, "The never-translate languages list is empty");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Languages' button is disabled");
|
||||
|
||||
info("Adding more languages to the neverTranslateLanguages pref");
|
||||
Services.prefs.setCharPref(NEVER_TRANSLATE_LANGS_PREF, "fr,en,es");
|
||||
is(tree.view.rowCount, 3, "The never-translate languages list has 3 items");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Languages' button is enabled");
|
||||
|
||||
click(removeAll, "Clicking the remove-all languages button");
|
||||
is(tree.view.rowCount, 0, "The never-translate languages list is empty");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Languages' button is disabled");
|
||||
is(
|
||||
getNeverTranslateLanguagesFromPref().length,
|
||||
0,
|
||||
"There are no languages in the neverTranslateLanguages pref"
|
||||
);
|
||||
|
||||
await waitForCloseDialogWindow(dialogWindow);
|
||||
await cleanup();
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,124 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PermissionTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/PermissionTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(async function test_about_preferences_never_translate_site_settings() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", false]],
|
||||
permissionsUrls: [
|
||||
"https://example.com",
|
||||
"https://example.org",
|
||||
"https://example.net",
|
||||
],
|
||||
});
|
||||
|
||||
info("Ensuring the list of never-translate sites is empty");
|
||||
is(
|
||||
getNeverTranslateSitesFromPerms().length,
|
||||
0,
|
||||
"The list of never-translate sites is empty"
|
||||
);
|
||||
|
||||
info("Adding two sites to the neverTranslateSites perms");
|
||||
PermissionTestUtils.add(
|
||||
"https://example.com",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
PermissionTestUtils.add(
|
||||
"https://example.org",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
PermissionTestUtils.add(
|
||||
"https://example.net",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
|
||||
const dialogWindow = await waitForOpenDialogWindow(
|
||||
"chrome://browser/content/preferences/dialogs/translations.xhtml",
|
||||
() => {
|
||||
click(
|
||||
settingsButton,
|
||||
"Opening the about:preferences Translations Settings"
|
||||
);
|
||||
}
|
||||
);
|
||||
let tree = dialogWindow.document.getElementById("neverTranslateSitesTree");
|
||||
let remove = dialogWindow.document.getElementById("removeNeverTranslateSite");
|
||||
let removeAll = dialogWindow.document.getElementById(
|
||||
"removeAllNeverTranslateSites"
|
||||
);
|
||||
|
||||
is(tree.view.rowCount, 3, "The never-translate sites list has 2 items");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Sites' button is enabled");
|
||||
|
||||
info("Selecting the first never-translate site.");
|
||||
tree.view.selection.select(0);
|
||||
ok(!remove.disabled, "The 'Remove Site' button is enabled");
|
||||
|
||||
click(remove, "Clicking the remove-site button");
|
||||
is(
|
||||
tree.view.rowCount,
|
||||
2,
|
||||
"The never-translate sites list now contains 2 items"
|
||||
);
|
||||
is(
|
||||
getNeverTranslateSitesFromPerms().length,
|
||||
2,
|
||||
"There are 2 sites with permissions"
|
||||
);
|
||||
|
||||
info("Removing all sites from the neverTranslateSites perms");
|
||||
PermissionTestUtils.remove("https://example.com", TRANSLATIONS_PERMISSION);
|
||||
PermissionTestUtils.remove("https://example.org", TRANSLATIONS_PERMISSION);
|
||||
PermissionTestUtils.remove("https://example.net", TRANSLATIONS_PERMISSION);
|
||||
|
||||
is(tree.view.rowCount, 0, "The never-translate sites list is empty");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Sites' button is disabled");
|
||||
|
||||
info("Adding more sites to the neverTranslateSites perms");
|
||||
PermissionTestUtils.add(
|
||||
"https://example.org",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
PermissionTestUtils.add(
|
||||
"https://example.com",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
PermissionTestUtils.add(
|
||||
"https://example.net",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
|
||||
is(tree.view.rowCount, 3, "The never-translate sites list has 3 items");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Sites' button is enabled");
|
||||
|
||||
click(removeAll, "Clicking the remove-all sites button");
|
||||
is(tree.view.rowCount, 0, "The never-translate sites list is empty");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Sites' button is disabled");
|
||||
is(
|
||||
getNeverTranslateSitesFromPerms().length,
|
||||
0,
|
||||
"There are no sites in the neverTranslateSites perms"
|
||||
);
|
||||
|
||||
await waitForCloseDialogWindow(dialogWindow);
|
||||
await cleanup();
|
||||
});
|
||||
@@ -0,0 +1,462 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_translations_settings_pane_elements() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
const {
|
||||
translationsSettingsBackButton,
|
||||
translationsSettingsHeader,
|
||||
translationsSettingsDescription,
|
||||
translateAlwaysHeader,
|
||||
translateNeverHeader,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
translateNeverSiteHeader,
|
||||
translateNeverSiteDesc,
|
||||
downloadLanguageSection,
|
||||
translateDownloadLanguagesLearnMore,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
const translateDownloadLanguagesHeader =
|
||||
downloadLanguageSection.querySelector("h2");
|
||||
assertVisibility({
|
||||
message: "Expect paneTranslations elements to be visible.",
|
||||
visible: {
|
||||
translationsSettingsBackButton,
|
||||
translationsSettingsHeader,
|
||||
translationsSettingsDescription,
|
||||
translateAlwaysHeader,
|
||||
translateNeverHeader,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
translateNeverSiteHeader,
|
||||
translateNeverSiteDesc,
|
||||
translateDownloadLanguagesLearnMore,
|
||||
},
|
||||
hidden: {
|
||||
settingsButton,
|
||||
},
|
||||
});
|
||||
|
||||
info(
|
||||
"In translations settings page, click on back button to go back to main preferences page."
|
||||
);
|
||||
const paneEvent = BrowserTestUtils.waitForEvent(
|
||||
document,
|
||||
"paneshown",
|
||||
false,
|
||||
event => event.detail.category === "paneGeneral"
|
||||
);
|
||||
|
||||
click(translationsSettingsBackButton);
|
||||
await paneEvent;
|
||||
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: {
|
||||
settingsButton,
|
||||
},
|
||||
hidden: {
|
||||
translationsSettingsBackButton,
|
||||
translationsSettingsHeader,
|
||||
translationsSettingsDescription,
|
||||
translateAlwaysHeader,
|
||||
translateNeverHeader,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
translateNeverSiteHeader,
|
||||
translateNeverSiteDesc,
|
||||
translateDownloadLanguagesHeader,
|
||||
translateDownloadLanguagesLearnMore,
|
||||
},
|
||||
});
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(async function test_translations_settings_always_translate() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
const {
|
||||
alwaysTranslateMenuList,
|
||||
alwaysTranslateLanguageList,
|
||||
alwaysTranslateMenuPopup,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
info("Testing the Always translate langauge settings");
|
||||
await testLanguageList(
|
||||
alwaysTranslateLanguageList,
|
||||
alwaysTranslateMenuList,
|
||||
alwaysTranslateMenuPopup,
|
||||
ALWAYS_TRANSLATE_LANGS_PREF,
|
||||
"Always"
|
||||
);
|
||||
await testLanguageListWithPref(
|
||||
alwaysTranslateLanguageList,
|
||||
ALWAYS_TRANSLATE_LANGS_PREF,
|
||||
"Always"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(async function test_translations_settings_never_translate() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
const {
|
||||
neverTranslateMenuList,
|
||||
neverTranslateLanguageList,
|
||||
neverTranslateMenuPopup,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
info("Testing the Never translate langauge settings");
|
||||
await testLanguageList(
|
||||
neverTranslateLanguageList,
|
||||
neverTranslateMenuList,
|
||||
neverTranslateMenuPopup,
|
||||
NEVER_TRANSLATE_LANGS_PREF,
|
||||
"Never"
|
||||
);
|
||||
await testLanguageListWithPref(
|
||||
neverTranslateLanguageList,
|
||||
NEVER_TRANSLATE_LANGS_PREF,
|
||||
"Never"
|
||||
);
|
||||
await cleanup();
|
||||
});
|
||||
function getLangsFromPref(pref) {
|
||||
let rawLangs = Services.prefs.getCharPref(pref);
|
||||
if (!rawLangs) {
|
||||
return [];
|
||||
}
|
||||
let langArr = rawLangs.split(",");
|
||||
return langArr;
|
||||
}
|
||||
|
||||
async function testLanguageList(
|
||||
languageList,
|
||||
menuList,
|
||||
menuPopup,
|
||||
pref,
|
||||
sectionName
|
||||
) {
|
||||
info("Ensure the Always/Never list is empty initially.");
|
||||
|
||||
is(
|
||||
languageList.childElementCount,
|
||||
0,
|
||||
`Language list empty in ${sectionName} Translate list`
|
||||
);
|
||||
|
||||
const menuItems = menuPopup.children;
|
||||
|
||||
info(
|
||||
"Click each language on the menulist to add it into the Always/Never list."
|
||||
);
|
||||
for (const menuItem of menuItems) {
|
||||
menuList.open = true;
|
||||
|
||||
let clickMenu = BrowserTestUtils.waitForEvent(
|
||||
menuList.querySelector("menupopup"),
|
||||
"popuphidden"
|
||||
);
|
||||
click(menuItem);
|
||||
menuList.querySelector("menupopup").hidePopup();
|
||||
await clickMenu;
|
||||
|
||||
/** Languages are always added on the top, so check the firstChild
|
||||
* for newly added languages.
|
||||
* the firstChild.querySelector("label").innerText is the language display name
|
||||
* which is compared with the menulist display name that is selected
|
||||
*/
|
||||
let langElem = languageList.firstElementChild;
|
||||
const displayName = getIntlDisplayName(menuItem.value);
|
||||
is(
|
||||
langElem.querySelector("label").innerText,
|
||||
displayName,
|
||||
`Language list has element ${displayName}`
|
||||
);
|
||||
|
||||
const langTag = langElem.querySelector("label").getAttribute("value");
|
||||
ok(
|
||||
getLangsFromPref(pref).includes(langTag),
|
||||
`Perferences contains ${langTag}`
|
||||
);
|
||||
}
|
||||
/** The test cases has 4 languages, so check if 4 languages are added to the list */
|
||||
let langNum = languageList.childElementCount;
|
||||
is(langNum, 4, "Number of languages added is 4");
|
||||
|
||||
info(
|
||||
"Remove each language from the Always/Never list that we added initially."
|
||||
);
|
||||
for (let i = 0; i < langNum; i++) {
|
||||
// Delete the first language in the list
|
||||
let langElem = languageList.children[0];
|
||||
let langName = langElem.querySelector("label").innerText;
|
||||
const langTag = langElem.querySelector("label").getAttribute("value");
|
||||
let langButton = langElem.querySelector("moz-button");
|
||||
let clickButton = BrowserTestUtils.waitForEvent(langButton, "click");
|
||||
langButton.click();
|
||||
await clickButton;
|
||||
|
||||
ok(
|
||||
!getLangsFromPref(pref).includes(langTag),
|
||||
`Perferences does not contain ${langTag}`
|
||||
);
|
||||
|
||||
if (i < langNum) {
|
||||
is(
|
||||
languageList.childElementCount,
|
||||
langNum - i - 1,
|
||||
`${langName} removed from ${sectionName} Translate`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function testLanguageListWithPref(languageList, pref, sectionName) {
|
||||
const langs = [
|
||||
"fr",
|
||||
"de",
|
||||
"en",
|
||||
"es",
|
||||
"fr,de",
|
||||
"fr,en",
|
||||
"fr,es",
|
||||
"de,en",
|
||||
"de,en,es",
|
||||
"es,fr,en",
|
||||
"en,es,fr,de",
|
||||
];
|
||||
|
||||
info("Ensure the Always/Never list is empty initially.");
|
||||
|
||||
is(
|
||||
languageList.childElementCount,
|
||||
0,
|
||||
`Language list is empty in ${sectionName} Translate list`
|
||||
);
|
||||
|
||||
info(
|
||||
"Add languages to the Always/Never list in translations setting by setting the ALWAYS_TRANSLATE_LANGS_PREF/NEVER_TRANSLATE_LANGS_PREF."
|
||||
);
|
||||
|
||||
for (const langOptions of langs) {
|
||||
Services.prefs.setCharPref(pref, langOptions);
|
||||
|
||||
/** Languages are always added on the top, so check the firstChild
|
||||
* for newly added languages.
|
||||
* the firstChild.querySelector("label").innerText is the language display name
|
||||
* which is compared with the menulist display name that is selected
|
||||
*/
|
||||
|
||||
const langsAdded = langOptions.split(",");
|
||||
is(
|
||||
languageList.childElementCount,
|
||||
langsAdded.length,
|
||||
`Language list has ${langsAdded.length} elements `
|
||||
);
|
||||
|
||||
let langsAddedHtml = Array.from(languageList.querySelectorAll("label"));
|
||||
|
||||
for (const lang of langsAdded) {
|
||||
const langFind = langsAddedHtml
|
||||
.find(el => el.getAttribute("value") === lang)
|
||||
.getAttribute("value");
|
||||
is(langFind, lang, `Language list has element ${lang}`);
|
||||
}
|
||||
}
|
||||
|
||||
Services.prefs.setCharPref(pref, "");
|
||||
is(
|
||||
languageList.childElementCount,
|
||||
0,
|
||||
`All removed from ${sectionName} Translate`
|
||||
);
|
||||
}
|
||||
|
||||
const { PermissionTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/PermissionTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(async function test_translations_settings_never_translate_site() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
const { neverTranslateSiteList } =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
info("Ensuring the list of never-translate sites is empty");
|
||||
is(
|
||||
getNeverTranslateSitesFromPerms().length,
|
||||
0,
|
||||
"The list of never-translate sites is empty"
|
||||
);
|
||||
|
||||
is(
|
||||
neverTranslateSiteList.childElementCount,
|
||||
0,
|
||||
"The never-translate sites html list is empty"
|
||||
);
|
||||
|
||||
info("Adding sites to the neverTranslateSites perms");
|
||||
await PermissionTestUtils.add(
|
||||
"https://example.com",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
await PermissionTestUtils.add(
|
||||
"https://example.org",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
await PermissionTestUtils.add(
|
||||
"https://example.net",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
|
||||
is(
|
||||
getNeverTranslateSitesFromPerms().length,
|
||||
3,
|
||||
"The list of never-translate sites has 3 elements"
|
||||
);
|
||||
|
||||
is(
|
||||
neverTranslateSiteList.childElementCount,
|
||||
3,
|
||||
"The never-translate sites html list has 3 elements"
|
||||
);
|
||||
|
||||
const permissionsUrls = [
|
||||
"https://example.com",
|
||||
"https://example.org",
|
||||
"https://example.net",
|
||||
];
|
||||
|
||||
info(
|
||||
"Ensure that the Never translate sites in permissions settings are reflected in Never translate sites section of translations settings page"
|
||||
);
|
||||
|
||||
const siteNum = neverTranslateSiteList.children.length;
|
||||
for (let i = siteNum; i > 0; i--) {
|
||||
is(
|
||||
neverTranslateSiteList.children[i - 1].querySelector("label").textContent,
|
||||
permissionsUrls[permissionsUrls.length - i],
|
||||
`Never translate URL ${
|
||||
permissionsUrls[permissionsUrls.length - i]
|
||||
} is added`
|
||||
);
|
||||
}
|
||||
|
||||
info(
|
||||
"Delete each site by clicking the button in Never translate sites section of translations settings page and check if it is removed in the Never translate sites in permissions settings"
|
||||
);
|
||||
for (let i = 0; i < siteNum; i++) {
|
||||
// Delete the first site in the list
|
||||
let siteElem = neverTranslateSiteList.children[0];
|
||||
// Delete the first language in the list
|
||||
let siteName = siteElem.querySelector("label").innerText;
|
||||
let siteButton = siteElem.querySelector("moz-button");
|
||||
|
||||
ok(
|
||||
neverTranslateSiteList.querySelector(`label[value="${siteName}"]`),
|
||||
`Site ${siteName} present in the Never transalate site list`
|
||||
);
|
||||
|
||||
ok(
|
||||
getNeverTranslateSitesFromPerms().find(p => p.origin === siteName),
|
||||
`Site ${siteName} present in the Never transalate site permissions list`
|
||||
);
|
||||
|
||||
let clickButton = BrowserTestUtils.waitForEvent(siteButton, "click");
|
||||
siteButton.click();
|
||||
await clickButton;
|
||||
|
||||
ok(
|
||||
!neverTranslateSiteList.querySelector(`label[value="${siteName}"]`),
|
||||
`Site ${siteName} removed successfully from the Never transalate site list`
|
||||
);
|
||||
|
||||
ok(
|
||||
!getNeverTranslateSitesFromPerms().find(p => p.origin === siteName),
|
||||
`Site ${siteName} removed from successfully from the Never transalate site permissions list`
|
||||
);
|
||||
|
||||
if (i < siteNum) {
|
||||
is(
|
||||
neverTranslateSiteList.childElementCount,
|
||||
siteNum - i - 1,
|
||||
`${siteName} removed from Never Translate Site`
|
||||
);
|
||||
}
|
||||
const siteLen = siteNum - i - 1;
|
||||
is(
|
||||
getNeverTranslateSitesFromPerms().length,
|
||||
siteLen,
|
||||
`There are ${siteLen} site in Never translate site`
|
||||
);
|
||||
}
|
||||
await cleanup();
|
||||
});
|
||||
@@ -0,0 +1,635 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PermissionTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/PermissionTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(async function test_translations_settings_keyboard_a11y() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
const {
|
||||
translationsSettingsBackButton,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
translateDownloadLanguagesLearnMore,
|
||||
downloadLanguageList,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
const document = gBrowser.selectedBrowser.contentDocument;
|
||||
|
||||
info("Press the Tab key to focus the first page element, the back button");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translationsSettingsBackButton.id,
|
||||
"Key is focused on back button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Always Translate Menulist button"
|
||||
);
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
alwaysTranslateMenuList.id,
|
||||
"Key is focused on Always Translate Menulist button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Never Translate Menulist button"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
neverTranslateMenuList.id,
|
||||
"Key is focused on Never Translate Menulist button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Download Languages' Learn More link"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translateDownloadLanguagesLearnMore.id,
|
||||
"Key is focused on Download Languages' Learn More link"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Download Languages list section"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
downloadLanguageList.id,
|
||||
"Key is focused on Download Languages list section"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(async function test_translations_settings_keyboard_download_a11y() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
const {
|
||||
translationsSettingsBackButton,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
translateDownloadLanguagesLearnMore,
|
||||
downloadLanguageList,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
const document = gBrowser.selectedBrowser.contentDocument;
|
||||
|
||||
info("Press the Tab key to focus the first page element, the back button");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translationsSettingsBackButton.id,
|
||||
"Key is focused on back button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Always Translate Menulist button"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
alwaysTranslateMenuList.id,
|
||||
"Key is focused on Always Translate Menulist button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Never Translate Menulist button"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
neverTranslateMenuList.id,
|
||||
"Key is focused on Never Translate Menulist button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Download Languages' Learn More link"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translateDownloadLanguagesLearnMore.id,
|
||||
"Key is focused on Download Languages' Learn More link"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Download Languages list section"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
downloadLanguageList.id,
|
||||
"Key is focused on Download Languages list section"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Arrow Down key to focus the first language element in the Download List Section"
|
||||
);
|
||||
|
||||
for (let element of downloadLanguageList.children) {
|
||||
info(
|
||||
"Press the Arrow Down key to focus the next language element in the Download List Section"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
element.id,
|
||||
"Key is focused on the language " +
|
||||
element.querySelector("label").textContent +
|
||||
" within the language list"
|
||||
);
|
||||
}
|
||||
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
downloadLanguageList.lastElementChild.id,
|
||||
"Key is focused on the last language " +
|
||||
downloadLanguageList.lastElementChild.querySelector("label").textContent +
|
||||
" within the language list"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Arrow up key to focus the previous language element in the Download List Section"
|
||||
);
|
||||
for (let i = downloadLanguageList.children.length - 2; i >= 0; i--) {
|
||||
info(
|
||||
"Press the Arrow up key to focus the previous language element in the Download List Section"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
downloadLanguageList.children[i].id,
|
||||
"Key is focused on the language " +
|
||||
downloadLanguageList.children[i].querySelector("label").textContent +
|
||||
" within the language list"
|
||||
);
|
||||
}
|
||||
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
downloadLanguageList.firstElementChild.id,
|
||||
"Key is focused on the first language within the language list"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(
|
||||
async function test_translations_settings_keyboard_never_translate_site_a11y() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
const {
|
||||
translationsSettingsBackButton,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
neverTranslateSiteList,
|
||||
translateDownloadLanguagesLearnMore,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
is(
|
||||
neverTranslateSiteList.childElementCount,
|
||||
0,
|
||||
"The never-translate sites html list is empty"
|
||||
);
|
||||
|
||||
info("Adding sites to the neverTranslateSites perms");
|
||||
await PermissionTestUtils.add(
|
||||
"https://example.com",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
await PermissionTestUtils.add(
|
||||
"https://example.org",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
await PermissionTestUtils.add(
|
||||
"https://example.net",
|
||||
TRANSLATIONS_PERMISSION,
|
||||
Services.perms.DENY_ACTION
|
||||
);
|
||||
|
||||
is(
|
||||
getNeverTranslateSitesFromPerms().length,
|
||||
3,
|
||||
"The list of never-translate sites has 3 elements"
|
||||
);
|
||||
|
||||
const document = gBrowser.selectedBrowser.contentDocument;
|
||||
|
||||
info("Press the Tab key to focus the first page element, the back button");
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translationsSettingsBackButton.id,
|
||||
"Key is focused on back button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Always Translate Menulist button"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
alwaysTranslateMenuList.id,
|
||||
"Key is focused on Always Translate Menulist button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Never Translate Menulist button"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
neverTranslateMenuList.id,
|
||||
"Key focus is now Never Translate List Menu button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Never Translate Site List section"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
neverTranslateSiteList.id,
|
||||
"Key focus is now Never Translate Site List"
|
||||
);
|
||||
info(
|
||||
"Press the Arrow Down key to focus the first site element in the Never Translate Site List"
|
||||
);
|
||||
for (const site of neverTranslateSiteList.children) {
|
||||
info(
|
||||
"Press the Arrow Down key to focus the next site element in the Never Translate Site List"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
site.id,
|
||||
"Key focus is now Never Translate Site list element " +
|
||||
site.querySelector("label").textContent
|
||||
);
|
||||
}
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
neverTranslateSiteList.lastElementChild.id,
|
||||
"Key is focused on the last site " +
|
||||
neverTranslateSiteList.lastElementChild.querySelector("label")
|
||||
.textContent +
|
||||
" within the site list"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Arrow up key to focus the previous site element in the Never Translate Site List"
|
||||
);
|
||||
for (let i = neverTranslateSiteList.children.length - 2; i >= 0; i--) {
|
||||
info(
|
||||
"Press the Arrow up key to focus the previous site element in the Never Translate Site List"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
neverTranslateSiteList.children[i].id,
|
||||
"Key is focused on the site " +
|
||||
neverTranslateSiteList.children[i].querySelector("label")
|
||||
.textContent +
|
||||
" within the site list"
|
||||
);
|
||||
}
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Download Languages' Learn More link"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translateDownloadLanguagesLearnMore.id,
|
||||
"Key is focused on Download Languages' Learn More link"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function test_translations_settings_keyboard_never_translate_a11y() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
const {
|
||||
translationsSettingsBackButton,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
neverTranslateLanguageList,
|
||||
neverTranslateMenuPopup,
|
||||
translateDownloadLanguagesLearnMore,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
const document = gBrowser.selectedBrowser.contentDocument;
|
||||
|
||||
info("Press the Tab key to focus the first page element, the back button");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translationsSettingsBackButton.id,
|
||||
"Key is focused on back button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Always Translate Menulist button"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
alwaysTranslateMenuList.id,
|
||||
"Key is focused on Always Translate Menulist button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Never Translate Menulist button."
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
neverTranslateMenuList.id,
|
||||
"Key is focused on Never Translate Menulist button"
|
||||
);
|
||||
|
||||
info("Press the Arrow Down key to focus on the first list element.");
|
||||
for (const menuItem of neverTranslateMenuPopup.children) {
|
||||
if (AppConstants.platform === "macosx") {
|
||||
info("Opening the menu popup.");
|
||||
const popupPromise = BrowserTestUtils.waitForEvent(
|
||||
neverTranslateMenuPopup,
|
||||
"popupshown"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
await popupPromise;
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
if (AppConstants.platform === "macosx") {
|
||||
info("Closing the menu popup.");
|
||||
const popupPromise = BrowserTestUtils.waitForEvent(
|
||||
neverTranslateMenuPopup,
|
||||
"popuphidden"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
await popupPromise;
|
||||
} else {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(resolve);
|
||||
});
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
await promise;
|
||||
}
|
||||
|
||||
is(
|
||||
neverTranslateLanguageList.firstElementChild.querySelector("label")
|
||||
.textContent,
|
||||
menuItem.textContent,
|
||||
menuItem.textContent + "is added to never translate language"
|
||||
);
|
||||
}
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Never Translate list"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
neverTranslateLanguageList.id,
|
||||
document.activeElement.id,
|
||||
"Key is focused on Always Translate list."
|
||||
);
|
||||
|
||||
info("Press the Arrow Down key to focus on the first list element.");
|
||||
|
||||
for (const lang of neverTranslateLanguageList.children) {
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
lang.id,
|
||||
"Key is focused on " +
|
||||
lang.querySelector("label").textContent +
|
||||
" element of Never Translate list."
|
||||
);
|
||||
}
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Download Languages' Learn More link"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translateDownloadLanguagesLearnMore.id,
|
||||
"Key is focused on Download Languages' Learn More link"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function test_translations_settings_keyboard_always_translate_a11y() {
|
||||
const {
|
||||
cleanup,
|
||||
elements: { settingsButton },
|
||||
} = await setupAboutPreferences(LANGUAGE_PAIRS, {
|
||||
prefs: [["browser.translations.newSettingsUI.enable", true]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Open translations settings page by clicking on translations settings button."
|
||||
);
|
||||
assertVisibility({
|
||||
message: "Expect paneGeneral elements to be visible.",
|
||||
visible: { settingsButton },
|
||||
});
|
||||
|
||||
const {
|
||||
translationsSettingsBackButton,
|
||||
alwaysTranslateMenuList,
|
||||
neverTranslateMenuList,
|
||||
alwaysTranslateLanguageList,
|
||||
alwaysTranslateMenuPopup,
|
||||
} =
|
||||
await TranslationsSettingsTestUtils.openAboutPreferencesTranslationsSettingsPane(
|
||||
settingsButton
|
||||
);
|
||||
|
||||
const document = gBrowser.selectedBrowser.contentDocument;
|
||||
|
||||
info("Press the Tab key to focus the first page element, the back button");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
translationsSettingsBackButton.id,
|
||||
"Key is focused on back button"
|
||||
);
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Always Translate Menulist button"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
alwaysTranslateMenuList.id,
|
||||
"Key is focused on Always Translate Menulist button"
|
||||
);
|
||||
|
||||
info("Press the Arrow Down key to focus on the first list element.");
|
||||
for (const menuItem of alwaysTranslateMenuPopup.children) {
|
||||
if (AppConstants.platform === "macosx") {
|
||||
info("Opening the menu popup.");
|
||||
const popupPromise = BrowserTestUtils.waitForEvent(
|
||||
alwaysTranslateMenuPopup,
|
||||
"popupshown"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
await popupPromise;
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
if (AppConstants.platform === "macosx") {
|
||||
info("Closing the menu popup.");
|
||||
const popupPromise = BrowserTestUtils.waitForEvent(
|
||||
alwaysTranslateMenuPopup,
|
||||
"popuphidden"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
await popupPromise;
|
||||
} else {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(resolve);
|
||||
});
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
await promise;
|
||||
}
|
||||
|
||||
is(
|
||||
alwaysTranslateLanguageList.firstElementChild.querySelector("label")
|
||||
.textContent,
|
||||
menuItem.textContent,
|
||||
menuItem.textContent + "is added to always translate language"
|
||||
);
|
||||
}
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Always Translate list"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
alwaysTranslateLanguageList.id,
|
||||
document.activeElement.id,
|
||||
"Key is focused on Always Translate list."
|
||||
);
|
||||
|
||||
info("Press the Arrow Down key to focus on the first list element.");
|
||||
|
||||
for (const lang of alwaysTranslateLanguageList.children) {
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
is(
|
||||
document.activeElement.parentNode.id,
|
||||
lang.id,
|
||||
"Key is focused on " +
|
||||
lang.querySelector("label").textContent +
|
||||
" element of Always Translate list."
|
||||
);
|
||||
}
|
||||
|
||||
info(
|
||||
"Press the Tab key to focus the next page element, the Never Translate list"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(
|
||||
document.activeElement.id,
|
||||
neverTranslateMenuList.id,
|
||||
"Key focus is now Never Translate List Menu button"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
}
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user