Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Errors
- This test failed 21 times in the preceding 30 days. quicksearch this test
- Manifest: browser/components/preferences/tests/browser.toml
/* Any copyright is dedicated to the Public Domain.
// This file tests the Privacy pane's Firefox VPN UI.
"use strict";
const lazy = {};
const { sinon } = ChromeUtils.importESModule(
);
ChromeUtils.defineESModuleGetters(lazy, {
SpecialMessageActions:
"resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
IPPEnrollAndEntitleManager:
"moz-src:///browser/components/ipprotection/IPPEnrollAndEntitleManager.sys.mjs",
});
const { BANDWIDTH } = ChromeUtils.importESModule(
"chrome://browser/content/ipprotection/ipprotection-constants.mjs"
);
const FEATURE_PREF = "browser.ipProtection.enabled";
const SITE_EXCEPTIONS_FEATURE_PREF =
"browser.ipProtection.features.siteExceptions";
const AUTOSTART_FEATURE_ENABLED_PREF =
"browser.ipProtection.features.autoStart";
const BANDWIDTH_FEATURE_ENABLED_PREF = "browser.ipProtection.bandwidth.enabled";
const AUTOSTART_PREF = "browser.ipProtection.autoStartEnabled";
const AUTOSTART_PRIVATE_PREF = "browser.ipProtection.autoStartPrivateEnabled";
const ONBOARDING_MESSAGE_MASK_PREF =
"browser.ipProtection.onboardingMessageMask";
const ENTITLEMENT_CACHE_PREF = "browser.ipProtection.entitlementCache";
const USAGE_CACHE_PREF = "browser.ipProtection.usageCache";
const IPP_ADDED_PREF = "browser.ipProtection.added";
const IPP_STATE_CACHE_PREF = "browser.ipProtection.stateCache";
const IPP_PANEL_HAS_OPENED_PREF = "browser.ipProtection.everOpenedPanel";
const IPP_CACHE_DISABLED_PREF = "browser.ipProtection.cacheDisabled";
const maxBytes = BANDWIDTH.MAX_IN_GB * BANDWIDTH.BYTES_IN_GB;
add_setup(async function ippSetup() {
await SpecialPowers.pushPrefEnv({
set: [[IPP_CACHE_DISABLED_PREF, true]],
});
registerCleanupFunction(async () => {
Services.prefs.clearUserPref(ONBOARDING_MESSAGE_MASK_PREF);
Services.prefs.clearUserPref(IPP_ADDED_PREF);
Services.prefs.clearUserPref(IPP_STATE_CACHE_PREF);
Services.prefs.clearUserPref(IPP_PANEL_HAS_OPENED_PREF);
});
});
async function setupVpnPrefs({
feature = false,
siteExceptions = false,
autostartFeatureEnabled = false,
bandwidth = false,
autostart = false,
autostartprivate = false,
entitlementCache = "",
usageCache = "",
}) {
let prefs = [
[FEATURE_PREF, feature],
[SITE_EXCEPTIONS_FEATURE_PREF, siteExceptions],
[AUTOSTART_FEATURE_ENABLED_PREF, autostartFeatureEnabled],
[BANDWIDTH_FEATURE_ENABLED_PREF, bandwidth],
[AUTOSTART_PREF, autostart],
[AUTOSTART_PRIVATE_PREF, autostartprivate],
[ENTITLEMENT_CACHE_PREF, entitlementCache],
[USAGE_CACHE_PREF, usageCache],
];
return SpecialPowers.pushPrefEnv({
set: prefs,
});
}
function testSettingsGroupVisible(browser) {
let settingGroup = browser.contentDocument.querySelector(
`setting-group[groupid="ipprotection"]`
);
is_element_visible(settingGroup, "ipprotection setting group is shown");
return settingGroup;
}
// Test the section is hidden on page load if the feature pref is disabled.
add_task(
async function test_section_removed_when_set_to_ineligible_experiment_pref() {
await setupVpnPrefs({ feature: false });
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = browser.contentDocument.querySelector(
`setting-group[groupid="ipprotection"]`
);
is_element_hidden(settingGroup, "ipprotection setting group is hidden");
}
);
await SpecialPowers.popPrefEnv();
}
);
// Test the section is shown on page load if the feature pref is enabled
add_task(
async function test_section_shown_when_set_to_eligible_experiment_pref() {
await setupVpnPrefs({ feature: true });
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
testSettingsGroupVisible(browser);
}
);
}
);
// Test the site exceptions controls load correctly.
add_task(async function test_exceptions_settings() {
await setupVpnPrefs({
feature: true,
siteExceptions: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let siteExceptionsGroup = settingGroup?.querySelector(
"#ipProtectionExceptions"
);
is_element_visible(siteExceptionsGroup, "Site exceptions group is shown");
let exceptionAllListButton = siteExceptionsGroup?.querySelector(
"#ipProtectionExceptionAllListButton"
);
is_element_visible(
exceptionAllListButton,
"Button for list of exclusions is shown"
);
}
);
});
// Test that we show the "Add" button in the site exceptions permission dialog
// and correctly add site exclusions.
add_task(async function test_exclusions_add_button() {
const PERM_NAME = "ipp-vpn";
await setupVpnPrefs({
feature: "beta",
siteExceptions: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let siteExceptionsGroup = settingGroup?.querySelector(
"#ipProtectionExceptions"
);
let exceptionAllListButton = siteExceptionsGroup?.querySelector(
"#ipProtectionExceptionAllListButton"
);
is_element_visible(
exceptionAllListButton,
"Button for list of exclusions is shown"
);
// Clear ipp-vpn to start with 0 exclusions
Services.perms.removeByType(PERM_NAME);
// Let's load the dialog
let promiseSubDialogLoaded = promiseLoadSubDialog(
"chrome://browser/content/preferences/dialogs/permissions.xhtml"
);
exceptionAllListButton.click();
const win = await promiseSubDialogLoaded;
let addButton = win.document.getElementById("btnAdd");
Assert.ok(addButton, "Add button exists");
Assert.ok(BrowserTestUtils.isVisible(addButton), "Add button is visible");
Assert.ok(addButton.disabled, "Add button is disabled");
// Now let's click the Add button to add a new exclusion
let permissionsBox = win.document.getElementById("permissionsBox");
let siteListUpdatedPromise = BrowserTestUtils.waitForMutationCondition(
permissionsBox,
{ subtree: true, childList: true },
() => {
return permissionsBox.children.length;
}
);
// Set up a mock url input value
let urlField = win.document.getElementById("url");
Assert.ok(urlField, "Dialog url field exists");
const site1 = "https://example.com";
urlField.focus();
EventUtils.sendString(site1, win);
Assert.ok(!addButton.disabled, "Add button is enabled");
addButton.click();
await siteListUpdatedPromise;
permissionsBox = win.document.getElementById("permissionsBox");
Assert.equal(
permissionsBox.children.length,
1,
"Should have 1 site listed as an exclusion"
);
let shownSite1 = permissionsBox.children[0];
Assert.equal(
shownSite1.getAttribute("origin"),
site1,
"Should match inputted site in the list of sites"
);
// Apply the changes
let saveButton = win.document.querySelector("dialog").getButton("accept");
Assert.ok(saveButton, "Save button is shown");
saveButton.click();
let exclusions = Services.perms.getAllByTypes([PERM_NAME]);
Assert.equal(
exclusions.length,
1,
"Should have 1 exclusion after pressing the Add button"
);
Assert.equal(
exclusions[0]?.principal.siteOrigin,
site1,
"Should match the inputted site"
);
// Clean up
Services.perms.removeByType(PERM_NAME);
Services.prefs.clearUserPref(ONBOARDING_MESSAGE_MASK_PREF);
}
);
});
// Test that the exclusion_added counter is incremented
// when exclusions are added via the permissions dialog
add_task(async function test_exclusions_telemetry() {
const PERM_NAME = "ipp-vpn";
await setupVpnPrefs({
feature: "beta",
siteExceptions: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let siteExceptionsGroup = settingGroup?.querySelector(
"#ipProtectionExceptions"
);
let exceptionAllListButton = siteExceptionsGroup?.querySelector(
"#ipProtectionExceptionAllListButton"
);
// Clear ipp-vpn and old telemetry
Services.perms.removeByType(PERM_NAME);
Services.fog.testResetFOG();
await Services.fog.testFlushAllChildren();
// Add an existing exclusion that we'll remove later
const site1 = "https://existing.example.com";
let principal1 =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(site1);
Services.perms.addFromPrincipal(
principal1,
PERM_NAME,
Services.perms.DENY_ACTION
);
// Reset telemetry after setting up the existing exclusion
Services.fog.testResetFOG();
await Services.fog.testFlushAllChildren();
// Load the permissions dialog
let promiseSubDialogLoaded = promiseLoadSubDialog(
"chrome://browser/content/preferences/dialogs/permissions.xhtml"
);
exceptionAllListButton.click();
const win = await promiseSubDialogLoaded;
let permissionsBox = win.document.getElementById("permissionsBox");
// Wait for existing exclusion to appear
await BrowserTestUtils.waitForMutationCondition(
permissionsBox,
{ subtree: true, childList: true },
() => permissionsBox.children.length === 1
);
// Add two new exclusions
let siteListUpdatedPromise = BrowserTestUtils.waitForMutationCondition(
permissionsBox,
{ subtree: true, childList: true },
() => {
return permissionsBox.children.length === 3;
}
);
let urlField = win.document.getElementById("url");
let addButton = win.document.getElementById("btnAdd");
const site2 = "https://example.com";
urlField.focus();
EventUtils.sendString(site2, win);
addButton.click();
const site3 = "https://another.example.com";
urlField.focus();
EventUtils.sendString(site3, win);
addButton.click();
await siteListUpdatedPromise;
// Remove the existing exclusion
siteListUpdatedPromise = BrowserTestUtils.waitForMutationCondition(
permissionsBox,
{ subtree: true, childList: true },
() => {
return permissionsBox.children.length === 2;
}
);
let removeButton = win.document.getElementById("removePermission");
let existingItem = Array.from(permissionsBox.children).find(
item => item.getAttribute("origin") === site1
);
Assert.ok(existingItem, "Should find the existing entry");
existingItem.click();
removeButton.click();
await siteListUpdatedPromise;
// Apply the changes and check telemetry
let saveButton = win.document.querySelector("dialog").getButton("accept");
saveButton.click();
// First verify the permissions were actually saved
let exclusions = Services.perms.getAllByTypes([PERM_NAME]);
Assert.equal(
exclusions.length,
2,
"Should have 2 new exclusions saved and remaining, ignoring the removed existing exclusion"
);
await Services.fog.testFlushAllChildren();
Assert.equal(
Glean.ipprotection.exclusionAdded.testGetValue(),
2,
"exclusion_added counter should be 2, ignoring the removed existing exclusion"
);
// Clean up
Services.perms.removeByType(PERM_NAME);
Services.prefs.clearUserPref(ONBOARDING_MESSAGE_MASK_PREF);
Services.fog.testResetFOG();
}
);
});
// Test that we show the correct number of site exclusions
add_task(async function test_exclusions_count() {
const PERM_NAME = "ipp-vpn";
await setupVpnPrefs({
feature: "beta",
siteExceptions: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = browser.contentDocument.querySelector(
`setting-group[groupid="ipprotection"]`
);
is_element_visible(settingGroup, "ipprotection setting group is shown");
let siteExceptionsGroup = settingGroup?.querySelector(
"#ipProtectionExceptions"
);
is_element_visible(siteExceptionsGroup, "Site exceptions group is shown");
let exceptionAllListButton = siteExceptionsGroup?.querySelector(
"#ipProtectionExceptionAllListButton"
);
is_element_visible(
exceptionAllListButton,
"Button for list of exclusions is shown"
);
let sitesCountUpdatedPromise = BrowserTestUtils.waitForMutationCondition(
exceptionAllListButton,
{ attributes: true, attributeFilter: ["data-l10n-args"] },
() => {
let args = exceptionAllListButton.getAttribute("data-l10n-args");
return args && JSON.parse(args)?.count === 0;
}
);
// Clear ipp-vpn to start with 0 exclusions
Services.perms.removeByType(PERM_NAME);
await sitesCountUpdatedPromise;
Assert.ok(true, "Should show 0 exclusions initially");
// Now test with 1 exclusion
sitesCountUpdatedPromise = BrowserTestUtils.waitForMutationCondition(
exceptionAllListButton,
{ attributes: true, attributeFilter: ["data-l10n-args"] },
() => {
let args = exceptionAllListButton.getAttribute("data-l10n-args");
return args && JSON.parse(args)?.count === 1;
}
);
let site1 = "https://example.com";
let principal1 =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(site1);
Services.perms.addFromPrincipal(
principal1,
PERM_NAME,
Services.perms.DENY_ACTION
);
await sitesCountUpdatedPromise;
Assert.ok(true, "Should show 1 exclusion after adding the first site");
// Now test with 2 exclusions
sitesCountUpdatedPromise = BrowserTestUtils.waitForMutationCondition(
exceptionAllListButton,
{ attributes: true, attributeFilter: ["data-l10n-args"] },
() => {
let args = exceptionAllListButton.getAttribute("data-l10n-args");
return args && JSON.parse(args)?.count === 2;
}
);
let site2 = "https://example.org";
let principal2 =
Services.scriptSecurityManager.createContentPrincipalFromOrigin(site2);
Services.perms.addFromPrincipal(
principal2,
PERM_NAME,
Services.perms.DENY_ACTION
);
await sitesCountUpdatedPromise;
Assert.ok(true, "Should show 2 exclusions after adding the second site");
// Clean up
Services.perms.removeByType(PERM_NAME);
Services.prefs.clearUserPref(ONBOARDING_MESSAGE_MASK_PREF);
}
);
});
// Test that autostart checkboxes exist and map to the correct preferences
add_task(async function test_autostart_checkboxes() {
await setupVpnPrefs({
feature: true,
autostartFeatureEnabled: true,
autostart: true,
autostartprivate: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let autoStartSettings = settingGroup?.querySelector(
"#ipProtectionAutoStart"
);
is_element_visible(
autoStartSettings,
"autoStart settings group is shown"
);
let autoStartCheckbox = autoStartSettings?.querySelector(
"#ipProtectionAutoStartCheckbox"
);
let autoStartPrivateCheckbox = autoStartSettings?.querySelector(
"#ipProtectionAutoStartPrivateCheckbox"
);
Assert.ok(
autoStartCheckbox.checked,
"Autostart checkbox should be checked"
);
Assert.ok(
autoStartPrivateCheckbox.checked,
"Autostart in private browsing checkbox should be checked"
);
}
);
});
// Test that additional links exist
add_task(async function test_additional_links() {
await setupVpnPrefs({
feature: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let ipProtectionLinks = settingGroup?.querySelector("#ipProtectionLinks");
is_element_visible(
ipProtectionLinks,
"VPN upgrade link section is shown"
);
}
);
});
// Test that the "not opted in" section is shown when entitlementCache is empty
add_task(async function test_not_opted_in_section_visible_when_empty() {
await setupVpnPrefs({
feature: true,
entitlementCache: "",
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let notOptedInSection = settingGroup?.querySelector(
"#ipProtectionNotOptedInSection"
);
is_element_visible(
notOptedInSection,
"Not opted in section is shown when entitlementCache is empty"
);
let getStartedButton = settingGroup?.querySelector("#getStartedButton");
is_element_visible(
getStartedButton,
"Get started button is shown when entitlementCache is empty"
);
}
);
});
// Test that clicking the "Get started" button start the optin flow.
add_task(async function test_get_started_button() {
let sandbox = sinon.createSandbox();
sandbox
.stub(lazy.SpecialMessageActions, "fxaSignInFlow")
.callsFake(async function () {
return true;
});
sandbox
.stub(lazy.IPPEnrollAndEntitleManager, "maybeEnrollAndEntitle")
.callsFake(async function () {
return true;
});
await setupVpnPrefs({
feature: true,
entitlementCache: "",
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let getStartedButton = settingGroup?.querySelector("#getStartedButton");
is_element_visible(
getStartedButton,
"Get started button is shown when entitlementCache is empty"
);
const waitForPanelShown = BrowserTestUtils.waitForEvent(
browser.ownerGlobal.document,
"popupshown",
false,
event => {
if (event.target.getAttribute("viewId") === "PanelUI-ipprotection") {
return true;
}
return false;
}
);
getStartedButton.click();
await waitForPanelShown;
Assert.ok(
lazy.SpecialMessageActions.fxaSignInFlow.calledOnce,
"fxaSignInFlow should be called once when Get started button is clicked"
);
Assert.ok(
lazy.IPPEnrollAndEntitleManager.maybeEnrollAndEntitle.calledOnce,
"maybeEnrollAndEntitle should be called once when Get started button is clicked"
);
}
);
// Clean up
EventUtils.synthesizeKey("KEY_Escape");
sandbox.restore();
});
// Test that the "not opted in" section is hidden when entitlementCache has a value
add_task(async function test_not_opted_in_section_hidden_when_opted_in() {
await setupVpnPrefs({
feature: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let notOptedInSection = settingGroup?.querySelector(
"#ipProtectionNotOptedInSection"
);
is_element_hidden(
notOptedInSection,
"Not opted in section is hidden when entitlementCache has a value"
);
let getStartedButton = settingGroup?.querySelector("#getStartedButton");
is_element_hidden(
getStartedButton,
"Get started button is hidden when entitlementCache has a value"
);
}
);
});
// Test that VPN sections are hidden when entitlementCache is empty
add_task(async function test_vpn_sections_hidden_when_not_opted_in() {
await setupVpnPrefs({
feature: true,
siteExceptions: true,
autostartFeatureEnabled: true,
entitlementCache: "",
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let siteExceptionsGroup = settingGroup?.querySelector(
"#ipProtectionExceptions"
);
is_element_hidden(
siteExceptionsGroup,
"Site exceptions group is hidden when entitlementCache is empty"
);
let autoStartSettings = settingGroup?.querySelector(
"#ipProtectionAutoStart"
);
is_element_hidden(
autoStartSettings,
"Autostart settings group is hidden when entitlementCache is empty"
);
let ipProtectionLinks = settingGroup?.querySelector("#ipProtectionLinks");
is_element_hidden(
ipProtectionLinks,
"VPN links section is hidden when entitlementCache is empty"
);
let bandwidthSettings = settingGroup?.querySelector(
"#ipProtectionBandwidth"
);
is_element_hidden(
bandwidthSettings,
"Bandwidth settings are hidden when entitlementCache is empty"
);
}
);
});
// Test that VPN sections are shown when entitlementCache has a value
add_task(async function test_vpn_sections_shown_when_opted_in() {
await setupVpnPrefs({
feature: true,
siteExceptions: true,
autostartFeatureEnabled: true,
entitlementCache: '{"some":"data"}',
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let siteExceptionsGroup = settingGroup?.querySelector(
"#ipProtectionExceptions"
);
is_element_visible(
siteExceptionsGroup,
"Site exceptions group is shown when entitlementCache has a value"
);
let autoStartSettings = settingGroup?.querySelector(
"#ipProtectionAutoStart"
);
is_element_visible(
autoStartSettings,
"Autostart settings group is shown when entitlementCache has a value"
);
let ipProtectionLinks = settingGroup?.querySelector("#ipProtectionLinks");
is_element_visible(
ipProtectionLinks,
"VPN links section is shown when entitlementCache has a value"
);
let bandwidthSettings = settingGroup?.querySelector(
"#ipProtectionBandwidth"
);
is_element_hidden(
bandwidthSettings,
"Bandwidth settings are hidden when bandwidth feature is not enabled"
);
}
);
});
// Test that the bandwidth progress bar displays the correct decimal precision
add_task(
async function test_bandwidth_usage_decimal_precision_in_preferences() {
// SECOND_THRESHOLD = 0.25 → 12.5 GB remaining, a clean decimal
const remainingBytes = maxBytes * BANDWIDTH.SECOND_THRESHOLD;
const usageCache = JSON.stringify({
max: String(maxBytes),
remaining: String(remainingBytes),
reset: "2026-03-01T00:00:00Z",
});
await setupVpnPrefs({
feature: true,
bandwidth: true,
entitlementCache: '{"some":"data"}',
usageCache,
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let bandwidthEl = settingGroup.querySelector(
"bandwidth-usage#ipProtectionBandwidth"
);
Assert.ok(bandwidthEl, "bandwidth-usage element should be present");
is_element_visible(
bandwidthEl,
"bandwidth-usage element should be visible"
);
await bandwidthEl.updateComplete;
Assert.equal(
bandwidthEl.bandwidthPercent,
75,
"bandwidthPercent should be 75 at the second threshold"
);
Assert.equal(
bandwidthEl.remainingRounded,
remainingBytes / BANDWIDTH.BYTES_IN_GB,
"remainingRounded should preserve the decimal GB value"
);
}
);
await SpecialPowers.popPrefEnv();
}
);
// Test that the bandwidth progress bar displays MB when remaining is less than 1 GB
add_task(async function test_bandwidth_usage_sub_gb_precision_in_preferences() {
const remainingBytes = Math.floor(0.9 * BANDWIDTH.BYTES_IN_GB);
const usageCache = JSON.stringify({
max: String(maxBytes),
remaining: String(remainingBytes),
reset: "2026-03-01T00:00:00Z",
});
await setupVpnPrefs({
feature: true,
bandwidth: true,
entitlementCache: '{\"some\":\"data\"}',
usageCache,
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:preferences#privacy" },
async function (browser) {
let settingGroup = testSettingsGroupVisible(browser);
let bandwidthEl = settingGroup.querySelector(
"bandwidth-usage#ipProtectionBandwidth"
);
Assert.ok(bandwidthEl, "bandwidth-usage element should be present");
is_element_visible(
bandwidthEl,
"bandwidth-usage element should be visible"
);
await bandwidthEl.updateComplete;
Assert.equal(
bandwidthEl.bandwidthPercent,
90,
"bandwidthPercent should be 90 when remaining is less than 1 GB"
);
Assert.equal(
bandwidthEl.remainingRounded,
Math.floor(remainingBytes / BANDWIDTH.BYTES_IN_MB),
"remainingRounded should be in MB when remaining is less than 1 GB"
);
Assert.equal(
bandwidthEl.description.getAttribute("data-l10n-id"),
"ip-protection-bandwidth-left-mb",
"Should use the MB l10n string when remaining is less than 1 GB"
);
}
);
await SpecialPowers.popPrefEnv();
});