From 8ef4b1b2e73ec1f40347a009e678ac918321a241 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 13:56:46 -0600 Subject: [PATCH 01/17] Replace labs flag with a real setting --- src/components/structures/LoggedInView.tsx | 3 +-- src/settings/Settings.js | 3 ++- src/stores/RoomListStore.js | 2 +- src/stores/room-list/RoomListStore2.ts | 4 ++-- src/stores/room-list/RoomListStoreTempProxy.ts | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 6354bb8739..e65c2bc606 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -667,8 +667,7 @@ class LoggedInView extends React.Component { disabled={this.props.leftDisabled} /> ); - if (SettingsStore.isFeatureEnabled("feature_new_room_list")) { - // TODO: Supply props like collapsed and disabled to LeftPanel2 + if (SettingsStore.getValue("feature_new_room_list")) { leftPanel = ( { return this._matrixClient; } - // TODO: Remove enabled flag with the old RoomListStore: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove enabled flag with the old RoomListStore: https://github.com/vector-im/riot-web/issues/14367 private checkEnabled() { - this.enabled = SettingsStore.isFeatureEnabled("feature_new_room_list"); + this.enabled = SettingsStore.getValue("feature_new_room_list"); if (this.enabled) { console.log("âš¡ new room list store engaged"); } diff --git a/src/stores/room-list/RoomListStoreTempProxy.ts b/src/stores/room-list/RoomListStoreTempProxy.ts index 86aff178ee..9aae159e19 100644 --- a/src/stores/room-list/RoomListStoreTempProxy.ts +++ b/src/stores/room-list/RoomListStoreTempProxy.ts @@ -28,7 +28,7 @@ import { ITagMap } from "./algorithms/models"; */ export class RoomListStoreTempProxy { public static isUsingNewStore(): boolean { - return SettingsStore.isFeatureEnabled("feature_new_room_list"); + return SettingsStore.getValue("feature_new_room_list"); } public static addListener(handler: () => void): RoomListStoreTempToken { From a59a8b76a985cc9e7d03fa9ed6f9655365f3e58c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 14:51:43 -0600 Subject: [PATCH 02/17] Update TODO comments to point to new issue --- res/css/structures/_LeftPanel2.scss | 2 +- res/css/views/rooms/_RoomBreadcrumbs2.scss | 2 +- res/css/views/rooms/_RoomSublist2.scss | 2 +- res/css/views/rooms/_RoomTile2.scss | 2 +- src/components/structures/LeftPanel2.tsx | 4 ++-- src/components/structures/RoomSearch.tsx | 2 +- src/components/views/rooms/RoomBreadcrumbs2.tsx | 4 ++-- src/components/views/rooms/RoomList2.tsx | 4 ++-- src/components/views/rooms/RoomSublist2.tsx | 4 ++-- src/components/views/rooms/RoomTile2.tsx | 4 ++-- .../views/settings/tabs/user/PreferencesUserSettingsTab.js | 4 ++-- src/stores/BreadcrumbsStore.ts | 6 +++--- src/stores/room-list/MessagePreviewStore.ts | 2 +- src/stores/room-list/RoomListStore2.ts | 4 ++-- src/stores/room-list/RoomListStoreTempProxy.ts | 2 +- 15 files changed, 24 insertions(+), 24 deletions(-) diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss index a73658d916..10eb9dd2e9 100644 --- a/res/css/structures/_LeftPanel2.scss +++ b/res/css/structures/_LeftPanel2.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 $tagPanelWidth: 70px; // only applies in this file, used for calculations diff --git a/res/css/views/rooms/_RoomBreadcrumbs2.scss b/res/css/views/rooms/_RoomBreadcrumbs2.scss index 6e5a5fbb16..0c3c41622e 100644 --- a/res/css/views/rooms/_RoomBreadcrumbs2.scss +++ b/res/css/views/rooms/_RoomBreadcrumbs2.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 .mx_RoomBreadcrumbs2 { width: 100%; diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist2.scss index 0e76152f86..9c919023e6 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist2.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 .mx_RoomSublist2 { // The sublist is a column of rows, essentially diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss index 50d376a66f..d844c14443 100644 --- a/res/css/views/rooms/_RoomTile2.scss +++ b/res/css/views/rooms/_RoomTile2.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 // Note: the room tile expects to be in a flexbox column container .mx_RoomTile2 { diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel2.tsx index 7fac6cbff1..769f7daefd 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel2.tsx @@ -34,8 +34,8 @@ import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomLi import {Key} from "../../Keyboard"; import IndicatorScrollbar from "../structures/IndicatorScrollbar"; -// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231 -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 /******************************************************************* * CAUTION * diff --git a/src/components/structures/RoomSearch.tsx b/src/components/structures/RoomSearch.tsx index 15f3bd5b54..d152a2b030 100644 --- a/src/components/structures/RoomSearch.tsx +++ b/src/components/structures/RoomSearch.tsx @@ -25,7 +25,7 @@ import { Key } from "../../Keyboard"; import AccessibleButton from "../views/elements/AccessibleButton"; import { Action } from "../../dispatcher/actions"; -// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367 /******************************************************************* * CAUTION * diff --git a/src/components/views/rooms/RoomBreadcrumbs2.tsx b/src/components/views/rooms/RoomBreadcrumbs2.tsx index 687f4dd73e..fce8c6ee3a 100644 --- a/src/components/views/rooms/RoomBreadcrumbs2.tsx +++ b/src/components/views/rooms/RoomBreadcrumbs2.tsx @@ -28,8 +28,8 @@ import RoomListStore from "../../../stores/room-list/RoomListStore2"; import { DefaultTagID } from "../../../stores/room-list/models"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; -// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231 -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 /******************************************************************* * CAUTION * diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index fb0136fb29..db246b182d 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -42,8 +42,8 @@ import { TagSpecificNotificationState } from "../../../stores/notifications/TagS import { Action } from "../../../dispatcher/actions"; import { ViewRoomDeltaPayload } from "../../../dispatcher/payloads/ViewRoomDeltaPayload"; -// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231 -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 /******************************************************************* * CAUTION * diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index eefd29f0b7..60d4e307a1 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -42,8 +42,8 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import { Key } from "../../../Keyboard"; import StyledCheckbox from "../elements/StyledCheckbox"; -// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231 -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 /******************************************************************* * CAUTION * diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index abb31a6f71..3b15d7b27a 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -51,8 +51,8 @@ import NotificationBadge from "./NotificationBadge"; import { NotificationColor } from "../../../stores/notifications/NotificationColor"; import { Volume } from "../../../RoomNotifsTypes"; -// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14231 -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 +// TODO: Remove banner on launch: https://github.com/vector-im/riot-web/issues/14367 +// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 /******************************************************************* * CAUTION * diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 40b622cf37..abe6b48712 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -32,12 +32,12 @@ export default class PreferencesUserSettingsTab extends React.Component { 'breadcrumbs', ]; - // TODO: Remove temp structures: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove temp structures: https://github.com/vector-im/riot-web/issues/14367 static ROOM_LIST_2_SETTINGS = [ 'breadcrumbs', ]; - // TODO: Remove temp structures: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove temp structures: https://github.com/vector-im/riot-web/issues/14367 static eligibleRoomListSettings = () => { if (RoomListStoreTempProxy.isUsingNewStore()) { return PreferencesUserSettingsTab.ROOM_LIST_2_SETTINGS; diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index c78f15c3b4..70d4e19a32 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -57,7 +57,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { protected async onAction(payload: ActionPayload) { if (!this.matrixClient) return; - // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14367 if (!RoomListStoreTempProxy.isUsingNewStore()) return; if (payload.action === 'setting_updated') { @@ -80,7 +80,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { } protected async onReady() { - // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14367 if (!RoomListStoreTempProxy.isUsingNewStore()) return; await this.updateRooms(); @@ -91,7 +91,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { } protected async onNotReady() { - // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14367 if (!RoomListStoreTempProxy.isUsingNewStore()) return; this.matrixClient.removeListener("Room.myMembership", this.onMyMembership); diff --git a/src/stores/room-list/MessagePreviewStore.ts b/src/stores/room-list/MessagePreviewStore.ts index 01ddde2e17..ea7fa830cd 100644 --- a/src/stores/room-list/MessagePreviewStore.ts +++ b/src/stores/room-list/MessagePreviewStore.ts @@ -192,7 +192,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient { protected async onAction(payload: ActionPayload) { if (!this.matrixClient) return; - // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove when new room list is made the default: https://github.com/vector-im/riot-web/issues/14367 if (!RoomListStoreTempProxy.isUsingNewStore()) return; if (payload.action === 'MatrixActions.Room.timeline' || payload.action === 'MatrixActions.Event.decrypted') { diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 1e5b4f302f..1acff9d3fd 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -89,7 +89,7 @@ export class RoomListStore2 extends AsyncStore { } private onRVSUpdate = () => { - if (!this.enabled) return; // TODO: Remove with https://github.com/vector-im/riot-web/issues/14231 + if (!this.enabled) return; // TODO: Remove with https://github.com/vector-im/riot-web/issues/14367 if (!this.matrixClient) return; // We assume there won't be RVS updates without a client const activeRoomId = RoomViewStore.getRoomId(); @@ -115,7 +115,7 @@ export class RoomListStore2 extends AsyncStore { return; } - // TODO: Remove with https://github.com/vector-im/riot-web/issues/14231 + // TODO: Remove with https://github.com/vector-im/riot-web/issues/14367 this.checkEnabled(); if (!this.enabled) return; diff --git a/src/stores/room-list/RoomListStoreTempProxy.ts b/src/stores/room-list/RoomListStoreTempProxy.ts index 9aae159e19..2a5348ab6e 100644 --- a/src/stores/room-list/RoomListStoreTempProxy.ts +++ b/src/stores/room-list/RoomListStoreTempProxy.ts @@ -24,7 +24,7 @@ import { ITagMap } from "./algorithms/models"; * Temporary RoomListStore proxy. Should be replaced with RoomListStore2 when * it is available to everyone. * - * TODO: Delete this: https://github.com/vector-im/riot-web/issues/14231 + * TODO: Delete this: https://github.com/vector-im/riot-web/issues/14367 */ export class RoomListStoreTempProxy { public static isUsingNewStore(): boolean { From c774b88bdabd0914109277ebc35c5fe5a18eb182 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 15:06:06 -0600 Subject: [PATCH 03/17] Initial pass of fixing tests * Use new components * Use new tagId prop on sublists * Define onResize for the room list so it doesn't crash --- test/components/views/rooms/RoomList-test.js | 30 ++++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index d0694a8437..681f5ccb6b 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -57,11 +57,11 @@ describe('RoomList', () => { parentDiv = document.createElement('div'); document.body.appendChild(parentDiv); - const RoomList = sdk.getComponent('views.rooms.RoomList'); + const RoomList = sdk.getComponent('views.rooms.RoomList2'); const WrappedRoomList = TestUtils.wrapInMatrixClientContext(RoomList); root = ReactDOM.render( - + {}} /> , parentDiv); ReactTestUtils.findRenderedComponentWithType(root, RoomList); @@ -117,8 +117,8 @@ describe('RoomList', () => { }); function expectRoomInSubList(room, subListTest) { - const RoomSubList = sdk.getComponent('structures.RoomSubList'); - const RoomTile = sdk.getComponent('views.rooms.RoomTile'); + const RoomSubList = sdk.getComponent('views.rooms.RoomSublist2'); + const RoomTile = sdk.getComponent('views.rooms.RoomTile2'); const subLists = ReactTestUtils.scryRenderedComponentsWithType(root, RoomSubList); const containingSubList = subLists.find(subListTest); @@ -140,20 +140,20 @@ describe('RoomList', () => { expect(expectedRoomTile.props.room).toBe(room); } - function expectCorrectMove(oldTag, newTag) { - const getTagSubListTest = (tag) => { - if (tag === undefined) return (s) => s.props.label.endsWith('Rooms'); - return (s) => s.props.tagName === tag; + function expectCorrectMove(oldTagId, newTagId) { + const getTagSubListTest = (tagId) => { + return (s) => s.props.tagId === tagId; }; // Default to finding the destination sublist with newTag - const destSubListTest = getTagSubListTest(newTag); - const srcSubListTest = getTagSubListTest(oldTag); + const destSubListTest = getTagSubListTest(newTagId); + const srcSubListTest = getTagSubListTest(oldTagId); // Set up the room that will be moved such that it has the correct state for a room in - // the section for oldTag - if (['m.favourite', 'm.lowpriority'].includes(oldTag)) movingRoom.tags = {[oldTag]: {}}; - if (oldTag === DefaultTagID.DM) { + // the section for oldTagId + if (oldTagId === DefaultTagID.Favourite || oldTagId === DefaultTagID.LowPriority) { + movingRoom.tags = {[oldTagId]: {}}; + } else if (oldTagId === DefaultTagID.DM) { // Mock inverse m.direct DMRoomMap.shared().roomToUser = { [movingRoom.roomId]: '@someotheruser:domain', @@ -167,7 +167,7 @@ describe('RoomList', () => { expectRoomInSubList(movingRoom, srcSubListTest); dis.dispatch({action: 'RoomListActions.tagRoom.pending', request: { - oldTag, newTag, room: movingRoom, + oldTagId, newTagId, room: movingRoom, }}); // Run all setTimeouts for dispatches and room list rate limiting @@ -287,7 +287,7 @@ describe('RoomList', () => { clock.runAll(); // By default, the test will - expectRoomInSubList(otherRoom, (s) => s.props.label.endsWith('Rooms')); + expectRoomInSubList(otherRoom, (s) => s.props.tagId === DefaultTagID.Untagged); }); itDoesCorrectOptimisticUpdatesForDraggedRoomTiles(); From f89fcd1fe9c3df9d44add57f160bab408b3e5ec6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 15:45:59 -0600 Subject: [PATCH 04/17] Fix tests and add general safety We don't need the fake clock anymore, but we do have to wait for async actions to complete before moving forward. This also exposes a number of functions for the store to be puppetted with. --- src/stores/room-list/RoomListStore2.ts | 43 ++++++++++++++------ test/components/views/rooms/RoomList-test.js | 40 ++++++++++-------- 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 1acff9d3fd..63c3abdf09 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -72,6 +72,34 @@ export class RoomListStore2 extends AsyncStore { return this._matrixClient; } + // Intended for test usage + public async resetStore() { + await this.reset(); + this.tagWatcher = new TagWatcher(this); + this.filterConditions = []; + this.initialListsGenerated = false; + this._matrixClient = null; + + this.algorithm.off(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); + this.algorithm = new Algorithm(); + this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); + } + + // Public for test usage. Do not call this. + public async makeReady(client: MatrixClient) { + // TODO: Remove with https://github.com/vector-im/riot-web/issues/14367 + this.checkEnabled(); + if (!this.enabled) return; + + this._matrixClient = client; + + // Update any settings here, as some may have happened before we were logically ready. + console.log("Regenerating room lists: Startup"); + await this.readAndCacheSettingsFromStore(); + await this.regenerateAllLists(); + this.onRVSUpdate(); // fake an RVS update to adjust sticky room, if needed + } + // TODO: Remove enabled flag with the old RoomListStore: https://github.com/vector-im/riot-web/issues/14367 private checkEnabled() { this.enabled = SettingsStore.getValue("feature_new_room_list"); @@ -115,17 +143,7 @@ export class RoomListStore2 extends AsyncStore { return; } - // TODO: Remove with https://github.com/vector-im/riot-web/issues/14367 - this.checkEnabled(); - if (!this.enabled) return; - - this._matrixClient = payload.matrixClient; - - // Update any settings here, as some may have happened before we were logically ready. - console.log("Regenerating room lists: Startup"); - await this.readAndCacheSettingsFromStore(); - await this.regenerateAllLists(); - this.onRVSUpdate(); // fake an RVS update to adjust sticky room, if needed + await this.makeReady(payload.matrixClient); } // TODO: Remove this once the RoomListStore becomes default @@ -372,7 +390,8 @@ export class RoomListStore2 extends AsyncStore { this.emit(LISTS_UPDATE_EVENT, this); }; - private async regenerateAllLists() { + // This is only exposed externally for the tests. Do not call this within the app. + public async regenerateAllLists() { console.warn("Regenerating all room lists"); const sorts: ITagSortingMap = {}; diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 681f5ccb6b..ba53676fc1 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -15,11 +15,17 @@ import GroupStore from '../../../../src/stores/GroupStore.js'; import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk'; import {DefaultTagID} from "../../../../src/stores/room-list/models"; +import RoomListStore, {LISTS_UPDATE_EVENT} from "../../../../src/stores/room-list/RoomListStore2"; function generateRoomId() { return '!' + Math.random().toString().slice(2, 10) + ':domain'; } +function waitForRoomListStoreUpdate() { + return new Promise((resolve) => { + RoomListStore.instance.once(LISTS_UPDATE_EVENT, () => resolve()); + }); +} describe('RoomList', () => { function createRoom(opts) { @@ -34,7 +40,6 @@ describe('RoomList', () => { let client = null; let root = null; const myUserId = '@me:domain'; - let clock = null; const movingRoomId = '!someroomid'; let movingRoom; @@ -43,15 +48,13 @@ describe('RoomList', () => { let myMember; let myOtherMember; - beforeEach(function() { + beforeEach(async function(done) { TestUtils.stubClient(); client = MatrixClientPeg.get(); client.credentials = {userId: myUserId}; //revert this to prototype method as the test-utils monkey-patches this to return a hardcoded value client.getUserId = MatrixClient.prototype.getUserId; - clock = lolex.install(); - DMRoomMap.makeShared(); parentDiv = document.createElement('div'); @@ -102,16 +105,22 @@ describe('RoomList', () => { }); client.getRoom = (roomId) => roomMap[roomId]; + + // Now that everything has been set up, prepare and update the store + await RoomListStore.instance.makeReady(client); + + done(); }); - afterEach((done) => { + afterEach(async (done) => { if (parentDiv) { ReactDOM.unmountComponentAtNode(parentDiv); parentDiv.remove(); parentDiv = null; } - clock.uninstall(); + await RoomListStore.instance.resetLayouts(); + await RoomListStore.instance.resetStore(); done(); }); @@ -127,6 +136,7 @@ describe('RoomList', () => { try { const roomTiles = ReactTestUtils.scryRenderedComponentsWithType(containingSubList, RoomTile); console.info({roomTiles: roomTiles.length}); + console.log("IS SAME?", room === roomTiles[0].props.room, room, roomTiles[0].props.room); expectedRoomTile = roomTiles.find((tile) => tile.props.room === room); } catch (err) { // truncate the error message because it's spammy @@ -162,17 +172,12 @@ describe('RoomList', () => { dis.dispatch({action: 'MatrixActions.sync', prevState: null, state: 'PREPARED', matrixClient: client}); - clock.runAll(); - expectRoomInSubList(movingRoom, srcSubListTest); dis.dispatch({action: 'RoomListActions.tagRoom.pending', request: { oldTagId, newTagId, room: movingRoom, }}); - // Run all setTimeouts for dispatches and room list rate limiting - clock.runAll(); - expectRoomInSubList(movingRoom, destSubListTest); } @@ -269,6 +274,12 @@ describe('RoomList', () => { }; GroupStore._notifyListeners(); + // We also have to mock the client's getGroup function for the room list to filter it. + // It's not smart enough to tell the difference between a real group and a template though. + client.getGroup = (groupId) => { + return {groupId}; + }; + // Select tag dis.dispatch({action: 'select_tag', tag: '+group:domain'}, true); } @@ -277,16 +288,13 @@ describe('RoomList', () => { setupSelectedTag(); }); - it('displays the correct rooms when the groups rooms are changed', () => { + it('displays the correct rooms when the groups rooms are changed', async () => { GroupStore.getGroupRooms = (groupId) => { return [movingRoom, otherRoom]; }; GroupStore._notifyListeners(); - // Run through RoomList debouncing - clock.runAll(); - - // By default, the test will + await waitForRoomListStoreUpdate(); expectRoomInSubList(otherRoom, (s) => s.props.tagId === DefaultTagID.Untagged); }); From 767db73853f58d116d2007bdaba31338c46c4ed5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 15:50:04 -0600 Subject: [PATCH 05/17] Appease the linter --- test/components/views/rooms/RoomList-test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index ba53676fc1..ade74dfe44 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -1,7 +1,6 @@ import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; import ReactDOM from 'react-dom'; -import lolex from 'lolex'; import * as TestUtils from '../../../test-utils'; From 044c2238990dfb6ecd164dcbed89680d7f17bf9b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 15:53:12 -0600 Subject: [PATCH 06/17] Remove debug --- test/components/views/rooms/RoomList-test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index ade74dfe44..7876ad0f18 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -135,7 +135,6 @@ describe('RoomList', () => { try { const roomTiles = ReactTestUtils.scryRenderedComponentsWithType(containingSubList, RoomTile); console.info({roomTiles: roomTiles.length}); - console.log("IS SAME?", room === roomTiles[0].props.room, room, roomTiles[0].props.room); expectedRoomTile = roomTiles.find((tile) => tile.props.room === room); } catch (err) { // truncate the error message because it's spammy From 85af3ebcc0b3548a4fc3915bb721fd27b0783600 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 15:58:15 -0600 Subject: [PATCH 07/17] Lie about DMs in tests --- test/components/views/rooms/RoomList-test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 7876ad0f18..91e5e77937 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -56,6 +56,11 @@ describe('RoomList', () => { DMRoomMap.makeShared(); + // Lie to the room list store about DMs not existing + DMRoomMap.getUserIdForRoomId = () => { + return null; + } + parentDiv = document.createElement('div'); document.body.appendChild(parentDiv); From 5ace405062cf150c9c9672c593bbed78abae688c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 16:01:42 -0600 Subject: [PATCH 08/17] The linter will never be appeased --- test/components/views/rooms/RoomList-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 91e5e77937..3521ec0705 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -59,7 +59,7 @@ describe('RoomList', () => { // Lie to the room list store about DMs not existing DMRoomMap.getUserIdForRoomId = () => { return null; - } + }; parentDiv = document.createElement('div'); document.body.appendChild(parentDiv); From 121e41d20b4bc9baa73d7a2ea36f90a1b8b6804f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 16:20:53 -0600 Subject: [PATCH 09/17] Remove irrelevant function --- test/components/views/rooms/RoomList-test.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 3521ec0705..7876ad0f18 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -56,11 +56,6 @@ describe('RoomList', () => { DMRoomMap.makeShared(); - // Lie to the room list store about DMs not existing - DMRoomMap.getUserIdForRoomId = () => { - return null; - }; - parentDiv = document.createElement('div'); document.body.appendChild(parentDiv); From f12d9512098dbd4e56dc1f985f042a6d79e6ff5f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 16:34:42 -0600 Subject: [PATCH 10/17] Update end-to-end tests for new room list --- .../src/usecases/accept-invite.js | 6 ++-- .../src/usecases/create-room.js | 36 +++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/test/end-to-end-tests/src/usecases/accept-invite.js b/test/end-to-end-tests/src/usecases/accept-invite.js index 3f208cc1fc..a61aaec64c 100644 --- a/test/end-to-end-tests/src/usecases/accept-invite.js +++ b/test/end-to-end-tests/src/usecases/accept-invite.js @@ -15,10 +15,12 @@ See the License for the specific language governing permissions and limitations under the License. */ +const {findSublist} = require("./create-room"); + module.exports = async function acceptInvite(session, name) { session.log.step(`accepts "${name}" invite`); - //TODO: brittle selector - const invitesHandles = await session.queryAll('.mx_RoomTile_name.mx_RoomTile_invite'); + const inviteSublist = await findSublist("invites"); + const invitesHandles = await inviteSublist.$(".mx_RoomTile2_name"); const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => { const text = await session.innerText(inviteHandle); return {inviteHandle, text}; diff --git a/test/end-to-end-tests/src/usecases/create-room.js b/test/end-to-end-tests/src/usecases/create-room.js index 7e219fd159..50cb1e02f3 100644 --- a/test/end-to-end-tests/src/usecases/create-room.js +++ b/test/end-to-end-tests/src/usecases/create-room.js @@ -16,21 +16,27 @@ limitations under the License. */ async function openRoomDirectory(session) { - const roomDirectoryButton = await session.query('.mx_LeftPanel_explore .mx_AccessibleButton'); + const roomDirectoryButton = await session.query('.mx_LeftPanel2_exploreButton'); await roomDirectoryButton.click(); } +async function findSublist(name) { + const sublists = await session.queryAll('.mx_RoomSublist2'); + for (const sublist of sublists) { + const header = await sublist.$('.mx_RoomSublist2_headerText'); + const headerText = await session.innerText(header); + if (headerText.toLowerCase().includes(name.toLowerCase())) { + return sublist; + } + } + throw new Error(`could not find room list section that contains '${name}' in header`); +} + async function createRoom(session, roomName, encrypted=false) { session.log.step(`creates room "${roomName}"`); - const roomListHeaders = await session.queryAll('.mx_RoomSubList_labelContainer'); - const roomListHeaderLabels = await Promise.all(roomListHeaders.map(h => session.innerText(h))); - const roomsIndex = roomListHeaderLabels.findIndex(l => l.toLowerCase().includes("rooms")); - if (roomsIndex === -1) { - throw new Error("could not find room list section that contains 'rooms' in header"); - } - const roomsHeader = roomListHeaders[roomsIndex]; - const addRoomButton = await roomsHeader.$(".mx_RoomSubList_addRoom"); + const roomsSublist = await findSublist("rooms"); + const addRoomButton = await roomsSublist.$(".mx_RoomSublist2_auxButton"); await addRoomButton.click(); const roomNameInput = await session.query('.mx_CreateRoomDialog_name input'); @@ -51,14 +57,8 @@ async function createRoom(session, roomName, encrypted=false) { async function createDm(session, invitees) { session.log.step(`creates DM with ${JSON.stringify(invitees)}`); - const roomListHeaders = await session.queryAll('.mx_RoomSubList_labelContainer'); - const roomListHeaderLabels = await Promise.all(roomListHeaders.map(h => session.innerText(h))); - const dmsIndex = roomListHeaderLabels.findIndex(l => l.toLowerCase().includes('direct messages')); - if (dmsIndex === -1) { - throw new Error("could not find room list section that contains 'direct messages' in header"); - } - const dmsHeader = roomListHeaders[dmsIndex]; - const startChatButton = await dmsHeader.$(".mx_RoomSubList_addRoom"); + const dmsSublist = await findSublist("people"); + const startChatButton = await dmsSublist.$(".mx_RoomSublist2_auxButton"); await startChatButton.click(); const inviteesEditor = await session.query('.mx_InviteDialog_editor textarea'); @@ -83,4 +83,4 @@ async function createDm(session, invitees) { session.log.done(); } -module.exports = {openRoomDirectory, createRoom, createDm}; +module.exports = {openRoomDirectory, findSublist, createRoom, createDm}; From 9000888013f27f75b106719832eeb88f571414dc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 16:38:24 -0600 Subject: [PATCH 11/17] Pass the session through --- test/end-to-end-tests/src/usecases/accept-invite.js | 2 +- test/end-to-end-tests/src/usecases/create-room.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/end-to-end-tests/src/usecases/accept-invite.js b/test/end-to-end-tests/src/usecases/accept-invite.js index a61aaec64c..27992e16df 100644 --- a/test/end-to-end-tests/src/usecases/accept-invite.js +++ b/test/end-to-end-tests/src/usecases/accept-invite.js @@ -19,7 +19,7 @@ const {findSublist} = require("./create-room"); module.exports = async function acceptInvite(session, name) { session.log.step(`accepts "${name}" invite`); - const inviteSublist = await findSublist("invites"); + const inviteSublist = await findSublist(session, "invites"); const invitesHandles = await inviteSublist.$(".mx_RoomTile2_name"); const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => { const text = await session.innerText(inviteHandle); diff --git a/test/end-to-end-tests/src/usecases/create-room.js b/test/end-to-end-tests/src/usecases/create-room.js index 50cb1e02f3..24e42b92dd 100644 --- a/test/end-to-end-tests/src/usecases/create-room.js +++ b/test/end-to-end-tests/src/usecases/create-room.js @@ -20,7 +20,7 @@ async function openRoomDirectory(session) { await roomDirectoryButton.click(); } -async function findSublist(name) { +async function findSublist(session, name) { const sublists = await session.queryAll('.mx_RoomSublist2'); for (const sublist of sublists) { const header = await sublist.$('.mx_RoomSublist2_headerText'); @@ -35,7 +35,7 @@ async function findSublist(name) { async function createRoom(session, roomName, encrypted=false) { session.log.step(`creates room "${roomName}"`); - const roomsSublist = await findSublist("rooms"); + const roomsSublist = await findSublist(session, "rooms"); const addRoomButton = await roomsSublist.$(".mx_RoomSublist2_auxButton"); await addRoomButton.click(); @@ -57,7 +57,7 @@ async function createRoom(session, roomName, encrypted=false) { async function createDm(session, invitees) { session.log.step(`creates DM with ${JSON.stringify(invitees)}`); - const dmsSublist = await findSublist("people"); + const dmsSublist = await findSublist(session, "people"); const startChatButton = await dmsSublist.$(".mx_RoomSublist2_auxButton"); await startChatButton.click(); From 9bf2505e51db82afe2ef5d8e8b4356e9f25b79fc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 7 Jul 2020 16:48:03 -0600 Subject: [PATCH 12/17] queryAll, not just query --- test/end-to-end-tests/src/usecases/accept-invite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/end-to-end-tests/src/usecases/accept-invite.js b/test/end-to-end-tests/src/usecases/accept-invite.js index 27992e16df..d38fdcd0db 100644 --- a/test/end-to-end-tests/src/usecases/accept-invite.js +++ b/test/end-to-end-tests/src/usecases/accept-invite.js @@ -20,7 +20,7 @@ const {findSublist} = require("./create-room"); module.exports = async function acceptInvite(session, name) { session.log.step(`accepts "${name}" invite`); const inviteSublist = await findSublist(session, "invites"); - const invitesHandles = await inviteSublist.$(".mx_RoomTile2_name"); + const invitesHandles = await inviteSublist.$$(".mx_RoomTile2_name"); const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => { const text = await session.innerText(inviteHandle); return {inviteHandle, text}; From bba819759215326d417f6ea5d3de8ee1074d06cd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 9 Jul 2020 22:40:34 -0600 Subject: [PATCH 13/17] Use the new layout store --- test/components/views/rooms/RoomList-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 7876ad0f18..81ccf7d7f8 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -15,6 +15,7 @@ import GroupStore from '../../../../src/stores/GroupStore.js'; import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk'; import {DefaultTagID} from "../../../../src/stores/room-list/models"; import RoomListStore, {LISTS_UPDATE_EVENT} from "../../../../src/stores/room-list/RoomListStore2"; +import RoomListLayoutStore from "../../../../src/stores/room-list/RoomListLayoutStore"; function generateRoomId() { return '!' + Math.random().toString().slice(2, 10) + ':domain'; @@ -118,7 +119,7 @@ describe('RoomList', () => { parentDiv = null; } - await RoomListStore.instance.resetLayouts(); + await RoomListLayoutStore.instance.resetLayouts(); await RoomListStore.instance.resetStore(); done(); From f8db0a4637ada26406cfd475d287f61d523381c5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 10 Jul 2020 10:21:00 -0600 Subject: [PATCH 14/17] Resolve complex merge conflicts --- src/stores/room-list/RoomListStore2.ts | 33 +++++++------------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index bbc2e7f478..d53514dd7a 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -97,11 +97,14 @@ export class RoomListStore2 extends AsyncStore { this._matrixClient = client; + // Update any settings here, as some may have happened before we were logically ready. // Update any settings here, as some may have happened before we were logically ready. console.log("Regenerating room lists: Startup"); await this.readAndCacheSettingsFromStore(); - await this.regenerateAllLists(); - this.onRVSUpdate(); // fake an RVS update to adjust sticky room, if needed + await this.regenerateAllLists({trigger: false}); + await this.handleRVSUpdate({trigger: false}); // fake an RVS update to adjust sticky room, if needed + + this.updateFn.trigger(); } // TODO: Remove enabled flag with the old RoomListStore: https://github.com/vector-im/riot-web/issues/14367 @@ -162,25 +165,9 @@ export class RoomListStore2 extends AsyncStore { return; } -<<<<<<< await this.makeReady(payload.matrixClient); -======= - // TODO: Remove with https://github.com/vector-im/riot-web/issues/14231 - this.checkEnabled(); - if (!this.enabled) return; - - this._matrixClient = payload.matrixClient; - - // Update any settings here, as some may have happened before we were logically ready. - console.log("Regenerating room lists: Startup"); - await this.readAndCacheSettingsFromStore(); - await this.regenerateAllLists({trigger: false}); - await this.handleRVSUpdate({trigger: false}); // fake an RVS update to adjust sticky room, if needed - - this.updateFn.trigger(); return; // no point in running the next conditions - they won't match ->>>>>>> } // TODO: Remove this once the RoomListStore becomes default @@ -511,17 +498,15 @@ export class RoomListStore2 extends AsyncStore { this.updateFn.mark(); }; -<<<<<<< - // This is only exposed externally for the tests. Do not call this within the app. - public async regenerateAllLists() { -======= /** * Regenerates the room whole room list, discarding any previous results. + * + * Note: This is only exposed externally for the tests. Do not call this from within + * the app. * @param trigger Set to false to prevent a list update from being sent. Should only * be used if the calling code will manually trigger the update. */ - private async regenerateAllLists({trigger = true}) { ->>>>>>> + public async regenerateAllLists({trigger = true}) { console.warn("Regenerating all room lists"); const sorts: ITagSortingMap = {}; From 314250a6e4234aa56c0c967de983000377801f5c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 10 Jul 2020 10:38:07 -0600 Subject: [PATCH 15/17] Add a test mode flag to the store --- src/stores/room-list/RoomListStore2.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index d53514dd7a..acbb72f1d8 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -46,6 +46,12 @@ interface IState { export const LISTS_UPDATE_EVENT = "lists_update"; export class RoomListStore2 extends AsyncStore { + /** + * Set to true if you're running tests on the store. Should not be touched in + * any other environment. + */ + public static TEST_MODE = false; + private _matrixClient: MatrixClient; private initialListsGenerated = false; private enabled = false; @@ -104,6 +110,7 @@ export class RoomListStore2 extends AsyncStore { await this.regenerateAllLists({trigger: false}); await this.handleRVSUpdate({trigger: false}); // fake an RVS update to adjust sticky room, if needed + this.updateFn.mark(); // we almost certainly want to trigger an update. this.updateFn.trigger(); } @@ -152,7 +159,14 @@ export class RoomListStore2 extends AsyncStore { if (trigger) this.updateFn.trigger(); } - protected onDispatch(payload: ActionPayload) { + protected async onDispatch(payload: ActionPayload) { + // When we're running tests we can't reliably use setImmediate out of timing concerns. + // As such, we use a more synchronous model. + if (RoomListStore2.TEST_MODE) { + await this.onDispatchAsync(payload); + return; + } + // We do this to intentionally break out of the current event loop task, allowing // us to instead wait for a more convenient time to run our updates. setImmediate(() => this.onDispatchAsync(payload)); From 3826d8135838f494cba4d6c834b279a8b54394b3 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 10 Jul 2020 11:05:56 -0600 Subject: [PATCH 16/17] Enable test mode --- test/components/views/rooms/RoomList-test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 81ccf7d7f8..e84f943708 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -14,7 +14,7 @@ import GroupStore from '../../../../src/stores/GroupStore.js'; import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk'; import {DefaultTagID} from "../../../../src/stores/room-list/models"; -import RoomListStore, {LISTS_UPDATE_EVENT} from "../../../../src/stores/room-list/RoomListStore2"; +import RoomListStore, {LISTS_UPDATE_EVENT, RoomListStore2} from "../../../../src/stores/room-list/RoomListStore2"; import RoomListLayoutStore from "../../../../src/stores/room-list/RoomListLayoutStore"; function generateRoomId() { @@ -49,6 +49,8 @@ describe('RoomList', () => { let myOtherMember; beforeEach(async function(done) { + RoomListStore2.TEST_MODE = true; + TestUtils.stubClient(); client = MatrixClientPeg.get(); client.credentials = {userId: myUserId}; From bb6d46f92666b302523b3d849ed3e5b7f9f7c61f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 10 Jul 2020 15:57:05 -0600 Subject: [PATCH 17/17] When the algorithm changes, re-add the filter listener --- src/stores/room-list/RoomListStore2.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stores/room-list/RoomListStore2.ts b/src/stores/room-list/RoomListStore2.ts index 0d1ddfc4ca..75e43b3b57 100644 --- a/src/stores/room-list/RoomListStore2.ts +++ b/src/stores/room-list/RoomListStore2.ts @@ -92,8 +92,10 @@ export class RoomListStore2 extends AsyncStore { this._matrixClient = null; this.algorithm.off(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); + this.algorithm.off(FILTER_CHANGED, this.onAlgorithmListUpdated); this.algorithm = new Algorithm(); this.algorithm.on(LIST_UPDATED_EVENT, this.onAlgorithmListUpdated); + this.algorithm.on(FILTER_CHANGED, this.onAlgorithmListUpdated); } // Public for test usage. Do not call this.