Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { RemoteSettingsCrashPull } = ChromeUtils.importESModule(
"resource://gre/modules/RemoteSettingsCrashPull.sys.mjs"
);
const { RemoteSettings } = ChromeUtils.importESModule(
);
const { UnsubmittedCrashHandler } = ChromeUtils.importESModule(
"resource:///modules/ContentCrashHandlers.sys.mjs"
);
const kNotificationSelector =
'notification-message[message-bar-type="infobar"]' +
'[value="pending-crash-reports-req"]';
const kRemoteSettingsCollectionName = "crash-reports-ondemand";
let rscp = undefined;
add_setup(async function setup() {
rscp = RemoteSettingsCrashPull;
await SpecialPowers.pushPrefEnv({
set: [["browser.crashReports.requestedNeverShowAgain", false]],
});
});
async function getNotification(shouldBeNull = false) {
await TestUtils.waitForCondition(() => {
if (shouldBeNull) {
return document.querySelector(kNotificationSelector) === null;
}
return document.querySelector(kNotificationSelector) !== null;
}, "Trying to get a notification");
return document.querySelector(kNotificationSelector);
}
add_task(async function test_no_notification() {
Assert.equal(
null,
document.querySelector(kNotificationSelector),
"No existing notification"
);
});
add_task(async function test_no_crash_no_notification() {
const payload = {
current: [],
created: [
{
hashes: [
"2435191bfd64cf0c8cbf0397f1cb5654f778388a3be72cb01502196896f5a0e9",
],
},
],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload,
});
try {
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) !== null,
"Waiting for a notification to have been shown (not good)"
);
} catch (ex) {
if (!ex.includes("timed out after 50 tries")) {
throw ex;
}
}
});
add_task(async function test_one_crash_one_notification() {
const _checkForInterestingUnsubmittedCrash =
rscp.checkForInterestingUnsubmittedCrash;
rscp.checkForInterestingUnsubmittedCrash = async _ => {
return ["989df240-a40c-405a-9a22-f2fc4a31db6c"];
};
const payload = {
current: [],
created: [
{
hashes: [
// value here is not important
"2435191bfd64cf0c8cbf0397f1cb5654f778388a3be72cb01502196896f5a0e9",
],
},
],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload,
});
const notification = await getNotification();
const notificationMsg = notification.shadowRoot.querySelector(".message");
rscp.checkForInterestingUnsubmittedCrash =
_checkForInterestingUnsubmittedCrash;
const msg = notificationMsg.getAttribute("data-l10n-id");
Assert.equal(
msg,
"requested-crash-reports-message",
"Shows requested crash msg"
);
const args = JSON.parse(notificationMsg.getAttribute("data-l10n-args"));
Assert.equal(args.reportCount, 1, "Shows requested crash msg for one crash");
const closeButton = notification.shadowRoot.querySelector(".close");
closeButton.click();
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) === null,
"Waiting for notification to be removed"
);
});
add_task(async function test_multiple_crashes_one_notification() {
const _checkForInterestingUnsubmittedCrash =
rscp.checkForInterestingUnsubmittedCrash;
rscp.checkForInterestingUnsubmittedCrash = async _ => {
return [
"26df2a6c-ff31-477b-b1ad-b2dc10a0a664",
"8c2c68f9-fdc3-4465-999c-3b775ee38dca",
"a60929fd-1f3b-4edf-ae81-64afc25dcbfb",
];
};
const payload = {
current: [],
created: [
{
hashes: [
// values here are not important
"2435191bfd64cf0c8cbf0397f1cb5654f778388a3be72cb01502196896f5a0e9",
"3a8721436b6585d43f8ecd96f9a336df564828285498f3f6c45848aae19d3477",
"e1d015e337acf8e7ce097ca08429594ebb7a8bb01463d2d9f477492abcaa5f8e",
"14d5d4e13e84f85f2553642bfd240f753bfe98b28e14bbe300533f9f25671c72",
"ea5a4e8c59eab078df1e8b645d8637de89f8d9c2ebe5926fe1d1bc02f1f98d25",
"6ab24ad7e2dd92501ccd8274ac4219780ef6628c861986e3e97b673a4fdb2691",
],
},
],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload,
});
const notification = await getNotification();
const notificationMsg = notification.shadowRoot.querySelector(".message");
rscp.checkForInterestingUnsubmittedCrash =
_checkForInterestingUnsubmittedCrash;
const args = JSON.parse(notificationMsg.getAttribute("data-l10n-args"));
Assert.equal(args.reportCount, 3, "Shows requested crash msg for one crash");
const closeButton = notification.shadowRoot.querySelector(".close");
closeButton.click();
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) === null,
"Waiting for notification to be removed"
);
});
add_task(async function test_one_crash_notification_click_send_nothrottle() {
const _checkForInterestingUnsubmittedCrash =
rscp.checkForInterestingUnsubmittedCrash;
rscp.checkForInterestingUnsubmittedCrash = async _ => {
return ["989df240-a40c-405a-9a22-f2fc4a31db6c"];
};
const payload = {
current: [],
created: [
{
hashes: [
// value here is not important
"2435191bfd64cf0c8cbf0397f1cb5654f778388a3be72cb01502196896f5a0e9",
],
},
],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload,
});
const notification = await getNotification();
rscp.checkForInterestingUnsubmittedCrash =
_checkForInterestingUnsubmittedCrash;
const _submitReports = UnsubmittedCrashHandler.submitReports;
let calledWithNoThrottle = false;
UnsubmittedCrashHandler.submitReports = (reportIDs, from, obj) => {
if ("noThrottle" in obj && obj.noThrottle) {
calledWithNoThrottle = true;
}
};
const sendButton = notification.querySelector(
'button[data-l10n-id="pending-crash-reports-send"]'
);
sendButton.click();
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) === null,
"Waiting for notification to be removed"
);
await TestUtils.waitForCondition(
() => calledWithNoThrottle,
"Waiting for notification send to call submitReports(noThrottle)"
);
UnsubmittedCrashHandler.submitReports = _submitReports;
});
add_task(async function test_multiple_crashes_notification_merge() {
const _checkForInterestingUnsubmittedCrash =
rscp.checkForInterestingUnsubmittedCrash;
rscp.checkForInterestingUnsubmittedCrash = async records => {
if (records[0].hashes[0] === "1") {
return ["32721fda-e22a-4a49-bee3-9d4c71f43605"];
}
return ["b36dc3a6-d5f0-4afa-acb4-29f6eccb8282"];
};
const payload1 = {
current: [],
created: [{ hashes: ["1"] }],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload1,
});
const notification1 = await getNotification();
const notification1Msg = notification1.shadowRoot.querySelector(".message");
const msg1 = notification1Msg.getAttribute("data-l10n-id");
Assert.equal(
msg1,
"requested-crash-reports-message",
"Shows requested crash msg"
);
const args1 = JSON.parse(notification1Msg.getAttribute("data-l10n-args"));
Assert.equal(args1.reportCount, 1, "Shows requested crash msg for one crash");
const payload2 = {
current: [],
created: [{ hashes: ["2"] }],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload2,
});
const notification2 = await getNotification();
const notification2Msg = notification2.shadowRoot.querySelector(".message");
const msg2 = notification2Msg.getAttribute("data-l10n-id");
Assert.equal(
msg2,
"requested-crash-reports-message",
"Shows requested crash msg"
);
const args2 = JSON.parse(notification2Msg.getAttribute("data-l10n-args"));
Assert.equal(
args2.reportCount,
2,
"Shows requested crash msg for two crashes"
);
rscp.checkForInterestingUnsubmittedCrash =
_checkForInterestingUnsubmittedCrash;
const _submitReports = UnsubmittedCrashHandler.submitReports;
let calledWithTwoReports = false;
UnsubmittedCrashHandler.submitReports = reportIDs => {
if (reportIDs.length === 2) {
calledWithTwoReports = true;
}
};
const sendButton = notification2.querySelector(
'button[data-l10n-id="pending-crash-reports-send"]'
);
sendButton.click();
await TestUtils.waitForCondition(
() => calledWithTwoReports,
"Waiting for notification send to call calledWithTwoReports"
);
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) === null,
"Waiting for notification to be removed"
);
UnsubmittedCrashHandler.submitReports = _submitReports;
});
add_task(
async function test_one_crash_one_notification_no_pending_unsubmitted() {
Assert.ok(
UnsubmittedCrashHandler.shouldShowPendingSubmissionsNotification(),
"Scheduled check would show an unsubmitted notification"
);
const _checkForInterestingUnsubmittedCrash =
rscp.checkForInterestingUnsubmittedCrash;
rscp.checkForInterestingUnsubmittedCrash = async _ => {
return ["989df240-a40c-405a-9a22-f2fc4a31db6c"];
};
const payload = {
current: [],
created: [
{
hashes: [
// value here is not important
"2435191bfd64cf0c8cbf0397f1cb5654f778388a3be72cb01502196896f5a0e9",
],
},
],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload,
});
const notification = await getNotification();
const notificationMsg = notification.shadowRoot.querySelector(".message");
rscp.checkForInterestingUnsubmittedCrash =
_checkForInterestingUnsubmittedCrash;
const msg = notificationMsg.getAttribute("data-l10n-id");
Assert.equal(
msg,
"requested-crash-reports-message",
"Shows requested crash msg"
);
const args = JSON.parse(notificationMsg.getAttribute("data-l10n-args"));
Assert.equal(
args.reportCount,
1,
"Shows requested crash msg for one crash"
);
Assert.ok(
!UnsubmittedCrashHandler.shouldShowPendingSubmissionsNotification(),
"Scheduled check would NOT show an unsubmitted notification when we show requested notification"
);
const closeButton = notification.shadowRoot.querySelector(".close");
closeButton.click();
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) === null,
"Waiting for notification to be removed"
);
Assert.ok(
UnsubmittedCrashHandler.shouldShowPendingSubmissionsNotification(),
"Scheduled check would show an unsubmitted notification again"
);
}
);
add_task(
async function test_one_crash_notification_click_never_show_again_set_pref() {
Assert.equal(
Services.prefs.getBoolPref(
"browser.crashReports.requestedNeverShowAgain"
),
false,
"Pref is disabled"
);
const _checkForInterestingUnsubmittedCrash =
rscp.checkForInterestingUnsubmittedCrash;
rscp.checkForInterestingUnsubmittedCrash = async _ => {
return ["989df240-a40c-405a-9a22-f2fc4a31db6c"];
};
const payload = {
current: [],
created: [
{
hashes: [
// value here is not important
"2435191bfd64cf0c8cbf0397f1cb5654f778388a3be72cb01502196896f5a0e9",
],
},
],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload,
});
const notification = await getNotification();
rscp.checkForInterestingUnsubmittedCrash =
_checkForInterestingUnsubmittedCrash;
const disableButton = notification.querySelector(
'button[data-l10n-id="requested-crash-reports-dont-show-again"]'
);
disableButton.click();
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) === null,
"Waiting for notification to be removed"
);
Assert.equal(
Services.prefs.getBoolPref(
"browser.crashReports.requestedNeverShowAgain"
),
true,
"Pref is enabled"
);
}
);
add_task(async function test_one_crash_notification_never_show_again() {
Services.prefs.setBoolPref(
"browser.crashReports.requestedNeverShowAgain",
true
);
const _checkForInterestingUnsubmittedCrash =
rscp.checkForInterestingUnsubmittedCrash;
rscp.checkForInterestingUnsubmittedCrash = async _ => {
return ["989df240-a40c-405a-9a22-f2fc4a31db6c"];
};
const payload = {
current: [],
created: [
{
hashes: [
// value here is not important
"2435191bfd64cf0c8cbf0397f1cb5654f778388a3be72cb01502196896f5a0e9",
],
},
],
updated: [],
deleted: [],
};
await RemoteSettings(kRemoteSettingsCollectionName).emit("sync", {
data: payload,
});
rscp.checkForInterestingUnsubmittedCrash =
_checkForInterestingUnsubmittedCrash;
try {
await TestUtils.waitForCondition(
() => document.querySelector(kNotificationSelector) !== null,
"Waiting for notification to be shown (should not)"
);
} catch (ex) {
if (!ex.includes("timed out after 50 tries")) {
throw ex;
}
}
});