Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 39d453a

Browse files
authored
Stop using the js-sdk's compare function (#12782)
* Stop using the js-sdk's compare function The file is supposed to be a js-sdk internal module so we shouldn't have been using it, and now it uses the native collator, it's completely trivial. It was also causing Intl.Collator to be accessed at the module scope which risked it beating the modernizr check. * add test * Fix tests Move the restoreAllMocks to prevent mock leakage and also add some custom themes to test the ordering of those. * Move spy to the right place * Add ANOTHER test * Add test for integration manager ordering
1 parent 3c9bd69 commit 39d453a

File tree

10 files changed

+146
-29
lines changed

10 files changed

+146
-29
lines changed

src/components/views/rooms/WhoIsTypingTile.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717

1818
import React from "react";
1919
import { Room, RoomEvent, RoomMember, RoomMemberEvent, MatrixEvent } from "matrix-js-sdk/src/matrix";
20-
import { compare } from "matrix-js-sdk/src/utils";
2120

2221
import * as WhoIsTyping from "../../../WhoIsTyping";
2322
import Timer from "../../../utils/Timer";
@@ -208,7 +207,8 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
208207

209208
// sort them so the typing members don't change order when
210209
// moved to delayedStopTypingTimers
211-
usersTyping.sort((a, b) => compare(a.name, b.name));
210+
const collator = new Intl.Collator();
211+
usersTyping.sort((a, b) => collator.compare(a.name, b.name));
212212

