Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { PromptTestUtils } = ChromeUtils.importESModule(
);
const LOG_DIR = PathUtils.join(PathUtils.profileDir, "weave", "logs");
const DAY_MS = 24 * 60 * 60 * 1000;
const NOW = Date.now();
// One recent error log, one recent success log, and one error log old enough to
// be excluded by the "last 7 days" date filter.
const TEST_LOGS = [
{
name: `error-sync-${NOW}.txt`,
contents: "1481 Sync.Service ERROR something failed\nstack frame\n",
},
{
name: `success-sync-${NOW - 1}.txt`,
contents: "1481 Sync.Service DEBUG all good here\n",
},
{
name: `error-sync-${NOW - 10 * DAY_MS}.txt`,
contents: "1481 Sync.Service ERROR ancient failure\n",
},
];
async function setupLogs() {
await IOUtils.makeDirectory(LOG_DIR, { ignoreExisting: true });
// Start from a clean slate so row counts are deterministic.
for (const path of await IOUtils.getChildren(LOG_DIR)) {
await IOUtils.remove(path);
}
for (const log of TEST_LOGS) {
await IOUtils.writeUTF8(PathUtils.join(LOG_DIR, log.name), log.contents);
}
}
registerCleanupFunction(async () => {
try {
await IOUtils.remove(LOG_DIR, { recursive: true });
} catch (ex) {
// Directory may not exist; nothing to clean up.
}
});
function rows(doc) {
return doc.querySelectorAll(".log-row");
}
function dispatch(doc, el, type) {
el.dispatchEvent(new doc.defaultView.Event(type, { bubbles: true }));
}
function search(doc, value) {
const input = doc.getElementById("filter-search");
input.value = value;
dispatch(doc, input, "MozInputSearch:search");
}
async function withPage(taskFn) {
await setupLogs();
await BrowserTestUtils.withNewTab("about:sync-log", async browser => {
const doc = browser.contentDocument;
await TestUtils.waitForCondition(
() => rows(doc).length === TEST_LOGS.length,
"all test logs are rendered"
);
await taskFn(doc, browser);
});
}
add_task(async function test_logs_are_listed() {
await withPage(async doc => {
is(rows(doc).length, 3, "All three logs are listed");
const typeFilter = doc.getElementById("filter-type");
is(typeFilter.value, "all", "The type filter defaults to all logs");
ok(
doc.querySelector("moz-radio[value=all]").checked,
"The all logs radio is checked by default"
);
});
});
add_task(async function test_filters() {
await withPage(async doc => {
doc.querySelector("moz-radio[value=success]").click();
await TestUtils.waitForCondition(
() => rows(doc).length === 1,
"Only the success log remains"
);
doc.querySelector("moz-radio[value=all]").click();
await TestUtils.waitForCondition(
() => rows(doc).length === 3,
"All logs are shown again"
);
const select = doc.getElementById("filter-date");
select.value = "7";
dispatch(doc, select, "change");
await TestUtils.waitForCondition(
() => rows(doc).length === 2,
"The 10-day-old log is filtered out"
);
select.value = "all";
dispatch(doc, select, "change");
search(doc, "all good here");
await TestUtils.waitForCondition(
() => rows(doc).length === 1,
"Search matches the success log by contents"
);
});
});
add_task(async function test_search() {
await withPage(async doc => {
// Matches a filename.
search(doc, "success");
await TestUtils.waitForCondition(
() => rows(doc).length === 1,
"Search matches the success log by filename"
);
// Matches log contents.
search(doc, "all good here");
await TestUtils.waitForCondition(
() =>
rows(doc).length === 1 &&
rows(doc)[0].querySelector(".log-badge").classList.contains("success"),
"Search matches the success log by contents"
);
search(doc, "does not exist");
await TestUtils.waitForCondition(
() =>
!rows(doc).length &&
!doc.getElementById("empty-state").hidden &&
doc.getElementById("empty-state").textContent ===
"No logs match the current filters.",
"An unmatched search shows the filtered empty state"
);
is(
doc.getElementById("empty-state").textContent,
"No logs match the current filters.",
"The filtered empty state differs from an empty log directory"
);
});
});
add_task(async function test_inline_viewer() {
await withPage(async doc => {
const errorRow = [...rows(doc)].find(row =>
row.querySelector(".log-badge").classList.contains("error")
);
const details = errorRow.querySelector(".log-row-details");
const summary = errorRow.querySelector(".log-row-header");
ok(!details.open, "The row starts collapsed");
summary.click();
await TestUtils.waitForCondition(
() => details.open && errorRow.querySelector(".log-line"),
"The log contents render when the row is expanded"
);
ok(
errorRow.querySelector(".log-line.error"),
"Error lines are highlighted in the inline viewer"
);
summary.click();
await TestUtils.waitForCondition(
() => !details.open,
"The row collapses again"
);
});
});
add_task(async function test_clear_logs() {
await withPage(async doc => {
const promptPromise = PromptTestUtils.handleNextPrompt(
null,
{ modalType: Services.prompt.MODAL_TYPE_WINDOW },
{ buttonNumClick: 0 }
);
doc.getElementById("clear-button").click();
await promptPromise;
await TestUtils.waitForCondition(
() => !rows(doc).length && !doc.getElementById("empty-state").hidden,
"All logs are cleared and the empty state is shown"
);
const remaining = await IOUtils.getChildren(LOG_DIR);
is(remaining.length, 0, "Log files are deleted from disk");
});
});