1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

Test typescriptification - room-member and room-state (#2601)

* renamed:    spec/MockStorageApi.js -> spec/MockStorageApi.ts

* renamed:    spec/olm-loader.js -> spec/olm-loader.t

* renamed:    spec/unit/room-state.spec.js -> spec/unit/room-state.spec.ts

* ts fixes in room-state.spec

* renamed:    spec/unit/room-member.spec.js -> spec/unit/room-member.spec.ts

* ts fixes in room-member.spec

* strict mode fixes for MockStorageApi

* strict ts fixes in room-state

* strict errors
This commit is contained in:
Kerry
2022-09-05 10:38:05 +02:00
committed by GitHub
parent e87ce873b0
commit 37187ef347
7 changed files with 380 additions and 333 deletions

View File

@ -1,6 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2019, 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,31 +17,32 @@ limitations under the License.
/**
* A mock implementation of the webstorage api
* @constructor
*/
export function MockStorageApi() {
this.data = {};
this.keys = [];
this.length = 0;
}
export class MockStorageApi {
public data: Record<string, string> = {};
public keys: string[] = [];
public length = 0;
MockStorageApi.prototype = {
setItem: function(k, v) {
public setItem(k: string, v: string): void {
this.data[k] = v;
this._recalc();
},
getItem: function(k) {
this.recalc();
}
public getItem(k: string): string | null {
return this.data[k] || null;
},
removeItem: function(k) {
}
public removeItem(k: string): void {
delete this.data[k];
this._recalc();
},
key: function(index) {
this.recalc();
}
public key(index: number): string {
return this.keys[index];
},
_recalc: function() {
const keys = [];
}
private recalc(): void {
const keys: string[] = [];
for (const k in this.data) {
if (!this.data.hasOwnProperty(k)) {
continue;
@ -50,6 +51,5 @@ MockStorageApi.prototype = {
}
this.keys = keys;
this.length = keys.length;
},
};
}
}

View File

@ -50,7 +50,7 @@ export class TestClient {
options?: Partial<ICreateClientOpts>,
) {
if (sessionStoreBackend === undefined) {
sessionStoreBackend = new MockStorageApi();
sessionStoreBackend = new MockStorageApi() as unknown as Storage;
}
this.httpBackend = new MockHttpBackend();

View File

@ -20,6 +20,7 @@ import * as utils from "../src/utils";
// try to load the olm library.
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
global.Olm = require('@matrix-org/olm');
logger.log('loaded libolm');
} catch (e) {
@ -28,6 +29,7 @@ try {
// also try to set node crypto
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const crypto = require('crypto');
utils.setCrypto(crypto);
} catch (err) {

View File

@ -24,5 +24,5 @@ limitations under the License.
* expect(beaconLivenessEmits.length).toBe(1);
* ```
*/
export const filterEmitCallsByEventType = (eventType: string, spy: jest.SpyInstance<any, unknown[]>) =>
export const filterEmitCallsByEventType = (eventType: string, spy: jest.SpyInstance<any, any[]>) =>
spy.mock.calls.filter((args) => args[0] === eventType);

View File

@ -15,6 +15,7 @@ import { CRYPTO_ENABLED } from "../../src/client";
import { DeviceInfo } from "../../src/crypto/deviceinfo";
import { logger } from '../../src/logger';
import { MemoryStore } from "../../src";
import { IStore } from '../../src/store';
const Olm = global.Olm;
@ -158,8 +159,8 @@ describe("Crypto", function() {
let fakeEmitter;
beforeEach(async function() {
const mockStorage = new MockStorageApi();
const clientStore = new MemoryStore({ localStorage: mockStorage });
const mockStorage = new MockStorageApi() as unknown as Storage;
const clientStore = new MemoryStore({ localStorage: mockStorage }) as unknown as IStore;
const cryptoStore = new MemoryCryptoStore();
cryptoStore.storeEndToEndDeviceData({
@ -469,12 +470,12 @@ describe("Crypto", function() {
jest.setTimeout(10000);
const client = (new TestClient("@a:example.com", "dev")).client;
await client.initCrypto();
client.crypto.getSecretStorageKey = async () => null;
client.crypto.getSecretStorageKey = jest.fn().mockResolvedValue(null);
client.crypto.isCrossSigningReady = async () => false;
client.crypto.baseApis.uploadDeviceSigningKeys = jest.fn().mockResolvedValue(null);
client.crypto.baseApis.setAccountData = () => null;
client.crypto.baseApis.uploadKeySignatures = () => null;
client.crypto.baseApis.http.authedRequest = () => null;
client.crypto.baseApis.setAccountData = jest.fn().mockResolvedValue(null);
client.crypto.baseApis.uploadKeySignatures = jest.fn();
client.crypto.baseApis.http.authedRequest = jest.fn();
const createSecretStorageKey = async () => {
return {
keyInfo: undefined, // Returning undefined here used to cause a crash

View File

@ -1,12 +1,29 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as utils from "../test-utils/test-utils";
import { RoomMember } from "../../src/models/room-member";
import { RoomMember, RoomMemberEvent } from "../../src/models/room-member";
import { RoomState } from "../../src";
describe("RoomMember", function() {
const roomId = "!foo:bar";
const userA = "@alice:bar";
const userB = "@bertha:bar";
const userC = "@clarissa:bar";
let member;
let member = new RoomMember(roomId, userA);
beforeEach(function() {
member = new RoomMember(roomId, userA);
@ -27,17 +44,17 @@ describe("RoomMember", function() {
avatar_url: "mxc://flibble/wibble",
},
});
const url = member.getAvatarUrl(hsUrl);
const url = member.getAvatarUrl(hsUrl, 1, 1, '', false, false);
// we don't care about how the mxc->http conversion is done, other
// than it contains the mxc body.
expect(url.indexOf("flibble/wibble")).not.toEqual(-1);
expect(url?.indexOf("flibble/wibble")).not.toEqual(-1);
});
it("should return nothing if there is no m.room.member and allowDefault=false",
function() {
const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", false);
expect(url).toEqual(null);
});
function() {
const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", false, false);
expect(url).toEqual(null);
});
});
describe("setPowerLevelEvent", function() {
@ -66,92 +83,92 @@ describe("RoomMember", function() {
});
it("should emit 'RoomMember.powerLevel' if the power level changes.",
function() {
const event = utils.mkEvent({
type: "m.room.power_levels",
room: roomId,
user: userA,
content: {
users_default: 20,
users: {
"@bertha:bar": 200,
"@invalid:user": 10, // shouldn't barf on this.
function() {
const event = utils.mkEvent({
type: "m.room.power_levels",
room: roomId,
user: userA,
content: {
users_default: 20,
users: {
"@bertha:bar": 200,
"@invalid:user": 10, // shouldn't barf on this.
},
},
},
event: true,
});
let emitCount = 0;
event: true,
});
let emitCount = 0;
member.on("RoomMember.powerLevel", function(emitEvent, emitMember) {
emitCount += 1;
expect(emitMember).toEqual(member);
expect(emitEvent).toEqual(event);
});
member.on(RoomMemberEvent.PowerLevel, function(emitEvent, emitMember) {
emitCount += 1;
expect(emitMember).toEqual(member);
expect(emitEvent).toEqual(event);
});
member.setPowerLevelEvent(event);
expect(emitCount).toEqual(1);
member.setPowerLevelEvent(event); // no-op
expect(emitCount).toEqual(1);
});
member.setPowerLevelEvent(event);
expect(emitCount).toEqual(1);
member.setPowerLevelEvent(event); // no-op
expect(emitCount).toEqual(1);
});
it("should honour power levels of zero.",
function() {
const event = utils.mkEvent({
type: "m.room.power_levels",
room: roomId,
user: userA,
content: {
users_default: 20,
users: {
"@alice:bar": 0,
function() {
const event = utils.mkEvent({
type: "m.room.power_levels",
room: roomId,
user: userA,
content: {
users_default: 20,
users: {
"@alice:bar": 0,
},
},
},
event: true,
});
let emitCount = 0;
event: true,
});
let emitCount = 0;
// set the power level to something other than zero or we
// won't get an event
member.powerLevel = 1;
member.on("RoomMember.powerLevel", function(emitEvent, emitMember) {
emitCount += 1;
expect(emitMember.userId).toEqual('@alice:bar');
expect(emitMember.powerLevel).toEqual(0);
expect(emitEvent).toEqual(event);
});
// set the power level to something other than zero or we
// won't get an event
member.powerLevel = 1;
member.on(RoomMemberEvent.PowerLevel, function(emitEvent, emitMember) {
emitCount += 1;
expect(emitMember.userId).toEqual('@alice:bar');
expect(emitMember.powerLevel).toEqual(0);
expect(emitEvent).toEqual(event);
});
member.setPowerLevelEvent(event);
expect(member.powerLevel).toEqual(0);
expect(emitCount).toEqual(1);
});
member.setPowerLevelEvent(event);
expect(member.powerLevel).toEqual(0);
expect(emitCount).toEqual(1);
});
it("should not honor string power levels.",
function() {
const event = utils.mkEvent({
type: "m.room.power_levels",
room: roomId,
user: userA,
content: {
users_default: 20,
users: {
"@alice:bar": "5",
function() {
const event = utils.mkEvent({
type: "m.room.power_levels",
room: roomId,
user: userA,
content: {
users_default: 20,
users: {
"@alice:bar": "5",
},
},
},
event: true,
});
let emitCount = 0;
event: true,
});
let emitCount = 0;
member.on("RoomMember.powerLevel", function(emitEvent, emitMember) {
emitCount += 1;
expect(emitMember.userId).toEqual('@alice:bar');
expect(emitMember.powerLevel).toEqual(20);
expect(emitEvent).toEqual(event);
});
member.on(RoomMemberEvent.PowerLevel, function(emitEvent, emitMember) {
emitCount += 1;
expect(emitMember.userId).toEqual('@alice:bar');
expect(emitMember.powerLevel).toEqual(20);
expect(emitEvent).toEqual(event);
});
member.setPowerLevelEvent(event);
expect(member.powerLevel).toEqual(20);
expect(emitCount).toEqual(1);
});
member.setPowerLevelEvent(event);
expect(member.powerLevel).toEqual(20);
expect(emitCount).toEqual(1);
});
});
describe("setTypingEvent", function() {
@ -183,34 +200,34 @@ describe("RoomMember", function() {
});
it("should emit 'RoomMember.typing' if the typing state changes",
function() {
const event = utils.mkEvent({
type: "m.typing",
room: roomId,
content: {
user_ids: [
userA, userC,
],
},
event: true,
function() {
const event = utils.mkEvent({
type: "m.typing",
room: roomId,
content: {
user_ids: [
userA, userC,
],
},
event: true,
});
let emitCount = 0;
member.on(RoomMemberEvent.Typing, function(ev, mem) {
expect(mem).toEqual(member);
expect(ev).toEqual(event);
emitCount += 1;
});
member.typing = false;
member.setTypingEvent(event);
expect(emitCount).toEqual(1);
member.setTypingEvent(event); // no-op
expect(emitCount).toEqual(1);
});
let emitCount = 0;
member.on("RoomMember.typing", function(ev, mem) {
expect(mem).toEqual(member);
expect(ev).toEqual(event);
emitCount += 1;
});
member.typing = false;
member.setTypingEvent(event);
expect(emitCount).toEqual(1);
member.setTypingEvent(event); // no-op
expect(emitCount).toEqual(1);
});
});
describe("isOutOfBand", function() {
it("should be set by markOutOfBand", function() {
const member = new RoomMember();
const member = new RoomMember(roomId, userA);
expect(member.isOutOfBand()).toEqual(false);
member.markOutOfBand();
expect(member.isOutOfBand()).toEqual(true);
@ -235,50 +252,50 @@ describe("RoomMember", function() {
});
it("should set 'membership' and assign the event to 'events.member'.",
function() {
member.setMembershipEvent(inviteEvent);
expect(member.membership).toEqual("invite");
expect(member.events.member).toEqual(inviteEvent);
member.setMembershipEvent(joinEvent);
expect(member.membership).toEqual("join");
expect(member.events.member).toEqual(joinEvent);
});
function() {
member.setMembershipEvent(inviteEvent);
expect(member.membership).toEqual("invite");
expect(member.events.member).toEqual(inviteEvent);
member.setMembershipEvent(joinEvent);
expect(member.membership).toEqual("join");
expect(member.events.member).toEqual(joinEvent);
});
it("should set 'name' based on user_id, displayname and room state",
function() {
const roomState = {
getStateEvents: function(type) {
if (type !== "m.room.member") {
return [];
}
return [
utils.mkMembership({
event: true, mship: "join", room: roomId,
user: userB,
}),
utils.mkMembership({
event: true, mship: "join", room: roomId,
user: userC, name: "Alice",
}),
joinEvent,
];
},
getUserIdsWithDisplayName: function(displayName) {
return [userA, userC];
},
};
expect(member.name).toEqual(userA); // default = user_id
member.setMembershipEvent(joinEvent);
expect(member.name).toEqual("Alice"); // prefer displayname
member.setMembershipEvent(joinEvent, roomState);
expect(member.name).not.toEqual("Alice"); // it should disambig.
// user_id should be there somewhere
expect(member.name.indexOf(userA)).not.toEqual(-1);
});
function() {
const roomState = {
getStateEvents: function(type) {
if (type !== "m.room.member") {
return [];
}
return [
utils.mkMembership({
event: true, mship: "join", room: roomId,
user: userB,
}),
utils.mkMembership({
event: true, mship: "join", room: roomId,
user: userC, name: "Alice",
}),
joinEvent,
];
},
getUserIdsWithDisplayName: function(displayName) {
return [userA, userC];
},
} as unknown as RoomState;
expect(member.name).toEqual(userA); // default = user_id
member.setMembershipEvent(joinEvent);
expect(member.name).toEqual("Alice"); // prefer displayname
member.setMembershipEvent(joinEvent, roomState);
expect(member.name).not.toEqual("Alice"); // it should disambig.
// user_id should be there somewhere
expect(member.name.indexOf(userA)).not.toEqual(-1);
});
it("should emit 'RoomMember.membership' if the membership changes", function() {
let emitCount = 0;
member.on("RoomMember.membership", function(ev, mem) {
member.on(RoomMemberEvent.Membership, function(ev, mem) {
emitCount += 1;
expect(mem).toEqual(member);
expect(ev).toEqual(inviteEvent);
@ -291,7 +308,7 @@ describe("RoomMember", function() {
it("should emit 'RoomMember.name' if the name changes", function() {
let emitCount = 0;
member.on("RoomMember.name", function(ev, mem) {
member.on(RoomMemberEvent.Name, function(ev, mem) {
emitCount += 1;
expect(mem).toEqual(member);
expect(ev).toEqual(joinEvent);
@ -341,7 +358,7 @@ describe("RoomMember", function() {
getUserIdsWithDisplayName: function(displayName) {
return [userA, userC];
},
};
} as unknown as RoomState;
expect(member.name).toEqual(userA); // default = user_id
member.setMembershipEvent(joinEvent, roomState);
expect(member.name).not.toEqual("Alíce"); // it should disambig.

View File

@ -1,14 +1,37 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { MockedObject } from 'jest-mock';
import * as utils from "../test-utils/test-utils";
import { makeBeaconEvent, makeBeaconInfoEvent } from "../test-utils/beacon";
import { filterEmitCallsByEventType } from "../test-utils/emitter";
import { RoomState, RoomStateEvent } from "../../src/models/room-state";
import { BeaconEvent, getBeaconInfoIdentifier } from "../../src/models/beacon";
import {
Beacon,
BeaconEvent,
getBeaconInfoIdentifier,
} from "../../src/models/beacon";
import { EventType, RelationType, UNSTABLE_MSC2716_MARKER } from "../../src/@types/event";
import {
MatrixEvent,
MatrixEventEvent,
} from "../../src/models/event";
import { M_BEACON } from "../../src/@types/beacon";
import { MatrixClient } from "../../src/client";
describe("RoomState", function() {
const roomId = "!foo:bar";
@ -17,7 +40,7 @@ describe("RoomState", function() {
const userC = "@cleo:bar";
const userLazy = "@lazy:bar";
let state;
let state = new RoomState(roomId);
beforeEach(function() {
state = new RoomState(roomId);
@ -67,8 +90,8 @@ describe("RoomState", function() {
it("should return a member which changes as state changes", function() {
const member = state.getMember(userB);
expect(member.membership).toEqual("join");
expect(member.name).toEqual(userB);
expect(member?.membership).toEqual("join");
expect(member?.name).toEqual(userB);
state.setStateEvents([
utils.mkMembership({
@ -77,40 +100,40 @@ describe("RoomState", function() {
}),
]);
expect(member.membership).toEqual("leave");
expect(member.name).toEqual("BobGone");
expect(member?.membership).toEqual("leave");
expect(member?.name).toEqual("BobGone");
});
});
describe("getSentinelMember", function() {
it("should return a member with the user id as name", function() {
expect(state.getSentinelMember("@no-one:here").name).toEqual("@no-one:here");
expect(state.getSentinelMember("@no-one:here")?.name).toEqual("@no-one:here");
});
it("should return a member which doesn't change when the state is updated",
function() {
const preLeaveUser = state.getSentinelMember(userA);
state.setStateEvents([
utils.mkMembership({
room: roomId, user: userA, mship: "leave", event: true,
name: "AliceIsGone",
}),
]);
const postLeaveUser = state.getSentinelMember(userA);
function() {
const preLeaveUser = state.getSentinelMember(userA);
state.setStateEvents([
utils.mkMembership({
room: roomId, user: userA, mship: "leave", event: true,
name: "AliceIsGone",
}),
]);
const postLeaveUser = state.getSentinelMember(userA);
expect(preLeaveUser.membership).toEqual("join");
expect(preLeaveUser.name).toEqual(userA);
expect(preLeaveUser?.membership).toEqual("join");
expect(preLeaveUser?.name).toEqual(userA);
expect(postLeaveUser.membership).toEqual("leave");
expect(postLeaveUser.name).toEqual("AliceIsGone");
});
expect(postLeaveUser?.membership).toEqual("leave");
expect(postLeaveUser?.name).toEqual("AliceIsGone");
});
});
describe("getStateEvents", function() {
it("should return null if a state_key was specified and there was no match",
function() {
expect(state.getStateEvents("foo.bar.baz", "keyname")).toEqual(null);
});
function() {
expect(state.getStateEvents("foo.bar.baz", "keyname")).toEqual(null);
});
it("should return an empty list if a state_key was not specified and there" +
" was no match", function() {
@ -118,21 +141,21 @@ describe("RoomState", function() {
});
it("should return a list of matching events if no state_key was specified",
function() {
const events = state.getStateEvents("m.room.member");
expect(events.length).toEqual(2);
// ordering unimportant
expect([userA, userB].indexOf(events[0].getStateKey())).not.toEqual(-1);
expect([userA, userB].indexOf(events[1].getStateKey())).not.toEqual(-1);
});
function() {
const events = state.getStateEvents("m.room.member");
expect(events.length).toEqual(2);
// ordering unimportant
expect([userA, userB].indexOf(events[0].getStateKey() as string)).not.toEqual(-1);
expect([userA, userB].indexOf(events[1].getStateKey() as string)).not.toEqual(-1);
});
it("should return a single MatrixEvent if a state_key was specified",
function() {
const event = state.getStateEvents("m.room.member", userA);
expect(event.getContent()).toMatchObject({
membership: "join",
function() {
const event = state.getStateEvents("m.room.member", userA);
expect(event.getContent()).toMatchObject({
membership: "join",
});
});
});
});
describe("setStateEvents", function() {
@ -146,7 +169,7 @@ describe("RoomState", function() {
}),
];
let emitCount = 0;
state.on("RoomState.members", function(ev, st, mem) {
state.on(RoomStateEvent.Members, function(ev, st, mem) {
expect(ev).toEqual(memberEvents[emitCount]);
expect(st).toEqual(state);
expect(mem).toEqual(state.getMember(ev.getSender()));
@ -166,7 +189,7 @@ describe("RoomState", function() {
}),
];
let emitCount = 0;
state.on("RoomState.newMember", function(ev, st, mem) {
state.on(RoomStateEvent.NewMember, function(ev, st, mem) {
expect(state.getMember(mem.userId)).toEqual(mem);
expect(mem.userId).toEqual(memberEvents[emitCount].getSender());
expect(mem.membership).toBeFalsy(); // not defined yet
@ -192,7 +215,7 @@ describe("RoomState", function() {
}),
];
let emitCount = 0;
state.on("RoomState.events", function(ev, st) {
state.on(RoomStateEvent.Events, function(ev, st) {
expect(ev).toEqual(events[emitCount]);
expect(st).toEqual(state);
emitCount += 1;
@ -272,7 +295,7 @@ describe("RoomState", function() {
}),
];
let emitCount = 0;
state.on("RoomState.Marker", function(markerEvent, markerFoundOptions) {
state.on(RoomStateEvent.Marker, function(markerEvent, markerFoundOptions) {
expect(markerEvent).toEqual(events[emitCount]);
expect(markerFoundOptions).toEqual({ timelineWasEmpty: true });
emitCount += 1;
@ -296,7 +319,7 @@ describe("RoomState", function() {
it('does not add redacted beacon info events to state', () => {
const redactedBeaconEvent = makeBeaconInfoEvent(userA, roomId);
const redactionEvent = { event: { type: 'm.room.redaction' } };
const redactionEvent = new MatrixEvent({ type: 'm.room.redaction' });
redactedBeaconEvent.makeRedacted(redactionEvent);
const emitSpy = jest.spyOn(state, 'emit');
@ -316,27 +339,27 @@ describe("RoomState", function() {
state.setStateEvents([beaconEvent]);
const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beaconEvent));
expect(beaconInstance.isLive).toEqual(true);
expect(beaconInstance?.isLive).toEqual(true);
state.setStateEvents([updatedBeaconEvent]);
// same Beacon
expect(state.beacons.get(getBeaconInfoIdentifier(beaconEvent))).toBe(beaconInstance);
// updated liveness
expect(state.beacons.get(getBeaconInfoIdentifier(beaconEvent)).isLive).toEqual(false);
expect(state.beacons.get(getBeaconInfoIdentifier(beaconEvent))?.isLive).toEqual(false);
});
it('destroys and removes redacted beacon events', () => {
const beaconId = '$beacon1';
const beaconEvent = makeBeaconInfoEvent(userA, roomId, { isLive: true }, beaconId);
const redactedBeaconEvent = makeBeaconInfoEvent(userA, roomId, { isLive: true }, beaconId);
const redactionEvent = { event: { type: 'm.room.redaction', redacts: beaconEvent.getId() } };
const redactionEvent = new MatrixEvent({ type: 'm.room.redaction', redacts: beaconEvent.getId() });
redactedBeaconEvent.makeRedacted(redactionEvent);
state.setStateEvents([beaconEvent]);
const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beaconEvent));
const destroySpy = jest.spyOn(beaconInstance, 'destroy');
expect(beaconInstance.isLive).toEqual(true);
const destroySpy = jest.spyOn(beaconInstance as Beacon, 'destroy');
expect(beaconInstance?.isLive).toEqual(true);
state.setStateEvents([redactedBeaconEvent]);
@ -357,7 +380,7 @@ describe("RoomState", function() {
// live beacon is now not live
const updatedLiveBeaconEvent = makeBeaconInfoEvent(
userA, roomId, { isLive: false }, liveBeaconEvent.getId(), '$beacon1',
userA, roomId, { isLive: false }, liveBeaconEvent.getId(),
);
state.setStateEvents([updatedLiveBeaconEvent]);
@ -377,8 +400,8 @@ describe("RoomState", function() {
state.markOutOfBandMembersStarted();
state.setOutOfBandMembers([oobMemberEvent]);
const member = state.getMember(userLazy);
expect(member.userId).toEqual(userLazy);
expect(member.isOutOfBand()).toEqual(true);
expect(member?.userId).toEqual(userLazy);
expect(member?.isOutOfBand()).toEqual(true);
});
it("should have no effect when not in correct status", function() {
@ -394,7 +417,7 @@ describe("RoomState", function() {
user: userLazy, mship: "join", room: roomId, event: true,
});
let eventReceived = false;
state.once('RoomState.newMember', (_, __, member) => {
state.once(RoomStateEvent.NewMember, (_event, _state, member) => {
expect(member.userId).toEqual(userLazy);
eventReceived = true;
});
@ -410,8 +433,8 @@ describe("RoomState", function() {
state.markOutOfBandMembersStarted();
state.setOutOfBandMembers([oobMemberEvent]);
const memberA = state.getMember(userA);
expect(memberA.events.member.getId()).not.toEqual(oobMemberEvent.getId());
expect(memberA.isOutOfBand()).toEqual(false);
expect(memberA?.events?.member?.getId()).not.toEqual(oobMemberEvent.getId());
expect(memberA?.isOutOfBand()).toEqual(false);
});
it("should emit members when updating a member", function() {
@ -420,7 +443,7 @@ describe("RoomState", function() {
user: doesntExistYetUserId, mship: "join", room: roomId, event: true,
});
let eventReceived = false;
state.once('RoomState.members', (_, __, member) => {
state.once(RoomStateEvent.Members, (_event, _state, member) => {
expect(member.userId).toEqual(doesntExistYetUserId);
eventReceived = true;
});
@ -443,8 +466,8 @@ describe("RoomState", function() {
[userA, userB, userLazy].forEach((userId) => {
const member = state.getMember(userId);
const memberCopy = copy.getMember(userId);
expect(member.name).toEqual(memberCopy.name);
expect(member.isOutOfBand()).toEqual(memberCopy.isOutOfBand());
expect(member?.name).toEqual(memberCopy?.name);
expect(member?.isOutOfBand()).toEqual(memberCopy?.isOutOfBand());
});
// check member keys
expect(Object.keys(state.members)).toEqual(Object.keys(copy.members));
@ -496,78 +519,80 @@ describe("RoomState", function() {
describe("maySendStateEvent", function() {
it("should say any member may send state with no power level event",
function() {
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
});
function() {
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
});
it("should say members with power >=50 may send state with power level event " +
"but no state default",
function() {
const powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
const powerLevelEvent = new MatrixEvent({
type: "m.room.power_levels", room_id: roomId, sender: userA,
state_key: "",
content: {
users_default: 10,
// state_default: 50, "intentionally left blank"
events_default: 25,
users: {
[userA]: 50,
},
},
};
powerLevelEvent.content.users[userA] = 50;
});
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
state.setStateEvents([powerLevelEvent]);
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(false);
});
it("should obey state_default",
function() {
const powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
content: {
users_default: 10,
state_default: 30,
events_default: 25,
users: {
function() {
const powerLevelEvent = new MatrixEvent({
type: "m.room.power_levels", room_id: roomId, sender: userA,
state_key: "",
content: {
users_default: 10,
state_default: 30,
events_default: 25,
users: {
[userA]: 30,
[userB]: 29,
},
},
},
};
powerLevelEvent.content.users[userA] = 30;
powerLevelEvent.content.users[userB] = 29;
});
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
state.setStateEvents([powerLevelEvent]);
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(false);
});
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(false);
});
it("should honour explicit event power levels in the power_levels event",
function() {
const powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
content: {
events: {
"m.room.other_thing": 76,
function() {
const powerLevelEvent = new MatrixEvent({
type: "m.room.power_levels", room_id: roomId, sender: userA,
state_key: "", content: {
events: {
"m.room.other_thing": 76,
},
users_default: 10,
state_default: 50,
events_default: 25,
users: {
[userA]: 80,
[userB]: 50,
},
},
users_default: 10,
state_default: 50,
events_default: 25,
users: {
},
},
};
powerLevelEvent.content.users[userA] = 80;
powerLevelEvent.content.users[userB] = 50;
});
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
state.setStateEvents([powerLevelEvent]);
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.name', userB)).toEqual(true);
expect(state.maySendStateEvent('m.room.other_thing', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.other_thing', userB)).toEqual(false);
});
expect(state.maySendStateEvent('m.room.other_thing', userA)).toEqual(true);
expect(state.maySendStateEvent('m.room.other_thing', userB)).toEqual(false);
});
});
describe("getJoinedMemberCount", function() {
@ -682,71 +707,73 @@ describe("RoomState", function() {
describe("maySendEvent", function() {
it("should say any member may send events with no power level event",
function() {
expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
expect(state.maySendMessage(userA)).toEqual(true);
});
function() {
expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
expect(state.maySendMessage(userA)).toEqual(true);
});
it("should obey events_default",
function() {
const powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
content: {
users_default: 10,
state_default: 30,
events_default: 25,
users: {
function() {
const powerLevelEvent = new MatrixEvent({
type: "m.room.power_levels", room_id: roomId, sender: userA,
state_key: "",
content: {
users_default: 10,
state_default: 30,
events_default: 25,
users: {
[userA]: 26,
[userB]: 24,
},
},
},
};
powerLevelEvent.content.users[userA] = 26;
powerLevelEvent.content.users[userB] = 24;
});
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
state.setStateEvents([powerLevelEvent]);
expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
expect(state.maySendEvent('m.room.message', userB)).toEqual(false);
expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
expect(state.maySendEvent('m.room.message', userB)).toEqual(false);
expect(state.maySendMessage(userA)).toEqual(true);
expect(state.maySendMessage(userB)).toEqual(false);
});
expect(state.maySendMessage(userA)).toEqual(true);
expect(state.maySendMessage(userB)).toEqual(false);
});
it("should honour explicit event power levels in the power_levels event",
function() {
const powerLevelEvent = {
type: "m.room.power_levels", room: roomId, user: userA, event: true,
content: {
events: {
"m.room.other_thing": 33,
function() {
const powerLevelEvent = new MatrixEvent({
type: "m.room.power_levels", room_id: roomId, sender: userA,
state_key: "",
content: {
events: {
"m.room.other_thing": 33,
},
users_default: 10,
state_default: 50,
events_default: 25,
users: {
[userA]: 40,
[userB]: 30,
},
},
users_default: 10,
state_default: 50,
events_default: 25,
users: {
},
},
};
powerLevelEvent.content.users[userA] = 40;
powerLevelEvent.content.users[userB] = 30;
});
state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
state.setStateEvents([powerLevelEvent]);
expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
expect(state.maySendEvent('m.room.message', userB)).toEqual(true);
expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
expect(state.maySendEvent('m.room.message', userB)).toEqual(true);
expect(state.maySendMessage(userA)).toEqual(true);
expect(state.maySendMessage(userB)).toEqual(true);
expect(state.maySendMessage(userA)).toEqual(true);
expect(state.maySendMessage(userB)).toEqual(true);
expect(state.maySendEvent('m.room.other_thing', userA)).toEqual(true);
expect(state.maySendEvent('m.room.other_thing', userB)).toEqual(false);
});
expect(state.maySendEvent('m.room.other_thing', userA)).toEqual(true);
expect(state.maySendEvent('m.room.other_thing', userB)).toEqual(false);
});
});
describe('processBeaconEvents', () => {
const beacon1 = makeBeaconInfoEvent(userA, roomId, {}, '$beacon1', '$beacon1');
const beacon2 = makeBeaconInfoEvent(userB, roomId, {}, '$beacon2', '$beacon2');
const beacon1 = makeBeaconInfoEvent(userA, roomId, {}, '$beacon1');
const beacon2 = makeBeaconInfoEvent(userB, roomId, {}, '$beacon2');
const mockClient = { decryptEventIfNeeded: jest.fn() };
const mockClient = { decryptEventIfNeeded: jest.fn() } as unknown as MockedObject<MatrixClient>;
beforeEach(() => {
mockClient.decryptEventIfNeeded.mockClear();
@ -816,11 +843,11 @@ describe("RoomState", function() {
beaconInfoId: 'some-other-beacon',
});
state.setStateEvents([beacon1, beacon2], mockClient);
state.setStateEvents([beacon1, beacon2]);
expect(state.beacons.size).toEqual(2);
const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beacon1));
const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon;
const addLocationsSpy = jest.spyOn(beaconInstance, 'addLocations');
state.processBeaconEvents([location1, location2, location3], mockClient);
@ -885,7 +912,7 @@ describe("RoomState", function() {
});
state.setStateEvents([beacon1, beacon2]);
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1));
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon;
const addLocationsSpy = jest.spyOn(beacon, 'addLocations').mockClear();
state.processBeaconEvents([location, otherRelatedEvent], mockClient);
expect(addLocationsSpy).not.toHaveBeenCalled();
@ -945,13 +972,13 @@ describe("RoomState", function() {
});
jest.spyOn(decryptingRelatedEvent, 'isBeingDecrypted').mockReturnValue(true);
state.setStateEvents([beacon1, beacon2]);
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1));
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon;
const addLocationsSpy = jest.spyOn(beacon, 'addLocations').mockClear();
state.processBeaconEvents([decryptingRelatedEvent], mockClient);
// this event is a message after decryption
decryptingRelatedEvent.type = EventType.RoomMessage;
decryptingRelatedEvent.emit(MatrixEventEvent.Decrypted);
decryptingRelatedEvent.event.type = EventType.RoomMessage;
decryptingRelatedEvent.emit(MatrixEventEvent.Decrypted, decryptingRelatedEvent);
expect(addLocationsSpy).not.toHaveBeenCalled();
});
@ -967,14 +994,14 @@ describe("RoomState", function() {
});
jest.spyOn(decryptingRelatedEvent, 'isBeingDecrypted').mockReturnValue(true);
state.setStateEvents([beacon1, beacon2]);
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1));
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1)) as Beacon;
const addLocationsSpy = jest.spyOn(beacon, 'addLocations').mockClear();
state.processBeaconEvents([decryptingRelatedEvent], mockClient);
// update type after '''decryption'''
decryptingRelatedEvent.event.type = M_BEACON.name;
decryptingRelatedEvent.event.content = locationEvent.content;
decryptingRelatedEvent.emit(MatrixEventEvent.Decrypted);
decryptingRelatedEvent.event.content = locationEvent.event.content;
decryptingRelatedEvent.emit(MatrixEventEvent.Decrypted, decryptingRelatedEvent);
expect(addLocationsSpy).toHaveBeenCalledWith([decryptingRelatedEvent]);
});