213213
const typingString = WhoIsTyping.whoIsTypingString(usersTyping, this.props.whoIsTypingLimit);
214214
if (!typingString) {

src/components/views/settings/PowerLevelSelector.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import React, { useState, JSX, PropsWithChildren } from "react";
2020
import { Button } from "@vector-im/compound-web";
21-
import { compare } from "matrix-js-sdk/src/utils";
2221

2322
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
2423
import PowerSelector from "../elements/PowerSelector";
@@ -78,9 +77,11 @@ export function PowerLevelSelector({
7877
currentPowerLevel && currentPowerLevel.value !== userLevels[currentPowerLevel?.userId],
7978
);
8079

80+
const collator = new Intl.Collator();
81+
8182
// We sort the users by power level, then we filter them
8283
const users = Object.keys(userLevels)
83-
.sort((userA, userB) => sortUser(userA, userB, userLevels))
84+
.sort((userA, userB) => sortUser(collator, userA, userB, userLevels))
8485
.filter(filter);
8586

8687
// No user to display, we return the children into fragment to convert it to JSX.Element type
@@ -136,7 +137,14 @@ export function PowerLevelSelector({
136137
* @param userB
137138
* @param userLevels
138139
*/
139-
function sortUser(userA: string, userB: string, userLevels: PowerLevelSelectorProps["userLevels"]): number {
140+
function sortUser(
141+
collator: Intl.Collator,
142+
userA: string,
143+
userB: string,
144+
userLevels: PowerLevelSelectorProps["userLevels"],
145+
): number {
140146
const powerLevelDiff = userLevels[userA] - userLevels[userB];
141-
return powerLevelDiff !== 0 ? powerLevelDiff : compare(userA.toLocaleLowerCase(), userB.toLocaleLowerCase());
147+
return powerLevelDiff !== 0
148+
? powerLevelDiff
149+
: collator.compare(userA.toLocaleLowerCase(), userB.toLocaleLowerCase());
142150
}

src/integrations/IntegrationManagers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ limitations under the License.
1616

1717
import { logger } from "matrix-js-sdk/src/logger";
1818
import { ClientEvent, IClientWellKnown, MatrixClient } from "matrix-js-sdk/src/matrix";
19-
import { compare } from "matrix-js-sdk/src/utils";
2019

2120
import type { MatrixEvent } from "matrix-js-sdk/src/matrix";
2221
import SdkConfig from "../SdkConfig";
@@ -145,14 +144,15 @@ export class IntegrationManagers {
145144
}
146145

147146
public getOrderedManagers(): IntegrationManagerInstance[] {
147+
const collator = new Intl.Collator();
148148
const ordered: IntegrationManagerInstance[] = [];
149149
for (const kind of KIND_PREFERENCE) {
150150
const managers = this.managers.filter((m) => m.kind === kind);
151151
if (!managers || !managers.length) continue;
152152

153153
if (kind === Kind.Account) {
154154
// Order by state_keys (IDs)
155-
managers.sort((a, b) => compare(a.id ?? "", b.id ?? ""));
155+
managers.sort((a, b) => collator.compare(a.id ?? "", b.id ?? ""));
156156
}
157157

158158
ordered.push(...managers);

src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ limitations under the License.
1515
*/
1616

1717
import { Room } from "matrix-js-sdk/src/matrix";
18-
import { compare } from "matrix-js-sdk/src/utils";
1918

2019
import { TagID } from "../../models";
2120
import { IAlgorithm } from "./IAlgorithm";
@@ -25,8 +24,9 @@ import { IAlgorithm } from "./IAlgorithm";
2524
*/
2625
export class AlphabeticAlgorithm implements IAlgorithm {
2726
public sortRooms(rooms: Room[], tagId: TagID): Room[] {
27+
const collator = new Intl.Collator();
2828
return rooms.sort((a, b) => {
29-
return compare(a.name, b.name);
29+
return collator.compare(a.name, b.name);
3030
});
3131
}
3232
}

src/stores/widgets/WidgetLayoutStore.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { Room, RoomStateEvent, MatrixEvent } from "matrix-js-sdk/src/matrix";
1818
import { Optional } from "matrix-events-sdk";
19-
import { compare, MapWithDefault, recursiveMapToObject } from "matrix-js-sdk/src/utils";
19+
import { MapWithDefault, recursiveMapToObject } from "matrix-js-sdk/src/utils";
2020
import { IWidget } from "matrix-widget-api";
2121

2222
import SettingsStore from "../../settings/SettingsStore";
@@ -200,6 +200,8 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
200200
const runoff = topWidgets.slice(MAX_PINNED);
201201
rightWidgets.push(...runoff);
202202

203+
const collator = new Intl.Collator();
204+
203205
// Order the widgets in the top container, putting autopinned Jitsi widgets first
204206
// unless they have a specific order in mind
205207
topWidgets.sort((a, b) => {
@@ -219,7 +221,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
219221

220222
if (orderA === orderB) {
221223
// We just need a tiebreak
222-
return compare(a.id, b.id);
224+
return collator.compare(a.id, b.id);
223225
}
224226

225227
return orderA - orderB;

src/theme.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ See the License for the specific language governing permissions and
1515
limitations under the License.
1616
*/
1717

18-
import { compare } from "matrix-js-sdk/src/utils";
1918
import { logger } from "matrix-js-sdk/src/logger";
2019

2120
import { _t } from "./languageHandler";
@@ -113,7 +112,10 @@ export function getOrderedThemes(): ITheme[] {
113112
.map((p) => ({ id: p[0], name: p[1] })) // convert pairs to objects for code readability
114113
.filter((p) => !isHighContrastTheme(p.id));
115114
const builtInThemes = themes.filter((p) => !p.id.startsWith("custom-"));
116-
const customThemes = themes.filter((p) => !builtInThemes.includes(p)).sort((a, b) => compare(a.name, b.name));
115+
const collator = new Intl.Collator();
116+
const customThemes = themes
117+
.filter((p) => !builtInThemes.includes(p))
118+
.sort((a, b) => collator.compare(a.name, b.name));
117119
return [...builtInThemes, ...customThemes];
118120
}
119121

test/components/views/rooms/MemberList-test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import React from "react";
1919
import { act, fireEvent, render, RenderResult, screen } from "@testing-library/react";
2020
import { Room, MatrixClient, RoomState, RoomMember, User, MatrixEvent } from "matrix-js-sdk/src/matrix";
2121
import { KnownMembership } from "matrix-js-sdk/src/types";
22-
import { compare } from "matrix-js-sdk/src/utils";
2322
import { mocked, MockedObject } from "jest-mock";
2423

2524
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
@@ -145,7 +144,8 @@ describe("MemberList", () => {
145144
if (!groupChange) {
146145
const nameA = memberA.name[0] === "@" ? memberA.name.slice(1) : memberA.name;
147146
const nameB = memberB.name[0] === "@" ? memberB.name.slice(1) : memberB.name;
148-
const nameCompare = compare(nameB, nameA);
147+
const collator = new Intl.Collator();
148+
const nameCompare = collator.compare(nameB, nameA);
149149
console.log("Comparing name");
150150
expect(nameCompare).toBeGreaterThanOrEqual(0);
151151
} else {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
Copyright 2024 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
18+
import { mocked } from "jest-mock";
19+
20+
import { IntegrationManagers } from "../../src/integrations/IntegrationManagers";
21+
import { stubClient } from "../test-utils";
22+
23+
describe("IntegrationManagers", () => {
24+
let client: MatrixClient;
25+
let intMgrs: IntegrationManagers;
26+
27+
beforeEach(() => {
28+
client = stubClient();
29+
mocked(client).getAccountData.mockReturnValue({
30+
getContent: jest.fn().mockReturnValue({
31+
foo: {
32+
id: "foo",
33+
content: {
34+
type: "m.integration_manager",
35+
url: "http://foo/ui",
36+
data: {
37+
api_url: "http://foo/api",
38+
},
39+
},
40+
},
41+
bar: {
42+
id: "bar",
43+
content: {
44+
type: "m.integration_manager",
45+
url: "http://bar/ui",
46+
data: {
47+
api_url: "http://bar/api",
48+
},
49+
},
50+
},
51+
}),
52+
} as unknown as MatrixEvent);
53+
54+
intMgrs = new IntegrationManagers();
55+
intMgrs.startWatching();
56+
});
57+
58+
afterEach(() => {
59+
intMgrs.stopWatching();
60+
});
61+
62+
describe("getOrderedManagers", () => {
63+
it("should return integration managers in alphabetical order", () => {
64+
const orderedManagers = intMgrs.getOrderedManagers();
65+
66+
expect(orderedManagers[0].id).toBe("bar");
67+
expect(orderedManagers[1].id).toBe("foo");
68+
});
69+
});
70+
});

test/stores/WidgetLayoutStore-test.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,29 @@ import SettingsStore from "../../src/settings/SettingsStore";
2525

2626
// setup test env values
2727
const roomId = "!room:server";
28-
const mockRoom = <Room>{
29-
roomId: roomId,
30-
currentState: {
31-
getStateEvents: (_l, _x) => {
32-
return {
33-
getId: () => "$layoutEventId",
34-
getContent: () => null,
35-
};
36-
},
37-
},
38-
};
3928

4029
describe("WidgetLayoutStore", () => {
4130
let client: MatrixClient;
4231
let store: WidgetLayoutStore;
4332
let roomUpdateListener: (event: string) => void;
4433
let mockApps: IApp[];
34+
let mockRoom: Room;
35+
let layoutEventContent: Record<string, any> | null;
4536

4637
beforeEach(() => {
38+
layoutEventContent = null;
39+
mockRoom = <Room>{
40+
roomId: roomId,
41+
currentState: {
42+
getStateEvents: (_l, _x) => {
43+
return {
44+
getId: () => "$layoutEventId",
45+
getContent: () => layoutEventContent,
46+
};
47+
},
48+
},
49+
};
50+
4751
mockApps = [
4852
<IApp>{ roomId: roomId, id: "1" },
4953
<IApp>{ roomId: roomId, id: "2" },
@@ -87,6 +91,22 @@ describe("WidgetLayoutStore", () => {
8791
expect(store.getContainerHeight(mockRoom, Container.Top)).toBeNull();
8892
});
8993

94+
it("ordering of top container widgets should be consistent even if no index specified", async () => {
95+
layoutEventContent = {
96+
widgets: {
97+
"1": {
98+
container: "top",
99+
},
100+
"2": {
101+
container: "top",
102+
},
103+
},
104+
};
105+
106+
store.recalculateRoom(mockRoom);
107+
expect(store.getContainerWidgets(mockRoom, Container.Top)).toStrictEqual([mockApps[0], mockApps[1]]);
108+
});
109+
90110
it("add three widgets to top container", async () => {
91111
store.recalculateRoom(mockRoom);
92112
store.moveToContainer(mockRoom, mockApps[0], Container.Top);

test/theme-test.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@ limitations under the License.
1515
*/
1616

1717
import SettingsStore from "../src/settings/SettingsStore";
18-
import { enumerateThemes, setTheme } from "../src/theme";
18+
import { enumerateThemes, getOrderedThemes, setTheme } from "../src/theme";
1919

2020
describe("theme", () => {
21+
afterEach(() => {
22+
jest.restoreAllMocks();
23+
});
24+
2125
describe("setTheme", () => {
2226
let lightTheme: HTMLStyleElement;
2327
let darkTheme: HTMLStyleElement;
@@ -48,7 +52,6 @@ describe("theme", () => {
4852
});
4953

5054
afterEach(() => {
51-
jest.restoreAllMocks();
5255
jest.useRealTimers();
5356
});
5457

@@ -162,4 +165,16 @@ describe("theme", () => {
162165
});
163166
});
164167
});
168+
169+
describe("getOrderedThemes", () => {
170+
it("should return a list of themes in the correct order", () => {
171+
jest.spyOn(SettingsStore, "getValue").mockReturnValue([{ name: "Zebra Striped" }, { name: "Apple Green" }]);
172+
expect(getOrderedThemes()).toEqual([
173+
{ id: "light", name: "Light" },
174+
{ id: "dark", name: "Dark" },
175+
{ id: "custom-Apple Green", name: "Apple Green" },
176+
{ id: "custom-Zebra Striped", name: "Zebra Striped" },
177+
]);
178+
});
179+
});
165180
});

0 commit comments

Comments
 (0)