Bug 1933104 - Update tree-view rows at most once per frame. r=aleca,mkmelin
Differential Revision: https://phabricator.services.mozilla.com/D230066 --HG-- extra : amend_source : b0c8c2ea4312a7ff9c220c97127a9d8ba5c2b205
This commit is contained in:
Родитель
28d8dc4a61
Коммит
f1f5234eb1
|
@ -4398,8 +4398,10 @@ var threadPane = {
|
|||
threadTree.invalidate();
|
||||
break;
|
||||
case "custom-column-refreshed":
|
||||
// Invalidate only the column specified in data.
|
||||
threadTree.invalidate(data);
|
||||
// Invalidate the whole thing. This used to refresh just the column,
|
||||
// but now that filling the cells happens asynchronously, that's too
|
||||
// complicated. Kept for add-on compatibility.
|
||||
threadTree.invalidate();
|
||||
break;
|
||||
case "custom-column-added":
|
||||
this.addCustomColumn(data);
|
||||
|
|
|
@ -417,14 +417,10 @@ class AutoTreeViewTableRow extends TreeViewTableRow {
|
|||
}
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
|
||||
this.dataset.properties = this.view.getRowProperties(index);
|
||||
this.dataset.properties = this.view.getRowProperties(this._index);
|
||||
|
||||
for (const column of this.list.table.columns) {
|
||||
const cell = this.querySelector(`.${column.id.toLowerCase()}-column`);
|
||||
|
@ -433,7 +429,7 @@ class AutoTreeViewTableRow extends TreeViewTableRow {
|
|||
continue;
|
||||
}
|
||||
|
||||
const text = this.view.getCellText(index, column.id);
|
||||
const text = this.view.getCellText(this._index, column.id);
|
||||
cell.textContent = text;
|
||||
if (column.l10n.cell) {
|
||||
document.l10n.setAttributes(cell, column.l10n.cell, { title: text });
|
||||
|
|
|
@ -740,16 +740,9 @@ export class TreeView extends HTMLElement {
|
|||
* Updates all existing rows in place, without removing all the rows and
|
||||
* starting again. This can be used if the row element class hasn't changed
|
||||
* and its `index` setter is capable of handling any modifications required.
|
||||
*
|
||||
* @param {string} [columnId] - Optional column id, to limit invalidation to a
|
||||
* single column.
|
||||
*/
|
||||
invalidate(columnId) {
|
||||
this.invalidateRange(
|
||||
this.#firstBufferRowIndex,
|
||||
this.#lastBufferRowIndex,
|
||||
columnId
|
||||
);
|
||||
invalidate() {
|
||||
this.invalidateRange(this.#firstBufferRowIndex, this.#lastBufferRowIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -758,20 +751,16 @@ export class TreeView extends HTMLElement {
|
|||
* on its own.
|
||||
*
|
||||
* @param {integer} index
|
||||
* @param {string} [columnId] - Optional column id, to limit invalidation to a
|
||||
* single column.
|
||||
*/
|
||||
#doInvalidateRow(index, columnId) {
|
||||
#doInvalidateRow(index) {
|
||||
const rowCount = this._view?.rowCount ?? 0;
|
||||
const row = this.getRowAtIndex(index);
|
||||
if (row) {
|
||||
if (index >= rowCount) {
|
||||
this._removeRowAtIndex(index);
|
||||
} else {
|
||||
row.invalidateSingleColumn = columnId;
|
||||
row.index = index;
|
||||
row.selected = this._selection.isSelected(index);
|
||||
row.invalidateSingleColumn = null;
|
||||
}
|
||||
} else if (
|
||||
index >= this.#firstBufferRowIndex &&
|
||||
|
@ -786,17 +775,15 @@ export class TreeView extends HTMLElement {
|
|||
*
|
||||
* @param {integer} startIndex
|
||||
* @param {integer} endIndex
|
||||
* @param {string} [columnId] - Optional column id, to limit invalidation to a
|
||||
* single column.
|
||||
*/
|
||||
invalidateRange(startIndex, endIndex, columnId) {
|
||||
invalidateRange(startIndex, endIndex) {
|
||||
for (
|
||||
let index = Math.max(startIndex, this.#firstBufferRowIndex),
|
||||
last = Math.min(endIndex, this.#lastBufferRowIndex);
|
||||
index <= last;
|
||||
index++
|
||||
) {
|
||||
this.#doInvalidateRow(index, columnId);
|
||||
this.#doInvalidateRow(index);
|
||||
}
|
||||
this._ensureVisibleRowsAreDisplayed();
|
||||
}
|
||||
|
@ -2724,19 +2711,16 @@ export class TreeViewTableRow extends HTMLTableRowElement {
|
|||
this.setAttribute("aria-selected", !!this.selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of a column. If set, the index setter will only update the specified
|
||||
* column.
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
invalidateSingleColumn = null;
|
||||
|
||||
/**
|
||||
* The 0-based position of this row in the list. Override this setter to
|
||||
* fill layout based on values from the list's view. Always call back to
|
||||
* this class's getter/setter when inheriting.
|
||||
*
|
||||
* Setting the index doesn't instantly fill the row. That happens at the
|
||||
* next animation frame using the most recently set index. Tests that set
|
||||
* the index will also need to wait for an animation frame before checking
|
||||
* the row's content.
|
||||
*
|
||||
* @note Don't short-circuit the setter if the given index is equal to the
|
||||
* existing index. Rows can be reused to display new data at the same index.
|
||||
*
|
||||
|
@ -2746,27 +2730,50 @@ export class TreeViewTableRow extends HTMLTableRowElement {
|
|||
return this._index;
|
||||
}
|
||||
|
||||
#animationFrame = null;
|
||||
|
||||
set index(index) {
|
||||
this._index = index;
|
||||
|
||||
// Wait before filling the row. This setter could be called many times
|
||||
// before it even appears on the screen, and calling (potentially very
|
||||
// expensive) code each time would be a waste.
|
||||
if (!this.#animationFrame) {
|
||||
this.#animationFrame = requestAnimationFrame(() => {
|
||||
this.#animationFrame = null;
|
||||
// The row may no longer be attached to the tree. Don't waste time
|
||||
// filling it in that case.
|
||||
if (this.parentNode) {
|
||||
this._fillRow();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill out the row with content based on the current value of `this._index`.
|
||||
* Subclasses should override this setter and call back to it.
|
||||
*/
|
||||
_fillRow() {
|
||||
this.setAttribute(
|
||||
"role",
|
||||
this.list.table.body.getAttribute("role") === "treegrid"
|
||||
? "row"
|
||||
: "option"
|
||||
);
|
||||
this.setAttribute("aria-posinset", index + 1);
|
||||
this.id = `${this.list.id}-row${index}`;
|
||||
this.setAttribute("aria-posinset", this._index + 1);
|
||||
this.id = `${this.list.id}-row${this._index}`;
|
||||
|
||||
const isGroup = this.view.isContainer(index);
|
||||
const isGroup = this.view.isContainer(this._index);
|
||||
this.classList.toggle("children", isGroup);
|
||||
|
||||
const isGroupOpen = this.view.isContainerOpen(index);
|
||||
const isGroupOpen = this.view.isContainerOpen(this._index);
|
||||
if (isGroup) {
|
||||
this.setAttribute("aria-expanded", isGroupOpen);
|
||||
} else {
|
||||
this.removeAttribute("aria-expanded");
|
||||
}
|
||||
this.classList.toggle("collapsed", !isGroupOpen);
|
||||
this._index = index;
|
||||
|
||||
const table = this.closest("table");
|
||||
for (const column of table.columns) {
|
||||
|
@ -2803,7 +2810,7 @@ export class TreeViewTableRow extends HTMLTableRowElement {
|
|||
}
|
||||
document.l10n.setAttributes(
|
||||
img,
|
||||
this.list._selection.isSelected(index)
|
||||
this.list._selection.isSelected(this._index)
|
||||
? "tree-list-view-row-deselect"
|
||||
: "tree-list-view-row-select"
|
||||
);
|
||||
|
|
|
@ -49,12 +49,8 @@ class ThreadCard extends TreeViewTableRow {
|
|||
this.sortHeaderDetails = this.querySelector(".sort-header-details");
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
// XPCOM calls here must be keep to a minimum. Collect all of the
|
||||
// required data in one go.
|
||||
|
@ -62,7 +58,7 @@ class ThreadCard extends TreeViewTableRow {
|
|||
const threadLevel = {};
|
||||
|
||||
const cellTexts = this.view.cellDataForColumns(
|
||||
index,
|
||||
this._index,
|
||||
window.threadPane.cardColumns,
|
||||
properties,
|
||||
threadLevel
|
||||
|
|
|
@ -31,22 +31,11 @@ class ThreadRow extends TreeViewTableRow {
|
|||
);
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
|
||||
// Check if a only a single column should be updated.
|
||||
const columns = this.invalidateSingleColumn
|
||||
? window.threadPane.columns.filter(
|
||||
column => column.id == this.invalidateSingleColumn
|
||||
)
|
||||
: window.threadPane.columns;
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
const textColumns = [];
|
||||
for (const column of columns) {
|
||||
for (const column of window.threadPane.columns) {
|
||||
// No need to update the text of this cell if it's hidden, the selection
|
||||
// column, or a non-custom icon column that doesn't match a specific flag.
|
||||
if (column.hidden || (!column.custom && column.icon) || column.select) {
|
||||
|
@ -60,7 +49,7 @@ class ThreadRow extends TreeViewTableRow {
|
|||
const properties = {};
|
||||
const threadLevel = {};
|
||||
const cellTexts = this.view.cellDataForColumns(
|
||||
index,
|
||||
this._index,
|
||||
textColumns,
|
||||
properties,
|
||||
threadLevel
|
||||
|
@ -75,7 +64,7 @@ class ThreadRow extends TreeViewTableRow {
|
|||
|
||||
this.dataset.properties = properties.value.trim();
|
||||
|
||||
for (const column of columns) {
|
||||
for (const column of window.threadPane.columns) {
|
||||
// Skip this column if it's hidden.
|
||||
if (column.hidden) {
|
||||
continue;
|
||||
|
|
|
@ -138,6 +138,7 @@ add_task(async function testSwitchToCardsView() {
|
|||
);
|
||||
EventUtils.synthesizeKey("KEY_Escape", {});
|
||||
await hiddenPromise;
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
Assert.equal(
|
||||
threadTree.getAttribute("rows"),
|
||||
|
|
|
@ -70,8 +70,9 @@ add_task(async function test_dummy_row_in_table_view() {
|
|||
() => threadTree.getAttribute("rows") == "thread-row",
|
||||
"The tree view switched to a table layout"
|
||||
);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
Assert.deepEqual(
|
||||
Assert.equal(
|
||||
threadTree.getRowAtIndex(0).querySelector(".subject-line span").textContent,
|
||||
`Older (${folderAMessages.length}/${folderAMessages.length})`,
|
||||
"The subject text with the unread and total counter should match"
|
||||
|
@ -84,6 +85,7 @@ add_task(async function test_dummy_row_in_table_view() {
|
|||
// from this loop.
|
||||
for (let i = 1; i < folderAMessages.length; i++) {
|
||||
folderAMessages[i].markRead(true);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
Assert.equal(
|
||||
threadTree.getRowAtIndex(0).querySelector(".subject-line span")
|
||||
.textContent,
|
||||
|
@ -92,6 +94,7 @@ add_task(async function test_dummy_row_in_table_view() {
|
|||
}
|
||||
|
||||
folderAMessages[0].markRead(true);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
Assert.equal(
|
||||
threadTree.getRowAtIndex(0).querySelector(".subject-line span").textContent,
|
||||
`Older (${folderAMessages.length})`
|
||||
|
|
|
@ -675,6 +675,7 @@ add_task(async function testMultipleMessages() {
|
|||
!threadTree.getRowAtIndex(threadTree.currentIndex),
|
||||
"Current row is scrolled out of view"
|
||||
);
|
||||
await new Promise(resolve => window.requestAnimationFrame(resolve));
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
threadTree,
|
||||
|
|
|
@ -142,6 +142,7 @@ add_task(async function () {
|
|||
|
||||
emily.displayName = "I'm Emily!";
|
||||
book.modifyCard(emily);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
row = about3Pane.threadTree.getRowAtIndex(2);
|
||||
Assert.equal(
|
||||
|
@ -179,6 +180,7 @@ add_task(async function () {
|
|||
|
||||
emily.displayName = "";
|
||||
book.modifyCard(emily);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
row = about3Pane.threadTree.getRowAtIndex(2);
|
||||
Assert.equal(
|
||||
|
@ -216,6 +218,7 @@ add_task(async function () {
|
|||
|
||||
emily.displayName = "I'm Emily!";
|
||||
book.modifyCard(emily);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
row = about3Pane.threadTree.getRowAtIndex(2);
|
||||
Assert.equal(
|
||||
|
@ -253,6 +256,7 @@ add_task(async function () {
|
|||
|
||||
felix.displayName = "Felix's Flower Co.";
|
||||
book.modifyCard(felix);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
row = about3Pane.threadTree.getRowAtIndex(2);
|
||||
Assert.equal(
|
||||
|
@ -275,6 +279,7 @@ add_task(async function () {
|
|||
|
||||
felix.displayName = "";
|
||||
book.modifyCard(felix);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
row = about3Pane.threadTree.getRowAtIndex(2);
|
||||
Assert.equal(
|
||||
|
@ -456,6 +461,7 @@ add_task(async function () {
|
|||
// Prefer email only. Changing the preference causes the message to reload.
|
||||
Services.prefs.setIntPref("mail.addressDisplayFormat", 1);
|
||||
await BrowserTestUtils.browserLoaded(messagePaneBrowser);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
row = about3Pane.threadTree.getRowAtIndex(3);
|
||||
Assert.equal(
|
||||
|
@ -472,6 +478,7 @@ add_task(async function () {
|
|||
// Prefer name only. Changing the preference causes the message to reload.
|
||||
Services.prefs.setIntPref("mail.addressDisplayFormat", 2);
|
||||
await BrowserTestUtils.browserLoaded(messagePaneBrowser);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
row = about3Pane.threadTree.getRowAtIndex(3);
|
||||
Assert.equal(
|
||||
|
|
|
@ -384,6 +384,7 @@ async function subtest() {
|
|||
() => threadTree.table.body.rows.length == 15,
|
||||
"waiting for all of the table rows"
|
||||
);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
const dbView = about3Pane.gDBView;
|
||||
const subjects = [];
|
||||
|
@ -489,6 +490,7 @@ async function _doDelete(callback, index, expectedLoad) {
|
|||
}
|
||||
|
||||
threadTree.removeEventListener("select", onSelect);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
async function doDeleteCommand(expectedLoad) {
|
||||
|
|
|
@ -581,6 +581,7 @@ add_task(async function testThreadTreeA11yRoles() {
|
|||
() => threadTree.getRowAtIndex(0),
|
||||
"row0 should become available"
|
||||
);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
Assert.equal(
|
||||
threadTree.getRowAtIndex(0).getAttribute("role"),
|
||||
"row",
|
||||
|
@ -602,6 +603,7 @@ add_task(async function testThreadTreeA11yRoles() {
|
|||
() => threadTree.getRowAtIndex(0),
|
||||
"row0 should become available"
|
||||
);
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
Assert.equal(
|
||||
threadTree.getRowAtIndex(0).getAttribute("role"),
|
||||
"row",
|
||||
|
@ -623,6 +625,7 @@ add_task(async function test_read_new_properties() {
|
|||
|
||||
// Clicking the twisty to collapse a row should update the message display.
|
||||
goDoCommand("cmd_expandAllThreads");
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
threadTree.selectedIndex = 5;
|
||||
await messageLoaded(10);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ add_setup(async function () {
|
|||
messagePaneVisible: false,
|
||||
folderURI: testFolder.URI,
|
||||
});
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
document.getElementById("toolbar-menubar").removeAttribute("autohide");
|
||||
|
||||
|
@ -132,7 +132,10 @@ add_task(async function testColumnHeaderClick() {
|
|||
const targetMessage = messagesByDate[49];
|
||||
info(`selecting message "${targetMessage.subject}"`);
|
||||
threadTree.scrollToIndex(49, true);
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// Wait once for the scroll...
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
// ... and once for the row to be filled.
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
threadTree.selectedIndex = 49;
|
||||
verifySelection([49], [targetMessage.subject]);
|
||||
|
||||
|
@ -174,7 +177,10 @@ add_task(async function testColumnHeaderClick() {
|
|||
`selecting messages "${targetMessages.map(m => m.subject).join('", "')}"`
|
||||
);
|
||||
threadTree.scrollToIndex(83, true);
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
// Wait once for the scroll...
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
// ... and once for the rows to be filled.
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
threadTree.selectedIndices = [80, 81, 83];
|
||||
verifySelection(
|
||||
[80, 81, 83],
|
||||
|
|
|
@ -300,6 +300,7 @@ add_task(async function testIconsUnThreaded() {
|
|||
);
|
||||
|
||||
goDoCommand("cmd_sort", { target: { value: "unthreaded" } });
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
// Switched to unthreaded and test again.
|
||||
threadTree.selectedIndex = 0;
|
||||
|
@ -349,7 +350,7 @@ async function checkContextMenu(index, expectedStates, itemToActivate) {
|
|||
contextMenu.hidePopup();
|
||||
}
|
||||
await BrowserTestUtils.waitForPopupEvent(contextMenu, "hidden");
|
||||
await new Promise(resolve => window.requestAnimationFrame(resolve));
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
async function checkMessageMenu(expectedStates) {
|
||||
|
|
|
@ -205,6 +205,7 @@ add_task(async function () {
|
|||
checkPersistedValue("sortDirection", "");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(headerButtons[1], {}, win);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["violet", "africa", "", "", ""],
|
||||
["yellow", "antarctica", "", "", ""],
|
||||
|
@ -219,6 +220,7 @@ add_task(async function () {
|
|||
checkPersistedValue("sortDirection", "ascending");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(headerButtons[1], {}, win);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["orange", "south america", "", "", ""],
|
||||
["red", "north america", "", "", ""],
|
||||
|
@ -233,6 +235,7 @@ add_task(async function () {
|
|||
checkPersistedValue("sortDirection", "descending");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(headerButtons[0], {}, win);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "asia", "", "", ""],
|
||||
["green", "australia", "", "", ""],
|
||||
|
@ -247,6 +250,7 @@ add_task(async function () {
|
|||
checkPersistedValue("sortDirection", "ascending");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(headerButtons[0], {}, win);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["yellow", "antarctica", "", "", ""],
|
||||
["violet", "africa", "", "", ""],
|
||||
|
@ -261,6 +265,7 @@ add_task(async function () {
|
|||
checkPersistedValue("sortDirection", "descending");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(headerButtons[0], {}, win);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "asia", "", "", ""],
|
||||
["green", "australia", "", "", ""],
|
||||
|
@ -312,6 +317,7 @@ add_task(async function () {
|
|||
"columns",
|
||||
"colour,continent,sin:hidden,wonder,dwarf:hidden"
|
||||
);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "asia", "", "temple of artemis", ""],
|
||||
["green", "australia", "", "mausoleum of halicarnassus", ""],
|
||||
|
@ -394,6 +400,7 @@ add_task(async function () {
|
|||
|
||||
checkHeaderLabels(["continent", "colour", "sin", "wonder", "dwarf"]);
|
||||
checkHeaderVisibility([true, 150, false, true, false]);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["asia", "blue", "", "temple of artemis", ""],
|
||||
["australia", "green", "", "mausoleum of halicarnassus", ""],
|
||||
|
@ -424,6 +431,7 @@ add_task(async function () {
|
|||
|
||||
checkHeaderLabels(["colour", "continent", "sin", "wonder", "dwarf"]);
|
||||
checkHeaderVisibility([150, true, false, true, false]);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "asia", "", "temple of artemis", ""],
|
||||
["green", "australia", "", "mausoleum of halicarnassus", ""],
|
||||
|
@ -442,6 +450,7 @@ add_task(async function () {
|
|||
|
||||
checkHeaderLabels(["colour", "sin", "wonder", "continent", "dwarf"]);
|
||||
checkHeaderVisibility([150, false, true, true, false]);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "", "temple of artemis", "asia", ""],
|
||||
["green", "", "mausoleum of halicarnassus", "australia", ""],
|
||||
|
@ -463,6 +472,7 @@ add_task(async function () {
|
|||
);
|
||||
checkHeaderLabels(["colour", "sin", "wonder", "continent", "dwarf"]);
|
||||
checkHeaderVisibility([150, false, true, false, false]);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "", "temple of artemis", "", ""],
|
||||
["green", "", "mausoleum of halicarnassus", "", ""],
|
||||
|
@ -484,6 +494,7 @@ add_task(async function () {
|
|||
); // Have the continents drifted?
|
||||
checkHeaderLabels(["colour", "sin", "wonder", "continent", "dwarf"]);
|
||||
checkHeaderVisibility([150, false, true, true, false]);
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "", "temple of artemis", "asia", ""],
|
||||
["green", "", "mausoleum of halicarnassus", "australia", ""],
|
||||
|
@ -609,6 +620,7 @@ add_task(async function () {
|
|||
checkHeaderVisibility([150, true, false, false, false]);
|
||||
checkPersistedValue("sortColumn", "colour");
|
||||
checkPersistedValue("sortDirection", "ascending");
|
||||
await new Promise(resolve => win.requestAnimationFrame(resolve));
|
||||
checkTableRows([
|
||||
["blue", "asia", "", "", ""],
|
||||
["green", "australia", "", "", ""],
|
||||
|
@ -640,6 +652,9 @@ add_task(async function () {
|
|||
Services.xulStore.setValue(url, "autoTree", "sortDirection", "descending");
|
||||
tab.browser.reload();
|
||||
await BrowserTestUtils.browserLoaded(tab.browser);
|
||||
await new Promise(resolve =>
|
||||
tab.browser.contentWindow.requestAnimationFrame(resolve)
|
||||
);
|
||||
|
||||
win = tab.browser.contentWindow;
|
||||
doc = tab.browser.contentDocument;
|
||||
|
|
|
@ -816,6 +816,8 @@ async function subtestRowCountChange() {
|
|||
expectedCount * ROW_HEIGHT,
|
||||
"space for all rows is allocated"
|
||||
);
|
||||
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
async function removeValues(index, count, expectedRemoved) {
|
||||
|
@ -841,12 +843,15 @@ async function subtestRowCountChange() {
|
|||
expectedCount * ROW_HEIGHT,
|
||||
"space for all rows is allocated"
|
||||
);
|
||||
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
async function scrollListToPosition(topOfScroll) {
|
||||
await doListActionAndWaitForRowBuffer(() => {
|
||||
list.scrollTo(0, topOfScroll);
|
||||
});
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
Assert.equal(
|
||||
|
@ -1251,6 +1256,8 @@ async function subtestExpandCollapse() {
|
|||
},
|
||||
};
|
||||
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
Assert.equal(
|
||||
list.querySelectorAll("collapsed").length,
|
||||
0,
|
||||
|
@ -1308,12 +1315,13 @@ async function subtestExpandCollapse() {
|
|||
|
||||
// Click the twisties of rows without children.
|
||||
|
||||
function performChange(id, expectedChange, changeCallback) {
|
||||
async function performChange(id, expectedChange, changeCallback) {
|
||||
listener.reset();
|
||||
let row = doc.getElementById(id);
|
||||
const before = row.classList.contains("collapsed");
|
||||
|
||||
changeCallback(row);
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
row = doc.getElementById(id);
|
||||
if (expectedChange == "collapsed") {
|
||||
|
@ -1346,9 +1354,9 @@ async function subtestExpandCollapse() {
|
|||
}
|
||||
}
|
||||
|
||||
function clickTwisty(id, expectedChange) {
|
||||
async function clickTwisty(id, expectedChange) {
|
||||
info(`clicking the twisty on ${id}`);
|
||||
performChange(id, expectedChange, row =>
|
||||
await performChange(id, expectedChange, row =>
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
row.querySelector(".twisty"),
|
||||
{},
|
||||
|
@ -1357,9 +1365,9 @@ async function subtestExpandCollapse() {
|
|||
);
|
||||
}
|
||||
|
||||
function clickThread(id, expectedChange) {
|
||||
async function clickThread(id, expectedChange) {
|
||||
info(`clicking the thread on ${id}`);
|
||||
performChange(id, expectedChange, row => {
|
||||
await performChange(id, expectedChange, row => {
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
row.querySelector(".tree-button-thread"),
|
||||
{},
|
||||
|
@ -1369,7 +1377,7 @@ async function subtestExpandCollapse() {
|
|||
}
|
||||
|
||||
for (const id of idsWithoutChildren) {
|
||||
clickTwisty(id, null);
|
||||
await clickTwisty(id, null);
|
||||
Assert.equal(list.querySelector(".selected").id, id);
|
||||
}
|
||||
|
||||
|
@ -1403,57 +1411,57 @@ async function subtestExpandCollapse() {
|
|||
|
||||
// Collapse row 2.
|
||||
|
||||
clickTwisty("row-2", "collapsed");
|
||||
await clickTwisty("row-2", "collapsed");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2");
|
||||
checkSelectedAndCurrent(5, "row-3-1-2");
|
||||
|
||||
// Collapse row 3.
|
||||
|
||||
clickTwisty("row-3", "collapsed");
|
||||
await clickTwisty("row-3", "collapsed");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(2, "row-3");
|
||||
|
||||
// Expand row 2.
|
||||
|
||||
clickTwisty("row-2", "expanded");
|
||||
await clickTwisty("row-2", "expanded");
|
||||
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Expand row 3.
|
||||
|
||||
clickTwisty("row-3", "expanded");
|
||||
await clickTwisty("row-3", "expanded");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Collapse row 3-1.
|
||||
|
||||
clickTwisty("row-3-1", "collapsed");
|
||||
await clickTwisty("row-3-1", "collapsed");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Collapse row 3.
|
||||
|
||||
clickTwisty("row-3", "collapsed");
|
||||
await clickTwisty("row-3", "collapsed");
|
||||
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Expand row 3.
|
||||
|
||||
clickTwisty("row-3", "expanded");
|
||||
await clickTwisty("row-3", "expanded");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Expand row 3-1.
|
||||
|
||||
clickTwisty("row-3-1", "expanded");
|
||||
await clickTwisty("row-3-1", "expanded");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Test key presses.
|
||||
|
||||
function pressKey(id, key, expectedChange) {
|
||||
async function pressKey(id, key, expectedChange) {
|
||||
info(`pressing ${key}`);
|
||||
performChange(id, expectedChange, () => {
|
||||
await performChange(id, expectedChange, () => {
|
||||
EventUtils.synthesizeKey(key, {}, content);
|
||||
});
|
||||
}
|
||||
|
@ -1461,102 +1469,102 @@ async function subtestExpandCollapse() {
|
|||
// Row 0 has no children or parent, nothing should happen.
|
||||
|
||||
list.selectedIndex = 0;
|
||||
pressKey("row-1", "VK_LEFT");
|
||||
await pressKey("row-1", "VK_LEFT");
|
||||
checkSelectedAndCurrent(0, "row-1");
|
||||
pressKey("row-1", "VK_RIGHT");
|
||||
await pressKey("row-1", "VK_RIGHT");
|
||||
checkSelectedAndCurrent(0, "row-1");
|
||||
|
||||
// Collapse row 2.
|
||||
|
||||
list.selectedIndex = 1;
|
||||
pressKey("row-2", "VK_LEFT", "collapsed");
|
||||
await pressKey("row-2", "VK_LEFT", "collapsed");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2");
|
||||
checkSelectedAndCurrent(1, "row-2");
|
||||
|
||||
pressKey("row-2", "VK_LEFT");
|
||||
await pressKey("row-2", "VK_LEFT");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2");
|
||||
checkSelectedAndCurrent(1, "row-2");
|
||||
|
||||
// Collapse row 3.
|
||||
|
||||
list.selectedIndex = 2;
|
||||
pressKey("row-3", "VK_LEFT", "collapsed");
|
||||
await pressKey("row-3", "VK_LEFT", "collapsed");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(2, "row-3");
|
||||
|
||||
pressKey("row-3", "VK_LEFT");
|
||||
await pressKey("row-3", "VK_LEFT");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(2, "row-3");
|
||||
|
||||
// Expand row 2.
|
||||
|
||||
list.selectedIndex = 1;
|
||||
pressKey("row-2", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-2", "VK_RIGHT", "expanded");
|
||||
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(1, "row-2");
|
||||
|
||||
// Expand row 3.
|
||||
|
||||
list.selectedIndex = 4;
|
||||
pressKey("row-3", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-3", "VK_RIGHT", "expanded");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Go down the tree to row 3-1-1.
|
||||
|
||||
pressKey("row-3", "VK_RIGHT");
|
||||
await pressKey("row-3", "VK_RIGHT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
pressKey("row-3", "VK_RIGHT");
|
||||
await pressKey("row-3", "VK_RIGHT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
pressKey("row-3-1-1", "VK_RIGHT");
|
||||
await pressKey("row-3-1-1", "VK_RIGHT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
// Collapse row 3-1.
|
||||
|
||||
pressKey("row-3-1-1", "VK_LEFT");
|
||||
await pressKey("row-3-1-1", "VK_LEFT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
pressKey("row-3-1", "VK_LEFT", "collapsed");
|
||||
await pressKey("row-3-1", "VK_LEFT", "collapsed");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
// Collapse row 3.
|
||||
|
||||
pressKey("row-3-1", "VK_LEFT");
|
||||
await pressKey("row-3-1", "VK_LEFT");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
pressKey("row-3", "VK_LEFT", "collapsed");
|
||||
await pressKey("row-3", "VK_LEFT", "collapsed");
|
||||
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Expand row 3.
|
||||
|
||||
pressKey("row-3", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-3", "VK_RIGHT", "expanded");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
pressKey("row-3", "VK_RIGHT");
|
||||
await pressKey("row-3", "VK_RIGHT");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
// Expand row 3-1.
|
||||
|
||||
pressKey("row-3-1", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-3-1", "VK_RIGHT", "expanded");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
pressKey("row-3-1", "VK_RIGHT");
|
||||
await pressKey("row-3-1", "VK_RIGHT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
pressKey("row-3-1-1", "VK_RIGHT");
|
||||
await pressKey("row-3-1-1", "VK_RIGHT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
|
@ -1568,102 +1576,102 @@ async function subtestExpandCollapse() {
|
|||
// Row 0 has no children or parent, nothing should happen.
|
||||
|
||||
list.selectedIndex = 0;
|
||||
pressKey("row-1", "VK_RIGHT");
|
||||
await pressKey("row-1", "VK_RIGHT");
|
||||
checkSelectedAndCurrent(0, "row-1");
|
||||
pressKey("row-1", "VK_LEFT");
|
||||
await pressKey("row-1", "VK_LEFT");
|
||||
checkSelectedAndCurrent(0, "row-1");
|
||||
|
||||
// Collapse row 2.
|
||||
|
||||
list.selectedIndex = 1;
|
||||
pressKey("row-2", "VK_RIGHT", "collapsed");
|
||||
await pressKey("row-2", "VK_RIGHT", "collapsed");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2");
|
||||
checkSelectedAndCurrent(1, "row-2");
|
||||
|
||||
pressKey("row-2", "VK_RIGHT");
|
||||
await pressKey("row-2", "VK_RIGHT");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2");
|
||||
checkSelectedAndCurrent(1, "row-2");
|
||||
|
||||
// Collapse row 3.
|
||||
|
||||
list.selectedIndex = 2;
|
||||
pressKey("row-3", "VK_RIGHT", "collapsed");
|
||||
await pressKey("row-3", "VK_RIGHT", "collapsed");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(2, "row-3");
|
||||
|
||||
pressKey("row-3", "VK_RIGHT");
|
||||
await pressKey("row-3", "VK_RIGHT");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2", "row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(2, "row-3");
|
||||
|
||||
// Expand row 2.
|
||||
|
||||
list.selectedIndex = 1;
|
||||
pressKey("row-2", "VK_LEFT", "expanded");
|
||||
await pressKey("row-2", "VK_LEFT", "expanded");
|
||||
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(1, "row-2");
|
||||
|
||||
// Expand row 3.
|
||||
|
||||
list.selectedIndex = 4;
|
||||
pressKey("row-3", "VK_LEFT", "expanded");
|
||||
await pressKey("row-3", "VK_LEFT", "expanded");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Go down the tree to row 3-1-1.
|
||||
|
||||
pressKey("row-3", "VK_LEFT");
|
||||
await pressKey("row-3", "VK_LEFT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
pressKey("row-3", "VK_LEFT");
|
||||
await pressKey("row-3", "VK_LEFT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
pressKey("row-3-1-1", "VK_LEFT");
|
||||
await pressKey("row-3-1-1", "VK_LEFT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
// Collapse row 3-1.
|
||||
|
||||
pressKey("row-3-1-1", "VK_RIGHT");
|
||||
await pressKey("row-3-1-1", "VK_RIGHT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
pressKey("row-3-1", "VK_RIGHT", "collapsed");
|
||||
await pressKey("row-3-1", "VK_RIGHT", "collapsed");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
// Collapse row 3.
|
||||
|
||||
pressKey("row-3-1", "VK_RIGHT");
|
||||
await pressKey("row-3-1", "VK_RIGHT");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
pressKey("row-3", "VK_RIGHT", "collapsed");
|
||||
await pressKey("row-3", "VK_RIGHT", "collapsed");
|
||||
checkRowsAreHidden("row-3-1", "row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
// Expand row 3.
|
||||
|
||||
pressKey("row-3", "VK_LEFT", "expanded");
|
||||
await pressKey("row-3", "VK_LEFT", "expanded");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(4, "row-3");
|
||||
|
||||
pressKey("row-3", "VK_LEFT");
|
||||
await pressKey("row-3", "VK_LEFT");
|
||||
checkRowsAreHidden("row-3-1-1", "row-3-1-2");
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
// Expand row 3-1.
|
||||
|
||||
pressKey("row-3-1", "VK_LEFT", "expanded");
|
||||
await pressKey("row-3-1", "VK_LEFT", "expanded");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(5, "row-3-1");
|
||||
|
||||
pressKey("row-3-1", "VK_LEFT");
|
||||
await pressKey("row-3-1", "VK_LEFT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
pressKey("row-3-1-1", "VK_LEFT");
|
||||
await pressKey("row-3-1-1", "VK_LEFT");
|
||||
checkRowsAreHidden();
|
||||
checkSelectedAndCurrent(6, "row-3-1-1");
|
||||
|
||||
|
@ -1674,24 +1682,29 @@ async function subtestExpandCollapse() {
|
|||
listener.reset();
|
||||
|
||||
list.collapseRowAtIndex(6); // No children, no effect.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
|
||||
Assert.ok(!listener.collapsedIndex, "'collapsed' event did not fire");
|
||||
|
||||
list.expandRowAtIndex(6); // No children, no effect.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
|
||||
Assert.ok(!listener.expandedIndex, "'expanded' event did not fire");
|
||||
|
||||
list.collapseRowAtIndex(1); // Item with children that aren't selected.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
|
||||
Assert.equal(listener.collapsedIndex, 1, "row-2 fired 'collapsed' event");
|
||||
listener.reset();
|
||||
|
||||
list.expandRowAtIndex(1); // Item with children that aren't selected.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
|
||||
Assert.equal(listener.expandedIndex, 1, "row-2 fired 'expanded' event");
|
||||
listener.reset();
|
||||
|
||||
list.collapseRowAtIndex(5); // Item with children that are selected.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(selectHandler.seenEvent, "'select' event fired");
|
||||
Assert.equal(
|
||||
selectHandler.selectedAtEvent,
|
||||
|
@ -1705,6 +1718,7 @@ async function subtestExpandCollapse() {
|
|||
listener.reset();
|
||||
|
||||
list.expandRowAtIndex(5); // Selected item with children.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
|
||||
Assert.equal(listener.expandedIndex, 5, "row-3-1 fired 'expanded' event");
|
||||
checkRowsAreHidden();
|
||||
|
@ -1715,6 +1729,7 @@ async function subtestExpandCollapse() {
|
|||
selectHandler.reset();
|
||||
|
||||
list.collapseRowAtIndex(4); // Item with grandchildren that are selected.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(selectHandler.seenEvent, "'select' event fired");
|
||||
Assert.equal(
|
||||
selectHandler.selectedAtEvent,
|
||||
|
@ -1728,6 +1743,7 @@ async function subtestExpandCollapse() {
|
|||
listener.reset();
|
||||
|
||||
list.expandRowAtIndex(4); // Selected item with grandchildren.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.ok(!selectHandler.seenEvent, "'select' event did not fire");
|
||||
Assert.equal(listener.expandedIndex, 4, "row-3 fired 'expanded' event");
|
||||
checkRowsAreHidden();
|
||||
|
@ -1736,7 +1752,7 @@ async function subtestExpandCollapse() {
|
|||
|
||||
// Click thread for already expanded thread. Should select all in thread.
|
||||
selectHandler.reset();
|
||||
clickThread("row-3"); // Item with grandchildren.
|
||||
await clickThread("row-3"); // Item with grandchildren.
|
||||
Assert.ok(selectHandler.seenEvent, "'select' event fired");
|
||||
Assert.equal(
|
||||
selectHandler.selectedAtEvent,
|
||||
|
@ -1750,9 +1766,10 @@ async function subtestExpandCollapse() {
|
|||
// Click thread for collapsed thread. Should expand the thread and select all
|
||||
// children.
|
||||
list.collapseRowAtIndex(1); // Item with children that aren't selected.
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
Assert.equal(listener.collapsedIndex, 1, "row-2 fired 'collapsed' event");
|
||||
checkRowsAreHidden("row-2-1", "row-2-2");
|
||||
clickThread("row-2", "expanded");
|
||||
await clickThread("row-2", "expanded");
|
||||
Assert.equal(listener.expandedIndex, 1, "row-2 fired 'expanded' event");
|
||||
checkMultiSelect("row-2", "row-2-1", "row-2-2");
|
||||
checkCurrent(1);
|
||||
|
@ -1768,7 +1785,7 @@ async function subtestExpandCollapse() {
|
|||
EventUtils.synthesizeKey("VK_DOWN", { shiftKey: true }, content);
|
||||
checkMultiSelect("row-2", "row-2-1", "row-2-2");
|
||||
checkCurrent(3);
|
||||
clickTwisty("row-2", "collapsed");
|
||||
await clickTwisty("row-2", "collapsed");
|
||||
checkSelectedAndCurrent(1, "row-2");
|
||||
|
||||
list.removeEventListener("collapsed", listener);
|
||||
|
@ -1822,12 +1839,14 @@ async function subtestScrollWhenExpandCollapse() {
|
|||
|
||||
// Click the twisties of rows without children.
|
||||
|
||||
function performChange(id, expectedChange, changeCallback) {
|
||||
async function performChange(id, expectedChange, changeCallback) {
|
||||
listener.reset();
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
let row = doc.getElementById(id);
|
||||
const before = row.classList.contains("collapsed");
|
||||
|
||||
changeCallback(row);
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
row = doc.getElementById(id);
|
||||
if (expectedChange == "collapsed") {
|
||||
|
@ -1860,9 +1879,9 @@ async function subtestScrollWhenExpandCollapse() {
|
|||
}
|
||||
}
|
||||
|
||||
function clickTwisty(id, expectedChange) {
|
||||
async function clickTwisty(id, expectedChange) {
|
||||
info(`clicking the twisty on ${id}`);
|
||||
performChange(id, expectedChange, row =>
|
||||
await performChange(id, expectedChange, row =>
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
row.querySelector(".twisty"),
|
||||
{},
|
||||
|
@ -1873,9 +1892,9 @@ async function subtestScrollWhenExpandCollapse() {
|
|||
|
||||
// Test key presses.
|
||||
|
||||
function pressKey(id, key, expectedChange) {
|
||||
async function pressKey(id, key, expectedChange) {
|
||||
info(`pressing ${key}`);
|
||||
performChange(id, expectedChange, () => {
|
||||
await performChange(id, expectedChange, () => {
|
||||
EventUtils.synthesizeKey(key, {}, content);
|
||||
});
|
||||
}
|
||||
|
@ -1917,19 +1936,19 @@ async function subtestScrollWhenExpandCollapse() {
|
|||
list.selectedIndex = 0;
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
clickTwisty("row-3", "expanded");
|
||||
await clickTwisty("row-3", "expanded");
|
||||
await checkFirstVisibleRow(0);
|
||||
|
||||
clickTwisty("row-3", "collapsed");
|
||||
await clickTwisty("row-3", "collapsed");
|
||||
await checkFirstVisibleRow(0);
|
||||
|
||||
list.selectedIndex = 2;
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
pressKey("row-3", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-3", "VK_RIGHT", "expanded");
|
||||
await checkFirstVisibleRow(0);
|
||||
|
||||
pressKey("row-3", "VK_LEFT", "collapsed");
|
||||
await pressKey("row-3", "VK_LEFT", "collapsed");
|
||||
await checkFirstVisibleRow(0);
|
||||
|
||||
// Expanding/collapsing row with many children not all fitting the screen
|
||||
|
@ -1938,32 +1957,32 @@ async function subtestScrollWhenExpandCollapse() {
|
|||
list.selectedIndex = 0;
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
clickTwisty("row-4", "expanded");
|
||||
await clickTwisty("row-4", "expanded");
|
||||
await checkFirstVisibleRow(3);
|
||||
|
||||
clickTwisty("row-4", "collapsed");
|
||||
await clickTwisty("row-4", "collapsed");
|
||||
await checkVisibleRow(3);
|
||||
|
||||
list.selectedIndex = 3;
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
pressKey("row-4", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-4", "VK_RIGHT", "expanded");
|
||||
await checkFirstVisibleRow(3);
|
||||
|
||||
pressKey("row-4", "VK_LEFT", "collapsed");
|
||||
await pressKey("row-4", "VK_LEFT", "collapsed");
|
||||
await checkVisibleRow(3);
|
||||
|
||||
// Expanding a row near the bottom with few children should scroll just the
|
||||
// last child into view.
|
||||
|
||||
pressKey("row-4", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-4", "VK_RIGHT", "expanded");
|
||||
list.selectedIndex = 38;
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
clickTwisty("row-9", "expanded");
|
||||
await clickTwisty("row-9", "expanded");
|
||||
await checkLastVisibleRow(43);
|
||||
|
||||
clickTwisty("row-9", "collapsed");
|
||||
await clickTwisty("row-9", "collapsed");
|
||||
await checkVisibleRow(38);
|
||||
|
||||
list.selectedIndex = 0;
|
||||
|
@ -1971,10 +1990,10 @@ async function subtestScrollWhenExpandCollapse() {
|
|||
list.selectedIndex = 38;
|
||||
await new Promise(resolve => content.requestAnimationFrame(resolve));
|
||||
|
||||
pressKey("row-9", "VK_RIGHT", "expanded");
|
||||
await pressKey("row-9", "VK_RIGHT", "expanded");
|
||||
checkLastVisibleRow(43);
|
||||
|
||||
pressKey("row-9", "VK_LEFT", "collapsed");
|
||||
await pressKey("row-9", "VK_LEFT", "collapsed");
|
||||
checkVisibleRow(38);
|
||||
|
||||
list.removeEventListener("collapsed", listener);
|
||||
|
|
|
@ -28,15 +28,12 @@ class TestCardRow extends customElements.get("tree-view-table-row") {
|
|||
this.d3.classList.add("d3");
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.d2.textContent = this.view.getCellText(index, "GeneratedName");
|
||||
this.d3.textContent = this.view.getCellText(index, "PrimaryEmail");
|
||||
this.dataset.value = this.view.values[index];
|
||||
this.d2.textContent = this.view.getCellText(this._index, "GeneratedName");
|
||||
this.d3.textContent = this.view.getCellText(this._index, "PrimaryEmail");
|
||||
this.dataset.value = this.view.values[this._index];
|
||||
}
|
||||
}
|
||||
customElements.define("test-row", TestCardRow, { extends: "tr" });
|
||||
|
@ -54,13 +51,10 @@ class AlternativeCardRow extends customElements.get("tree-view-table-row") {
|
|||
this.cell = this.appendChild(document.createElement("td"));
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.cell.textContent = this.view.getCellText(index, "GeneratedName");
|
||||
this.cell.textContent = this.view.getCellText(this._index, "GeneratedName");
|
||||
}
|
||||
}
|
||||
customElements.define("alternative-row", AlternativeCardRow, {
|
||||
|
|
|
@ -10,12 +10,6 @@
|
|||
<title>Test for the tree-view custom element</title>
|
||||
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
|
||||
<style>
|
||||
:root {
|
||||
--color-gray-20: gray;
|
||||
--selected-item-color: rebeccapurple;
|
||||
--selected-item-text-color: white;
|
||||
}
|
||||
|
||||
#testTree {
|
||||
/* We want a total visible row area of 630px, but we need to account for the
|
||||
* height of the header as well. */
|
||||
|
@ -41,6 +35,11 @@
|
|||
line-height: 1.2;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
tr[is="test-row"].selected {
|
||||
background-color: rebeccapurple;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="tree-element-test-header.mjs"></script>
|
||||
</head>
|
||||
|
|
|
@ -40,16 +40,13 @@ class TestCardRow extends customElements.get("tree-view-table-row") {
|
|||
this.d2.classList.add("d2");
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.id = this.view.getRowProperties(index);
|
||||
this.id = this.view.getRowProperties(this._index);
|
||||
this.classList.remove("level0", "level1", "level2");
|
||||
this.classList.add(`level${this.view.getLevel(index)}`);
|
||||
this.d2.textContent = this.view.getCellText(index, "text");
|
||||
this.classList.add(`level${this.view.getLevel(this._index)}`);
|
||||
this.d2.textContent = this.view.getCellText(this._index, "text");
|
||||
}
|
||||
}
|
||||
customElements.define("test-row", TestCardRow, { extends: "tr" });
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
<title>Test for the tree-view custom element</title>
|
||||
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
|
||||
<style>
|
||||
:root {
|
||||
--color-gray-20: gray;
|
||||
--selected-item-color: rebeccapurple;
|
||||
--selected-item-text-color: white;
|
||||
}
|
||||
|
||||
.tree-view-scrollable-container {
|
||||
height: 630px;
|
||||
scroll-behavior: unset;
|
||||
|
@ -54,6 +48,11 @@
|
|||
tr[is="test-row"].level2 .d2 {
|
||||
padding-inline-start: 2em;
|
||||
}
|
||||
|
||||
tr[is="test-row"].selected {
|
||||
background-color: rebeccapurple;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="tree-element-test-levels.mjs"></script>
|
||||
</head>
|
||||
|
|
|
@ -10,12 +10,6 @@
|
|||
<title>Test for the tree-view custom element</title>
|
||||
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
|
||||
<style>
|
||||
:root {
|
||||
--color-gray-20: gray;
|
||||
--selected-item-color: rebeccapurple;
|
||||
--selected-item-text-color: white;
|
||||
}
|
||||
|
||||
/* We want a total visible row area of 630px. Intentionally avoid leaving
|
||||
* room for a header. */
|
||||
#testTree {
|
||||
|
@ -41,6 +35,11 @@
|
|||
line-height: 1.2;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
tr[is="test-row"].selected {
|
||||
background-color: rebeccapurple;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="tree-element-test-no-header.mjs"></script>
|
||||
</head>
|
||||
|
|
|
@ -40,16 +40,13 @@ class TestCardRow extends customElements.get("tree-view-table-row") {
|
|||
this.d2.classList.add("d2");
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.id = this.view.getRowProperties(index);
|
||||
this.id = this.view.getRowProperties(this._index);
|
||||
this.classList.remove("level0", "level1", "level2");
|
||||
this.classList.add(`level${this.view.getLevel(index)}`);
|
||||
this.d2.textContent = this.view.getCellText(index, "text");
|
||||
this.classList.add(`level${this.view.getLevel(this._index)}`);
|
||||
this.d2.textContent = this.view.getCellText(this._index, "text");
|
||||
}
|
||||
}
|
||||
customElements.define("test-row", TestCardRow, { extends: "tr" });
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
<title>Test for the tree-view custom element</title>
|
||||
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
|
||||
<style>
|
||||
:root {
|
||||
--color-gray-20: gray;
|
||||
--selected-item-color: rebeccapurple;
|
||||
--selected-item-text-color: white;
|
||||
}
|
||||
|
||||
.tree-view-scrollable-container {
|
||||
height: 630px;
|
||||
scroll-behavior: unset;
|
||||
|
@ -54,6 +48,11 @@
|
|||
tr[is="test-row"].level2 .d2 {
|
||||
padding-inline-start: 2em;
|
||||
}
|
||||
|
||||
tr[is="test-row"].selected {
|
||||
background-color: rebeccapurple;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="tree-element-test-scroll.mjs"></script>
|
||||
</head>
|
||||
|
|
|
@ -1218,21 +1218,20 @@ customElements.whenDefined("tree-view-table-row").then(() => {
|
|||
this.appendChild(this.cell);
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the row setter to generate the layout.
|
||||
* Generate the layout for the current card.
|
||||
*
|
||||
* @note This element could be recycled, make sure you set or clear all
|
||||
* properties.
|
||||
*/
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
const card = this.view.getCardFromRow(index);
|
||||
this.name.textContent = this.view.getCellText(index, "GeneratedName");
|
||||
const card = this.view.getCardFromRow(this._index);
|
||||
this.name.textContent = this.view.getCellText(
|
||||
this._index,
|
||||
"GeneratedName"
|
||||
);
|
||||
|
||||
// Add the address book name for All Address Books if in the sort Context
|
||||
// Address Book is checked. This is done for the list view only.
|
||||
|
@ -1249,7 +1248,10 @@ customElements.whenDefined("tree-view-table-row").then(() => {
|
|||
addressBookName.classList.add("address-book-name");
|
||||
this.firstLine.appendChild(addressBookName);
|
||||
}
|
||||
addressBookName.textContent = this.view.getCellText(index, "addrbook");
|
||||
addressBookName.textContent = this.view.getCellText(
|
||||
this._index,
|
||||
"addrbook"
|
||||
);
|
||||
} else {
|
||||
this.querySelector(".address-book-name")?.remove();
|
||||
}
|
||||
|
@ -1310,26 +1312,22 @@ customElements.whenDefined("tree-view-table-row").then(() => {
|
|||
}
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the row setter to generate the layout.
|
||||
* Generate the layout for the current card.
|
||||
*
|
||||
* @note This element could be recycled, make sure you set or clear all
|
||||
* properties.
|
||||
*/
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
_fillRow() {
|
||||
super._fillRow();
|
||||
|
||||
const card = this.view.getCardFromRow(index);
|
||||
const card = this.view.getCardFromRow(this._index);
|
||||
this.classList.toggle("MailList", card.isMailList);
|
||||
|
||||
for (const column of cardsPane.COLUMNS) {
|
||||
const cell = this.querySelector(`.${column.id.toLowerCase()}-column`);
|
||||
if (!column.hidden) {
|
||||
cell.textContent = this.view.getCellText(index, column.id);
|
||||
cell.textContent = this.view.getCellText(this._index, column.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1154,6 +1154,7 @@ add_task(async function test_list_table_layout() {
|
|||
await TestUtils.waitForCondition(
|
||||
() => !cardsHeader.querySelector(`[id="addrbook"]`).hidden
|
||||
);
|
||||
await new Promise(resolve => abWindow.requestAnimationFrame(resolve));
|
||||
|
||||
// Check for the contact that the column is shown.
|
||||
Assert.ok(
|
||||
|
@ -1217,6 +1218,7 @@ add_task(async function test_list_all_address_book() {
|
|||
await TestUtils.waitForCondition(
|
||||
() => !cardsHeader.querySelector(`[id="addrbook"]`).hidden
|
||||
);
|
||||
await new Promise(resolve => abWindow.requestAnimationFrame(resolve));
|
||||
|
||||
Assert.ok(
|
||||
cardsList
|
||||
|
|
|
@ -73,6 +73,7 @@ async function waitForCardsListReady(list) {
|
|||
const eventName = "_treerowbufferfillAbListReady";
|
||||
list._rowBufferReadyEvent = new CustomEvent(eventName);
|
||||
await BrowserTestUtils.waitForEvent(list, eventName);
|
||||
await new Promise(resolve => list.ownerGlobal.requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
async function openAddressBookWindow() {
|
||||
|
|
|
@ -710,25 +710,24 @@ add_task(async function test_custom_column_invalidation() {
|
|||
assert_visible_columns(INBOX_DEFAULTS);
|
||||
const about3Pane = document.getElementById("tabmail").currentAbout3Pane;
|
||||
|
||||
let factor = 1;
|
||||
ThreadPaneColumns.addCustomColumn("testCol1", {
|
||||
name: "Test1",
|
||||
hidden: true,
|
||||
sortCallback(header) {
|
||||
return header.subject.length * factor;
|
||||
return header.subject.length;
|
||||
},
|
||||
textCallback(header) {
|
||||
return header.subject.length * factor;
|
||||
return header.subject.length;
|
||||
},
|
||||
});
|
||||
ThreadPaneColumns.addCustomColumn("testCol2", {
|
||||
name: "Test2",
|
||||
hidden: true,
|
||||
sortCallback(header) {
|
||||
return header.subject.length * factor;
|
||||
return header.subject.length * 2;
|
||||
},
|
||||
textCallback(header) {
|
||||
return header.subject.length * factor;
|
||||
return header.subject.length * 2;
|
||||
},
|
||||
});
|
||||
await new Promise(setTimeout);
|
||||
|
@ -749,34 +748,10 @@ add_task(async function test_custom_column_invalidation() {
|
|||
10
|
||||
);
|
||||
Assert.greater(value1, 0, "Content of custom cell #1 should be non-zero");
|
||||
Assert.greater(value2, 0, "Content of custom cell #2 should be non-zero");
|
||||
Assert.equal(
|
||||
value1,
|
||||
value2,
|
||||
"Content of both custom cells should be identical"
|
||||
);
|
||||
|
||||
factor = 2;
|
||||
ThreadPaneColumns.refreshCustomColumn("testCol1");
|
||||
await new Promise(setTimeout);
|
||||
|
||||
const refreshedValue1 = parseInt(
|
||||
row.querySelector(".testcol1-column").textContent,
|
||||
10
|
||||
);
|
||||
const refreshedValue2 = parseInt(
|
||||
row.querySelector(".testcol2-column").textContent,
|
||||
10
|
||||
);
|
||||
Assert.equal(
|
||||
refreshedValue1,
|
||||
value1 * 2,
|
||||
"Content of custom cell #1 should have doubled"
|
||||
);
|
||||
Assert.equal(
|
||||
refreshedValue2,
|
||||
value2,
|
||||
"Content of custom cell #2 should have not changed"
|
||||
2 * value1,
|
||||
"Content of custom cell #2 should be twice cell #1"
|
||||
);
|
||||
|
||||
ThreadPaneColumns.removeCustomColumn("testCol1");
|
||||
|
|
|
@ -30,8 +30,6 @@ var {
|
|||
"resource://testing-common/mail/FolderDisplayHelpers.sys.mjs"
|
||||
);
|
||||
|
||||
const onHdrDeletedTimeout = 500;
|
||||
|
||||
var singleFolder, folderA, folderB, multiFolder;
|
||||
var tab1, tab2;
|
||||
var about3Pane;
|
||||
|
@ -58,16 +56,15 @@ add_setup(async function () {
|
|||
|
||||
const assertReplyCount = async replyCount => {
|
||||
await switch_tab();
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve =>
|
||||
about3Pane.setTimeout(resolve, onHdrDeletedTimeout)
|
||||
);
|
||||
Assert.equal(
|
||||
about3Pane.threadTree.getElementsByClassName("thread-replies")[0]
|
||||
.textContent,
|
||||
`${replyCount} replies`,
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
const replies = about3Pane.threadTree.querySelector(".thread-replies");
|
||||
Assert.deepEqual(
|
||||
about3Pane.document.l10n.getAttributes(replies),
|
||||
{ id: "threadpane-replies", args: { count: replyCount } },
|
||||
"Thread header in background tab should show the correct message count."
|
||||
);
|
||||
|
||||
await switch_tab();
|
||||
};
|
||||
|
||||
|
@ -141,16 +138,21 @@ add_task(async function delete_from_expanded_xfthread() {
|
|||
|
||||
const assertMessagesCount = async (unreadCount, messagesCount) => {
|
||||
await switch_tab();
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve =>
|
||||
about3Pane.setTimeout(resolve, onHdrDeletedTimeout)
|
||||
);
|
||||
Assert.equal(
|
||||
about3Pane.threadTree.getElementsByClassName("sort-header-details")[0]
|
||||
.textContent,
|
||||
`${unreadCount} unread of ${messagesCount} messages`,
|
||||
await new Promise(resolve => about3Pane.requestAnimationFrame(resolve));
|
||||
|
||||
const count = about3Pane.threadTree.querySelector(".sort-header-details");
|
||||
Assert.deepEqual(
|
||||
about3Pane.document.l10n.getAttributes(count),
|
||||
{
|
||||
id: "threadpane-sort-header-unread-count",
|
||||
args: {
|
||||
unread: unreadCount,
|
||||
total: messagesCount,
|
||||
},
|
||||
},
|
||||
"Group header in background tab should show the correct message count."
|
||||
);
|
||||
|
||||
await switch_tab();
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче