/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; add_setup(async function () { await SpecialPowers.pushPrefEnv({ set: [["test.wait300msAfterTabSwitch", true]], }); }); add_task(async function test_about_glean_redesign_views_hidden_behind_pref() { registerCleanupFunction(() => { Services.prefs.clearUserPref("about.glean.redesign.enabled"); }); await BrowserTestUtils.withNewTab("about:glean", async function (browser) { ok(!browser.isRemoteBrowser, "Browser should not be remote."); await SpecialPowers.spawn(browser, [], async function () { let metrics_table_category_button = content.document.getElementById( "category-metrics-table" ); is(metrics_table_category_button, null); }); }); await BrowserTestUtils.withNewTab("about:glean", async function (browser) { ok(!browser.isRemoteBrowser, "Browser should not be remote."); await SpecialPowers.spawn(browser, [], async function () { content.document.getElementById("enable-new-features").click(); let metrics_table_category_button = content.document.getElementById( "category-metrics-table" ); Assert.notEqual( metrics_table_category_button, null, "Metrics table category button should not be null" ); }); }); }); add_task(async function test_about_glean_metrics_table_loads_dynamically() { registerCleanupFunction(() => { Services.prefs.clearUserPref("about.glean.redesign.enabled"); }); Services.prefs.setBoolPref("about.glean.redesign.enabled", true); await BrowserTestUtils.withNewTab("about:glean", async function (browser) { ok(!browser.isRemoteBrowser, "Browser should not be remote."); await SpecialPowers.spawn(browser, [], async function () { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); content.document.getElementById("category-metrics-table").click(); let tableContainer, tableBody; const fetchTableBody = () => { tableContainer = content.document.getElementById( "metrics-table-instance" ); tableBody = content.document.getElementById("metrics-table-body"); }; fetchTableBody(); let currentChildrenLength = tableBody.children.length; const tableChildrenLengthChanged = () => { fetchTableBody(); if (currentChildrenLength != tableBody.children.length) { currentChildrenLength = tableBody.children.length; return true; } return false; }; let currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; const tableFirstChildChanged = () => { fetchTableBody(); if ( currentFirstChild != tableBody.children[0].attributes["data-d3-row"].value ) { currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; return true; } return false; }; Assert.equal( currentChildrenLength, 200, "Table should start with 200 elements in it" ); // Scroll and extend the count to 300 tableContainer.scrollTo({ top: tableContainer.scrollHeight - 1000, behavior: "instant", }); await TestUtils.waitForCondition( tableChildrenLengthChanged, "Wait for table children length to change", 200, 5 ); Assert.equal( tableBody.children.length, 300, "Table should now have 300 elements in it" ); // Scroll and extend the count to 400 tableContainer.scrollTo({ top: tableContainer.scrollHeight - 1000, behavior: "instant", }); await TestUtils.waitForCondition( tableChildrenLengthChanged, "Wait for table children length to change", 200, 5 ); Assert.equal( tableBody.children.length, 400, "Table should now have 400 elements in it" ); // Scroll and extend the count to 500 tableContainer.scrollTo({ top: tableContainer.scrollHeight - 1000, behavior: "instant", }); await TestUtils.waitForCondition( tableChildrenLengthChanged, "Wait for table children length to change", 200, 5 ); Assert.equal( tableBody.children.length, 500, "Table should now have 500 elements in it" ); // Scroll offset the metrics by 100 tableContainer.scrollTo({ top: tableContainer.scrollHeight - 1000, behavior: "instant", }); await TestUtils.waitForCondition( tableFirstChildChanged, "Wait for the table's first child to change", 100, 3 ); Assert.equal( tableBody.children.length, 500, "Table should still have 500 elements in it" ); }); }); }); add_task(async function test_about_glean_histogram() { Services.fog.testResetFOG(); registerCleanupFunction(() => { Services.prefs.clearUserPref("about.glean.redesign.enabled"); }); Services.prefs.setBoolPref("about.glean.redesign.enabled", true); await BrowserTestUtils.withNewTab("about:glean", async function (browser) { ok(!browser.isRemoteBrowser, "Browser should not be remote."); await SpecialPowers.spawn(browser, [], async function () { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); content.document.getElementById("category-metrics-table").click(); let tableBody; const fetchTableBody = () => { tableBody = content.document.getElementById("metrics-table-body"); }; fetchTableBody(); let currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; const tableFirstChildChanged = () => { fetchTableBody(); if ( currentFirstChild != tableBody.children[0].attributes["data-d3-row"].value ) { currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; return true; } return false; }; const input = content.document.getElementById("filter-metrics"); input.value = "aCustomDist"; input.dispatchEvent(new content.Event("input")); await TestUtils.waitForCondition( tableFirstChildChanged, "Wait for the table's first child to change", 100, 3 ); }); Glean.testOnlyIpc.aCustomDist.accumulateSamples([0, 0, 1, 1, 1]); await SpecialPowers.spawn(browser, [], async function () { content.document .querySelector( "[data-d3-row='testOnlyIpc.aCustomDist'] button[data-l10n-id='about-glean-button-load-value']" ) .click(); let valueCell; const getValueCell = () => { valueCell = content.document.querySelector( "[data-d3-row='testOnlyIpc.aCustomDist'] [data-d3-cell='value']" ); }; getValueCell(); Assert.equal(valueCell.childElementCount, 1); Assert.equal(valueCell.firstChild.tagName, "svg"); // td svg g.boxes g*n >rect+text+text const boxes = valueCell.firstChild.firstChild.children; Assert.equal(boxes.length, 2, "Bar graphs for bucket 0 and 1 shown"); Assert.equal(boxes[0].children[1].textContent, "2"); Assert.equal(boxes[0].children[2].textContent, "0"); Assert.equal(boxes[1].children[1].textContent, "3"); Assert.equal(boxes[1].children[2].textContent, "1"); }); Services.fog.testResetFOG(); await SpecialPowers.spawn(browser, [], async function () { content.document .querySelector( "[data-d3-row='testOnlyIpc.aCustomDist'] button[data-l10n-id='about-glean-button-load-value']" ) .click(); const valueCell = content.document.querySelector( "[data-d3-row='testOnlyIpc.aCustomDist'] [data-d3-cell='value']" ); Assert.equal(valueCell.childElementCount, 1); Assert.equal(valueCell.firstChild.tagName, "P"); Assert.equal( valueCell.firstChild.getAttribute("data-l10n-id"), "about-glean-no-data-to-display" ); }); }); }); add_task(async function test_about_glean_event_timeline() { Services.fog.testResetFOG(); registerCleanupFunction(() => { Services.prefs.clearUserPref("about.glean.redesign.enabled"); }); Services.prefs.setBoolPref("about.glean.redesign.enabled", true); await BrowserTestUtils.withNewTab("about:glean", async function (browser) { ok(!browser.isRemoteBrowser, "Browser should not be remote."); await SpecialPowers.spawn(browser, [], async function () { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); content.document.getElementById("category-metrics-table").click(); let tableBody; const fetchTableBody = () => { tableBody = content.document.getElementById("metrics-table-body"); }; fetchTableBody(); let currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; const tableFirstChildChanged = () => { fetchTableBody(); if ( currentFirstChild != tableBody.children[0].attributes["data-d3-row"].value ) { currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; return true; } return false; }; const input = content.document.getElementById("filter-metrics"); input.value = "anEvent"; input.dispatchEvent(new content.Event("input")); await TestUtils.waitForCondition( tableFirstChildChanged, "Wait for the table's first child to change", 100, 3 ); }); let extra = { value: "a value for Telemetry", extra1: "can set extras", extra2: "passing more data", }; Glean.testOnlyIpc.anEvent.record(extra); Glean.testOnlyIpc.anEvent.record(); Glean.testOnlyIpc.anEvent.record(); await SpecialPowers.spawn( browser, [extra.value], async function (extraValue) { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); content.document .querySelector( "[data-d3-row='testOnlyIpc.anEvent'] button[data-l10n-id='about-glean-button-load-value']" ) .click(); let valueCell; const getValueCell = () => { valueCell = content.document.querySelector( "[data-d3-row='testOnlyIpc.anEvent'] [data-d3-cell='value']" ); }; getValueCell(); Assert.equal(valueCell.childElementCount, 2); Assert.equal(valueCell.firstChild.tagName, "svg"); let code = content.document.querySelector( `[data-d3-row='testOnlyIpc.anEvent'] pre>code` ).textContent; const codeChanged = () => { const newCode = content.document.querySelector( `[data-d3-row='testOnlyIpc.anEvent'] pre>code` ).textContent; if (newCode != code) { code = newCode; return true; } return false; }; content.document .querySelector(`[data-d3-row='testOnlyIpc.anEvent'] g.event`) .focus(); await TestUtils.waitForCondition( codeChanged, "Wait for the table row's code textContent to change", 100, 3 ); getValueCell(); Assert.equal(valueCell.childElementCount, 2); Assert.equal(valueCell.lastChild.tagName, "PRE"); Assert.equal( valueCell.lastChild.firstChild.textContent.includes(extraValue), true ); } ); }); }); add_task(async function test_about_glean_ping_groups_and_none_label() { await BrowserTestUtils.withNewTab("about:glean", async browser => { await SpecialPowers.spawn(browser, [], async function () { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); const { Assert } = ChromeUtils.importESModule( "resource://testing-common/Assert.sys.mjs" ); // Wait for the select and optgroups to exist and be populated. await TestUtils.waitForCondition(() => { const doc = content.document; const select = doc.getElementById("ping-names"); const builtin = doc.getElementById("builtin-pings"); const custom = doc.getElementById("custom-pings"); return select && builtin && custom && builtin.children.length >= 1; }, "Wait for ping select to be populated"); const doc = content.document; const builtin = doc.getElementById("builtin-pings"); const custom = doc.getElementById("custom-pings"); // Built-in group should contain metrics/events/baseline and end with the localized 'none' option. const builtinOptions = Array.from(builtin.children); Assert.strictEqual( builtinOptions.length, 4, "Built-in group has 4 options (incl. none)" ); Assert.equal( builtinOptions[0].textContent, "metrics", "Built-ins include 'metrics'" ); Assert.equal( builtinOptions[1].textContent, "events", "Built-ins include 'events'" ); Assert.equal( builtinOptions[2].textContent, "baseline", "Built-ins include 'baseline'" ); // Note - We're checking the value instead of textContent // for the last built in option, as textContent may be localized. Assert.equal( builtinOptions[3].value, "(don't submit any ping)", "Built-ins include '(don't submit any ping)'" ); // Custom group should be alphabetically sorted by displayed label. const customLabels = Array.from(custom.children).map(o => o.textContent); if (customLabels.length >= 2) { const sorted = customLabels.slice().sort((a, b) => a.localeCompare(b)); Assert.deepEqual( customLabels, sorted, "Custom ping options are alphabetically sorted" ); } }); }); }); add_task(async function test_about_glean_metrics_table_settings() { Services.fog.testResetFOG(); registerCleanupFunction(() => { Services.prefs.clearUserPref("about.glean.redesign.enabled"); }); Services.prefs.setBoolPref("about.glean.redesign.enabled", true); await BrowserTestUtils.withNewTab("about:glean", async browser => { await SpecialPowers.spawn(browser, [], async function () { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); const { Assert } = ChromeUtils.importESModule( "resource://testing-common/Assert.sys.mjs" ); content.document.getElementById("category-metrics-table").click(); let tableBody; const fetchTableBody = () => { tableBody = content.document.getElementById("metrics-table-body"); }; fetchTableBody(); let currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; const tableFirstChildChanged = () => { fetchTableBody(); if ( currentFirstChild != tableBody.children[0].attributes["data-d3-row"].value ) { currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; return true; } return false; }; const input = content.document.getElementById("filter-metrics"); input.value = "aBool"; input.dispatchEvent(new content.Event("input")); await TestUtils.waitForCondition( tableFirstChildChanged, "Wait for the table's first child to change", 100, 3 ); content.document .querySelector( "[data-d3-row='testOnlyIpc.aBool'] button[data-l10n-id='about-glean-button-load-value']" ) .click(); content.document.getElementById("metrics-table-settings-button").click(); const checkbox = content.document.getElementById( "settings-hide-empty-value-rows" ); checkbox.checked = true; checkbox.dispatchEvent(new content.Event("input")); const tableNoChildren = () => { fetchTableBody(); if (tableBody.children.length === 0) { return true; } return false; }; await TestUtils.waitForCondition( tableNoChildren, "Wait for the table's children to be empty", 100, 3 ); Assert.equal( content.document.querySelector("[data-d3-row='testOnlyIpc.aBool']"), null, "Data row for `testOnlyIpc.aBool` should not exist" ); }); }); }); add_task( { skip_if: () => true, }, async function test_about_glean_export_button() { Services.fog.testResetFOG(); registerCleanupFunction(() => { Services.prefs.clearUserPref("about.glean.redesign.enabled"); }); Services.prefs.setBoolPref("about.glean.redesign.enabled", true); await BrowserTestUtils.withNewTab("about:glean", async browser => { await SpecialPowers.spawn(browser, [], async function () { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); content.document.getElementById("category-metrics-table").click(); let tableBody; const fetchTableBody = () => { tableBody = content.document.getElementById("metrics-table-body"); }; fetchTableBody(); let currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; const tableFirstChildChanged = () => { fetchTableBody(); if ( currentFirstChild != tableBody.children[0].attributes["data-d3-row"].value ) { currentFirstChild = tableBody.children[0].attributes["data-d3-row"].value; return true; } return false; }; const input = content.document.getElementById("filter-metrics"); input.value = "anEvent"; input.dispatchEvent(new content.Event("input")); await TestUtils.waitForCondition( tableFirstChildChanged, "Wait for the table's first child to change", 100, 3 ); }); const date = new Date(); const extra = { value: "a value for Telemetry", extra1: "can set extras", extra2: "passing more data", }; Glean.testOnlyIpc.anEvent.record(extra); await SpecialPowers.spawn( browser, [date.getTime()], async function (dateTime) { const { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); const { Assert } = ChromeUtils.importESModule( "resource://testing-common/Assert.sys.mjs" ); content.document .querySelector( "[data-d3-row='testOnlyIpc.anEvent'] button[data-l10n-id='about-glean-button-load-value']" ) .click(); content.document.getElementById("export-data").click(); const date = new Date(dateTime); let downloads = undefined, newDownloads = undefined; const downloadDir = Services.dirsvc.get("DfltDwnld", Ci.nsIFile).path; const downloadRegex = /about-glean-export-([\d-\.TZ_:]+)\.json/; const downloadsContainsGleanData = () => { IOUtils.getChildren(downloadDir).then( v => { downloads = v; newDownloads = v .filter(d => downloadRegex.test(d)) .map(d => [ d, new Date(d.match(downloadRegex)[1].replaceAll("_", ":")), ]) .filter(([_, fileDate]) => fileDate - date >= 0); }, reason => { ok(false, reason + (reason.stack ? "\n" + reason.stack : "")); } ); return newDownloads !== undefined && !!newDownloads.length; }; await TestUtils.waitForCondition( downloadsContainsGleanData, "Wait for the downloads to contain new Glean data", 100, 50 ); Assert.equal( 1, newDownloads.length, `New downloads has an unexpected value in list of downloads: ${JSON.stringify(downloads.filter(d => d.includes("glean")))}` ); const data = await IOUtils.readJSON(newDownloads[0][0]); // Clean up downloaded file await IOUtils.remove(newDownloads[0][0]); const index = data.findIndex(d => d.name === "testOnlyIpc.anEvent"); Assert.greaterOrEqual(index, 0); Assert.notEqual(undefined, data[index].value); } ); }); DownloadsPanel.hidePanel(); } );