Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Tests the bookmarks toolbar overflow.
*/
var gToolbar = document.getElementById("PersonalToolbar");
var gChevron = document.getElementById("PlacesChevron");
const BOOKMARKS_COUNT = 250;
add_setup(async function () {
let wasCollapsed = gToolbar.collapsed;
await PlacesUtils.bookmarks.eraseEverything();
// Add bookmarks.
await PlacesUtils.bookmarks.insertTree({
guid: PlacesUtils.bookmarks.toolbarGuid,
children: Array(BOOKMARKS_COUNT)
.fill("")
.map((_, i) => ({ url: `http://test.places.y${i}/` })),
});
// Toggle the bookmarks toolbar so that we start from a stable situation and
// are not affected by all bookmarks removal.
await toggleToolbar(false);
await toggleToolbar(true);
registerCleanupFunction(async () => {
if (wasCollapsed) {
await toggleToolbar(false);
}
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
});
});
add_task(async function test_overflow() {
// Check that the overflow chevron is visible.
Assert.ok(!gChevron.collapsed, "The overflow chevron should be visible");
let children = getPlacesChildren();
Assert.ok(
children.length < BOOKMARKS_COUNT,
"Not all the nodes should be built by default"
);
let visibleNodes = [];
for (let node of children) {
if (getComputedStyle(node).visibility == "visible") {
visibleNodes.push(node);
}
}
Assert.ok(
visibleNodes.length < children.length,
`The number of visible nodes (${visibleNodes.length}) should be smaller than the number of built nodes (${children.length})`
);
await test_index(
"Node at the last visible index",
visibleNodes.length - 1,
"visible"
);
await test_index(
"Node at the first invisible index",
visibleNodes.length,
"hidden"
);
await test_index("First non-built node", children.length, undefined);
await test_index("Later non-built node", children.length + 1, undefined);
await test_move_index(
"Move node from last visible to first hidden",
visibleNodes.length - 1,
visibleNodes.length,
"visible",
"hidden"
);
await test_move_index(
"Move node from fist visible to last built",
0,
children.length - 1,
"visible",
"hidden"
);
await test_move_index(
"Move node from fist visible to first non built",
0,
children.length,
"visible",
undefined
);
});
add_task(async function test_separator_first() {
await toggleToolbar(false);
// Check that if a separator is the first node, we still calculate overflow
// properly.
let bm = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
index: 0,
});
await toggleToolbar(true, 2);
let children = getPlacesChildren();
Assert.greater(children.length, 2, "Multiple elements are visible");
Assert.equal(
children[1]._placesNode.uri,
"Found the first bookmark"
);
Assert.equal(
getComputedStyle(children[1]).visibility,
"visible",
"The first bookmark is visible"
);
await PlacesUtils.bookmarks.remove(bm);
});
add_task(async function test_newWindow_noOverflow() {
info(
"Check toolbar in a new widow when it was already visible and not overflowed"
);
Assert.ok(!gToolbar.collapsed, "Toolbar is not collapsed in original window");
await PlacesUtils.bookmarks.eraseEverything();
// Add a single bookmark.
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
title: "Example",
});
// Add a favicon for the bookmark.
let favicon =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
"AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
await PlacesTestUtils.addFavicons(
new Map([["http://toolbar.overflow/", favicon]])
);
let win = await BrowserTestUtils.openNewBrowserWindow();
try {
let toolbar = win.document.getElementById("PersonalToolbar");
Assert.ok(!toolbar.collapsed, "The toolbar is not collapsed");
let content = win.document.getElementById("PlacesToolbarItems");
await TestUtils.waitForCondition(() => {
return (
content.children.length == 1 &&
content.children[0].hasAttribute("image")
);
});
let chevron = win.document.getElementById("PlacesChevron");
Assert.ok(chevron.collapsed, "The chevron should be collapsed");
} finally {
await BrowserTestUtils.closeWindow(win);
}
});
async function test_index(desc, index, expected) {
info(desc);
let children = getPlacesChildren();
let originalLen = children.length;
let nodeExisted = children.length > index;
let previousNodeIsVisible =
nodeExisted &&
getComputedStyle(children[index - 1]).visibility == "visible";
let promise = promiseUpdateVisibility(
expected == "visible" || previousNodeIsVisible
);
let bm = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index,
});
Assert.equal(bm.index, index, "Sanity check the bookmark index");
await promise;
children = getPlacesChildren();
if (!expected) {
Assert.ok(
children.length <= index,
"The new node should not have been added"
);
} else {
Assert.equal(
children[index]._placesNode.bookmarkGuid,
bm.guid,
"Found the added bookmark at the expected position"
);
Assert.equal(
getComputedStyle(children[index]).visibility,
expected,
`The bookmark node should be ${expected}`
);
}
Assert.equal(
children.length,
originalLen,
"Number of built nodes should stay the same"
);
info("Remove the node");
promise = promiseUpdateVisibility(expected == "visible");
await PlacesUtils.bookmarks.remove(bm);
await promise;
children = getPlacesChildren();
if (expected && nodeExisted) {
Assert.equal(
children[index]._placesNode.uri,
"Found the previous bookmark at the expected position"
);
Assert.equal(
getComputedStyle(children[index]).visibility,
expected,
`The bookmark node should be ${expected}`
);
}
Assert.equal(
children.length,
originalLen,
"Number of built nodes should stay the same"
);
}
async function test_move_index(desc, fromIndex, toIndex, original, expected) {
info(desc);
let children = getPlacesChildren();
let originalLen = children.length;
let movedGuid = children[fromIndex]._placesNode.bookmarkGuid;
let existingGuid = children[toIndex]
? children[toIndex]._placesNode.bookmarkGuid
: null;
let existingIndex = fromIndex < toIndex ? toIndex - 1 : toIndex + 1;
Assert.equal(
getComputedStyle(children[fromIndex]).visibility,
original,
`The bookmark node should be ${original}`
);
let promise = promiseUpdateVisibility(
original == "visible" || expected == "visible"
);
await PlacesUtils.bookmarks.update({
guid: movedGuid,
index: toIndex,
});
await promise;
children = getPlacesChildren();
if (!expected) {
Assert.ok(
children.length <= toIndex,
"Node in the new position is not expected"
);
Assert.ok(
children[originalLen - 1],
"We should keep number of built nodes consistent"
);
} else {
Assert.equal(
children[toIndex]._placesNode.bookmarkGuid,
movedGuid,
"Found the moved bookmark at the expected position"
);
Assert.equal(
getComputedStyle(children[toIndex]).visibility,
expected,
`The destination bookmark node should be ${expected}`
);
}
Assert.equal(
getComputedStyle(children[fromIndex]).visibility,
original,
`The origin bookmark node should be ${original}`
);
if (existingGuid) {
Assert.equal(
children[existingIndex]._placesNode.bookmarkGuid,
existingGuid,
"Found the pushed away bookmark at the expected position"
);
}
Assert.equal(
children.length,
originalLen,
"Number of built nodes should stay the same"
);
info("Moving back the node");
promise = promiseUpdateVisibility(
original == "visible" || expected == "visible"
);
await PlacesUtils.bookmarks.update({
guid: movedGuid,
index: fromIndex,
});
await promise;
children = getPlacesChildren();
Assert.equal(
children[fromIndex]._placesNode.bookmarkGuid,
movedGuid,
"Found the moved bookmark at the expected position"
);
if (expected) {
Assert.equal(
getComputedStyle(children[toIndex]).visibility,
expected,
`The bookmark node should be ${expected}`
);
}
Assert.equal(
getComputedStyle(children[fromIndex]).visibility,
original,
`The bookmark node should be ${original}`
);
if (existingGuid) {
Assert.equal(
children[toIndex]._placesNode.bookmarkGuid,
existingGuid,
"Found the pushed away bookmark at the expected position"
);
}
Assert.equal(
children.length,
originalLen,
"Number of built nodes should stay the same"
);
}
add_task(async function test_separator_first() {
// Check that if there are only separators, we still show nodes properly.
await toggleToolbar(false);
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
index: 0,
});
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
index: 0,
});
await toggleToolbar(true, 2);
let children = getPlacesChildren();
Assert.equal(children.length, 2, "The expected elements are visible");
Assert.equal(
getComputedStyle(children[0]).visibility,
"visible",
"The first bookmark is visible"
);
Assert.equal(
getComputedStyle(children[1]).visibility,
"visible",
"The second bookmark is visible"
);
await toggleToolbar(false);
await PlacesUtils.bookmarks.eraseEverything();
});
/**
* If the passed-in condition is fulfilled, awaits for the toolbar nodes
* visibility to have been updated.
*
* @param {boolean} [condition] Awaits for visibility only if this condition is true.
* @returns {Promise} resolved when the condition is not fulfilled or the
* visilibily update happened.
*/
function promiseUpdateVisibility(condition = true) {
if (condition) {
return BrowserTestUtils.waitForEvent(
gToolbar,
"BookmarksToolbarVisibilityUpdated"
);
}
return Promise.resolve();
}
/**
* Returns an array of toolbar children that are Places nodes, ignoring things
* like the chevron or other additional buttons.
*
* @returns {Array} An array of Places element nodes.
*/
function getPlacesChildren() {
return Array.prototype.filter.call(
document.getElementById("PlacesToolbarItems").children,
c => c._placesNode?.itemId
);
}
/**
* Toggles the toolbar on or off.
*
* @param {boolean} show Whether to show or hide the toolbar.
* @param {number} [expectedMinChildCount] Optional number of Places nodes that
* should be visible on the toolbar.
*/
async function toggleToolbar(show, expectedMinChildCount = 0) {
let promiseReady = Promise.resolve();
if (show) {
promiseReady = promiseUpdateVisibility();
}
await promiseSetToolbarVisibility(gToolbar, show);
await promiseReady;
if (show) {
if (getPlacesChildren().length < expectedMinChildCount) {
await new Promise(resolve => {
info("Waiting for bookmark elements to appear");
let mut = new MutationObserver(() => {
let children = getPlacesChildren();
info(`${children.length} bookmark elements appeared`);
if (children.length >= expectedMinChildCount) {
resolve();
mut.disconnect();
}
});
mut.observe(document.getElementById("PlacesToolbarItems"), {
childList: true,
});
});
}
}
}