collection search

This commit is contained in:
Bogdan Abaev 2023-11-01 14:50:50 -04:00 committed by Dan Stillman
parent 7da00957ef
commit f8a6b82c63
6 changed files with 742 additions and 89 deletions

View file

@ -1374,4 +1374,200 @@ describe("Zotero.CollectionTree", function() {
assert.lengthOf(win.document.querySelectorAll('#zotero-collections-tree .row.unread'), 2);
});
});
describe("#setFilter()", function () {
var collection1, collection2, collection3, collection4, collection5, collection6, collection7, collection8;
var search1, search2, feed1, feed2;
var allRows = [];
let keyboardClick = (key) => {
return new KeyboardEvent('keydown', {
key: key,
code: key,
bubbles: true,
});
};
before(async function () {
// Delete all previously added collections, feeds, searches
for (let col of Zotero.Collections.getByLibrary(userLibraryID)) {
await col.eraseTx();
}
await clearFeeds();
for (let s of Zotero.Searches.getByLibrary(userLibraryID)) {
await s.eraseTx();
}
// Display the collection search bar
win.document.getElementById("zotero-tb-collections-search").click();
// Do not hide the search panel on blur
win.document.getElementById("zotero-collections-search").removeEventListener('blur', zp.hideCollectionSearch);
feed1 = await createFeed({ name: "feed_1 " });
feed2 = await createFeed({ name: "feed_2" });
collection1 = await createDataObject('collection', { name: "collection_level_one", libraryID: userLibraryID });
collection2 = await createDataObject('collection', { name: "collection_level_two_1", parentID: collection1.id, libraryID: userLibraryID });
collection3 = await createDataObject('collection', { name: "collection_level_two_2", parentID: collection1.id, libraryID: userLibraryID });
collection4 = await createDataObject('collection', { name: "collection_level_three_1", parentID: collection2.id, libraryID: userLibraryID });
collection5 = await createDataObject('collection', { name: "collection_level_three_11", parentID: collection2.id, libraryID: userLibraryID });
collection6 = await createDataObject('collection', { name: "collection_level_one_1", libraryID: userLibraryID });
collection7 = await createDataObject('collection', { name: "collection_level_two_21", parentID: collection6.id, libraryID: userLibraryID });
collection8 = await createDataObject('collection', { name: "collection_level_two_22", parentID: collection6.id, libraryID: userLibraryID });
search1 = await createDataObject('search', { name: "search_1", libraryID: userLibraryID });
search2 = await createDataObject('search', { name: "search_2", libraryID: userLibraryID });
allRows = [feed1, feed2, collection1, collection2, collection3, collection4, collection5, collection6, collection7, collection8, search1, search2];
});
beforeEach(async function () {
// Empty filter and let it settle
await cv.setFilter("");
});
after(async function () {
await cv.setFilter("");
});
for (let type of ['collection', 'search', 'feed']) {
// eslint-disable-next-line no-loop-func
it(`should show only ${type} matching the filter`, async function () {
await cv.setFilter(type);
let displayedRowNames = cv._rows.filter(row => row.type == type).map(row => row.getName());
let expectedRowNames = allRows.filter(row => row.name.includes(type)).map(row => row.name);
assert.sameMembers(displayedRowNames, expectedRowNames);
});
}
it('should show non-passing entries whose children pass the filter', async function () {
await cv.setFilter("three");
let displayedRowNames = cv._rows.filter(row => row.type == "collection").map(row => row.ref.name);
let expectedNames = [
"collection_level_one",
"collection_level_two_1",
"collection_level_three_1",
"collection_level_three_11"
];
assert.sameMembers(displayedRowNames, expectedNames);
});
it('should not move focus from selected collection during filtering', async function () {
await cv.selectByID("C" + collection5.id);
await cv.setFilter("three");
let focusedRow = cv.getRow(cv.selection.focused);
assert.equal(focusedRow.id, "C" + collection5.id);
await cv.setFilter("two");
focusedRow = cv.getRow(cv.selection.focused);
assert.equal(focusedRow.id, "C" + collection5.id);
});
it('should only open relevant collections', async function () {
// Collapse top level collections 1 and 6
for (let c of [collection1, collection6]) {
let index = cv.getRowIndexByID("C" + c.id);
let row = cv.getRow(index);
if (row.isOpen) {
await cv.toggleOpenState(index);
}
}
// Set filter and remove it
await cv.setFilter(collection5.name);
await cv.setFilter("");
// Collection 1 and 2 had a matching child, so they are opened
let colOneRow = cv.getRow(cv.getRowIndexByID("C" + collection1.id));
assert.isTrue(colOneRow.isOpen);
let colTwoRow = cv.getRow(cv.getRowIndexByID("C" + collection2.id));
assert.isTrue(colTwoRow.isOpen);
// Collection 6 had no matches, it remains closed
let colSixRow = cv.getRow(cv.getRowIndexByID("C" + collection6.id));
assert.isFalse(colSixRow.isOpen);
});
for (let type of ['collection', 'search']) {
// eslint-disable-next-line no-loop-func
it(`should only hide ${type} if it's renamed to not match the filter`, async function () {
await cv.setFilter(type);
let objectToSelect = type == 'collection' ? collection5 : search2;
objectToSelect.name += "_updated";
await objectToSelect.saveTx();
let displayedRowNames = cv._rows.map(row => row.getName());
assert.include(displayedRowNames, objectToSelect.name);
objectToSelect.name = "not_matching_filter";
await objectToSelect.saveTx();
displayedRowNames = cv._rows.map(row => row.getName());
assert.notInclude(displayedRowNames, objectToSelect.name);
});
}
for (let type of ['collection', 'search']) {
// eslint-disable-next-line no-loop-func
it(`should only add ${type} if its name matches the filter`, async function () {
await cv.setFilter(type);
let newCollection = await createDataObject(type, { name: `new_${type}`, libraryID: userLibraryID });
let displayedRowNames = cv._rows.map(row => row.ref.name);
assert.include(displayedRowNames, newCollection.name);
newCollection = await createDataObject(type, { name: `not_passing_${type.substring(1)}`, libraryID: userLibraryID });
displayedRowNames = cv._rows.map(row => row.ref.name);
assert.notInclude(displayedRowNames, newCollection.name);
});
}
it(`should focus selected collection on Enter if it matches filter`, async function () {
await cv.selectByID(`C${collection3.id}`);
win.document.getElementById("zotero-collections-search").value = "_2";
await cv.setFilter("_2");
win.document.getElementById("zotero-collections-search").dispatchEvent(keyboardClick("Enter"));
assert.equal(cv.getSelectedCollection(true), collection3.id);
assert.equal(win.document.activeElement.id, 'collection-tree');
});
it(`should focus first matching collection on Enter if selected collection does not match filter`, async function () {
await cv.selectByID(`C${collection2.id}`);
win.document.getElementById("zotero-collections-search").focus();
win.document.getElementById("zotero-collections-search").value = "_2";
await cv.setFilter("_2");
win.document.getElementById("zotero-collections-search").dispatchEvent(keyboardClick("Enter"));
// Wait for the selection to go through
await Zotero.Promise.delay(100);
assert.equal(cv.getSelectedCollection(true), collection3.id);
assert.equal(win.document.activeElement.id, 'collection-tree');
});
it(`should not move focus from collection filter on Enter if no rows pass the filter`, async function () {
await cv.selectByID(`C${collection3.id}`);
win.document.getElementById("zotero-collections-search").focus();
win.document.getElementById("zotero-collections-search").value = "Not matching anything";
await cv.setFilter("Not matching anything");
win.document.getElementById("zotero-collections-search").dispatchEvent(keyboardClick("Enter"));
assert.equal(win.document.activeElement.id, 'zotero-collections-search');
});
it(`should skip context rows on arrow up/down`, async function () {
await cv.selectByID(`C${collection2.id}`);
await cv.setFilter("_2");
await cv.focusFirstMatchingRow();
// Skip collection6 that does not match on the way up and down
for (let col of [collection3, collection7, collection8]) {
assert.equal(cv.getSelectedCollection(true), col.id);
await cv.focusNextMatchingRow(cv.selection.focused);
}
await cv.selectByID(`C${collection8.id}`);
for (let col of [collection8, collection7, collection3]) {
assert.equal(cv.getSelectedCollection(true), col.id);
await cv.focusNextMatchingRow(cv.selection.focused, true);
}
});
it(`should clear filter on Escape from collectionTree`, async function () {
await cv.selectByID(`C${collection2.id}`);
let colTree = win.document.getElementById('collection-tree');
await cv.setFilter("_2");
cv.focusFirstMatchingRow();
colTree.dispatchEvent(keyboardClick("Escape"));
assert.equal(cv._filter, "");
assert.equal(cv.getSelectedCollection(true), collection2.id);
});
});
})