You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Two tweaks: * `httpBackend.flush()` now returns a value, so we can't pass its result straight into `done()`. * In one of the megolm tests, we need to wait for the device query to finish before marking the relevant device as known. One easy way to do this is actually to try sending the message first - that will block until the device query completes.
590 lines
22 KiB
JavaScript
590 lines
22 KiB
JavaScript
"use strict";
|
|
import 'source-map-support/register';
|
|
const sdk = require("../..");
|
|
const EventStatus = sdk.EventStatus;
|
|
const HttpBackend = require("../mock-request");
|
|
const utils = require("../test-utils");
|
|
|
|
import expect from 'expect';
|
|
|
|
describe("MatrixClient room timelines", function() {
|
|
const baseUrl = "http://localhost.or.something";
|
|
let client = null;
|
|
let httpBackend = null;
|
|
const userId = "@alice:localhost";
|
|
const userName = "Alice";
|
|
const accessToken = "aseukfgwef";
|
|
const roomId = "!foo:bar";
|
|
const otherUserId = "@bob:localhost";
|
|
const USER_MEMBERSHIP_EVENT = utils.mkMembership({
|
|
room: roomId, mship: "join", user: userId, name: userName,
|
|
});
|
|
const ROOM_NAME_EVENT = utils.mkEvent({
|
|
type: "m.room.name", room: roomId, user: otherUserId,
|
|
content: {
|
|
name: "Old room name",
|
|
},
|
|
});
|
|
let NEXT_SYNC_DATA;
|
|
const SYNC_DATA = {
|
|
next_batch: "s_5_3",
|
|
rooms: {
|
|
join: {
|
|
"!foo:bar": { // roomId
|
|
timeline: {
|
|
events: [
|
|
utils.mkMessage({
|
|
room: roomId, user: otherUserId, msg: "hello",
|
|
}),
|
|
],
|
|
prev_batch: "f_1_1",
|
|
},
|
|
state: {
|
|
events: [
|
|
ROOM_NAME_EVENT,
|
|
utils.mkMembership({
|
|
room: roomId, mship: "join",
|
|
user: otherUserId, name: "Bob",
|
|
}),
|
|
USER_MEMBERSHIP_EVENT,
|
|
utils.mkEvent({
|
|
type: "m.room.create", room: roomId, user: userId,
|
|
content: {
|
|
creator: userId,
|
|
},
|
|
}),
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
function setNextSyncData(events) {
|
|
events = events || [];
|
|
NEXT_SYNC_DATA = {
|
|
next_batch: "n",
|
|
presence: { events: [] },
|
|
rooms: {
|
|
invite: {},
|
|
join: {
|
|
"!foo:bar": {
|
|
timeline: { events: [] },
|
|
state: { events: [] },
|
|
ephemeral: { events: [] },
|
|
},
|
|
},
|
|
leave: {},
|
|
},
|
|
};
|
|
events.forEach(function(e) {
|
|
if (e.room_id !== roomId) {
|
|
throw new Error("setNextSyncData only works with one room id");
|
|
}
|
|
if (e.state_key) {
|
|
if (e.__prev_event === undefined) {
|
|
throw new Error(
|
|
"setNextSyncData needs the prev state set to '__prev_event' " +
|
|
"for " + e.type,
|
|
);
|
|
}
|
|
if (e.__prev_event !== null) {
|
|
// push the previous state for this event type
|
|
NEXT_SYNC_DATA.rooms.join[roomId].state.events.push(e.__prev_event);
|
|
}
|
|
// push the current
|
|
NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e);
|
|
} else if (["m.typing", "m.receipt"].indexOf(e.type) !== -1) {
|
|
NEXT_SYNC_DATA.rooms.join[roomId].ephemeral.events.push(e);
|
|
} else {
|
|
NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
beforeEach(function(done) {
|
|
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
|
httpBackend = new HttpBackend();
|
|
sdk.request(httpBackend.requestFn);
|
|
client = sdk.createClient({
|
|
baseUrl: baseUrl,
|
|
userId: userId,
|
|
accessToken: accessToken,
|
|
// these tests should work with or without timelineSupport
|
|
timelineSupport: true,
|
|
});
|
|
setNextSyncData();
|
|
httpBackend.when("GET", "/pushrules").respond(200, {});
|
|
httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
|
|
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA);
|
|
httpBackend.when("GET", "/sync").respond(200, function() {
|
|
return NEXT_SYNC_DATA;
|
|
});
|
|
client.startClient();
|
|
httpBackend.flush("/pushrules").then(function() {
|
|
return httpBackend.flush("/filter");
|
|
}).nodeify(done);
|
|
});
|
|
|
|
afterEach(function() {
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
client.stopClient();
|
|
});
|
|
|
|
describe("local echo events", function() {
|
|
it("should be added immediately after calling MatrixClient.sendEvent " +
|
|
"with EventStatus.SENDING and the right event.sender", function(done) {
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
expect(room.timeline.length).toEqual(1);
|
|
|
|
client.sendTextMessage(roomId, "I am a fish", "txn1");
|
|
// check it was added
|
|
expect(room.timeline.length).toEqual(2);
|
|
// check status
|
|
expect(room.timeline[1].status).toEqual(EventStatus.SENDING);
|
|
// check member
|
|
const member = room.timeline[1].sender;
|
|
expect(member.userId).toEqual(userId);
|
|
expect(member.name).toEqual(userName);
|
|
|
|
httpBackend.flush("/sync", 1).done(function() {
|
|
done();
|
|
});
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should be updated correctly when the send request finishes " +
|
|
"BEFORE the event comes down the event stream", function(done) {
|
|
const eventId = "$foo:bar";
|
|
httpBackend.when("PUT", "/txn1").respond(200, {
|
|
event_id: eventId,
|
|
});
|
|
|
|
const ev = utils.mkMessage({
|
|
body: "I am a fish", user: userId, room: roomId,
|
|
});
|
|
ev.event_id = eventId;
|
|
ev.unsigned = {transaction_id: "txn1"};
|
|
setNextSyncData([ev]);
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
client.sendTextMessage(roomId, "I am a fish", "txn1").done(
|
|
function() {
|
|
expect(room.timeline[1].getId()).toEqual(eventId);
|
|
httpBackend.flush("/sync", 1).done(function() {
|
|
expect(room.timeline[1].getId()).toEqual(eventId);
|
|
done();
|
|
});
|
|
});
|
|
httpBackend.flush("/txn1", 1);
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should be updated correctly when the send request finishes " +
|
|
"AFTER the event comes down the event stream", function(done) {
|
|
const eventId = "$foo:bar";
|
|
httpBackend.when("PUT", "/txn1").respond(200, {
|
|
event_id: eventId,
|
|
});
|
|
|
|
const ev = utils.mkMessage({
|
|
body: "I am a fish", user: userId, room: roomId,
|
|
});
|
|
ev.event_id = eventId;
|
|
ev.unsigned = {transaction_id: "txn1"};
|
|
setNextSyncData([ev]);
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
const promise = client.sendTextMessage(roomId, "I am a fish", "txn1");
|
|
httpBackend.flush("/sync", 1).done(function() {
|
|
expect(room.timeline.length).toEqual(2);
|
|
httpBackend.flush("/txn1", 1);
|
|
promise.done(function() {
|
|
expect(room.timeline.length).toEqual(2);
|
|
expect(room.timeline[1].getId()).toEqual(eventId);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
});
|
|
|
|
describe("paginated events", function() {
|
|
let sbEvents;
|
|
const sbEndTok = "pagin_end";
|
|
|
|
beforeEach(function() {
|
|
sbEvents = [];
|
|
httpBackend.when("GET", "/messages").respond(200, function() {
|
|
return {
|
|
chunk: sbEvents,
|
|
start: "pagin_start",
|
|
end: sbEndTok,
|
|
};
|
|
});
|
|
});
|
|
|
|
it("should set Room.oldState.paginationToken to null at the start" +
|
|
" of the timeline.", function(done) {
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
expect(room.timeline.length).toEqual(1);
|
|
|
|
client.scrollback(room).done(function() {
|
|
expect(room.timeline.length).toEqual(1);
|
|
expect(room.oldState.paginationToken).toBe(null);
|
|
done();
|
|
});
|
|
|
|
httpBackend.flush("/messages", 1);
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should set the right event.sender values", function(done) {
|
|
// We're aiming for an eventual timeline of:
|
|
//
|
|
// 'Old Alice' joined the room
|
|
// <Old Alice> I'm old alice
|
|
// @alice:localhost changed their name from 'Old Alice' to 'Alice'
|
|
// <Alice> I'm alice
|
|
// ------^ /messages results above this point, /sync result below
|
|
// <Bob> hello
|
|
|
|
// make an m.room.member event for alice's join
|
|
const joinMshipEvent = utils.mkMembership({
|
|
mship: "join", user: userId, room: roomId, name: "Old Alice",
|
|
url: null,
|
|
});
|
|
|
|
// make an m.room.member event with prev_content for alice's nick
|
|
// change
|
|
const oldMshipEvent = utils.mkMembership({
|
|
mship: "join", user: userId, room: roomId, name: userName,
|
|
url: "mxc://some/url",
|
|
});
|
|
oldMshipEvent.prev_content = {
|
|
displayname: "Old Alice",
|
|
avatar_url: null,
|
|
membership: "join",
|
|
};
|
|
|
|
// set the list of events to return on scrollback (/messages)
|
|
// N.B. synapse returns /messages in reverse chronological order
|
|
sbEvents = [
|
|
utils.mkMessage({
|
|
user: userId, room: roomId, msg: "I'm alice",
|
|
}),
|
|
oldMshipEvent,
|
|
utils.mkMessage({
|
|
user: userId, room: roomId, msg: "I'm old alice",
|
|
}),
|
|
joinMshipEvent,
|
|
];
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
// sync response
|
|
expect(room.timeline.length).toEqual(1);
|
|
|
|
client.scrollback(room).done(function() {
|
|
expect(room.timeline.length).toEqual(5);
|
|
const joinMsg = room.timeline[0];
|
|
expect(joinMsg.sender.name).toEqual("Old Alice");
|
|
const oldMsg = room.timeline[1];
|
|
expect(oldMsg.sender.name).toEqual("Old Alice");
|
|
const newMsg = room.timeline[3];
|
|
expect(newMsg.sender.name).toEqual(userName);
|
|
done();
|
|
});
|
|
|
|
httpBackend.flush("/messages", 1);
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should add it them to the right place in the timeline", function(done) {
|
|
// set the list of events to return on scrollback
|
|
sbEvents = [
|
|
utils.mkMessage({
|
|
user: userId, room: roomId, msg: "I am new",
|
|
}),
|
|
utils.mkMessage({
|
|
user: userId, room: roomId, msg: "I am old",
|
|
}),
|
|
];
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
expect(room.timeline.length).toEqual(1);
|
|
|
|
client.scrollback(room).done(function() {
|
|
expect(room.timeline.length).toEqual(3);
|
|
expect(room.timeline[0].event).toEqual(sbEvents[1]);
|
|
expect(room.timeline[1].event).toEqual(sbEvents[0]);
|
|
done();
|
|
});
|
|
|
|
httpBackend.flush("/messages", 1);
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should use 'end' as the next pagination token", function(done) {
|
|
// set the list of events to return on scrollback
|
|
sbEvents = [
|
|
utils.mkMessage({
|
|
user: userId, room: roomId, msg: "I am new",
|
|
}),
|
|
];
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
expect(room.oldState.paginationToken).toBeTruthy();
|
|
|
|
client.scrollback(room, 1).done(function() {
|
|
expect(room.oldState.paginationToken).toEqual(sbEndTok);
|
|
});
|
|
|
|
httpBackend.flush("/sync", 1);
|
|
httpBackend.flush("/messages", 1).done(function() {
|
|
done();
|
|
});
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
});
|
|
|
|
describe("new events", function() {
|
|
it("should be added to the right place in the timeline", function(done) {
|
|
const eventData = [
|
|
utils.mkMessage({user: userId, room: roomId}),
|
|
utils.mkMessage({user: userId, room: roomId}),
|
|
];
|
|
setNextSyncData(eventData);
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
|
|
let index = 0;
|
|
client.on("Room.timeline", function(event, rm, toStart) {
|
|
expect(toStart).toBe(false);
|
|
expect(rm).toEqual(room);
|
|
expect(event.event).toEqual(eventData[index]);
|
|
index += 1;
|
|
});
|
|
|
|
httpBackend.flush("/messages", 1);
|
|
httpBackend.flush("/sync", 1).then(function() {
|
|
expect(index).toEqual(2);
|
|
expect(room.timeline.length).toEqual(3);
|
|
expect(room.timeline[2].event).toEqual(
|
|
eventData[1],
|
|
);
|
|
expect(room.timeline[1].event).toEqual(
|
|
eventData[0],
|
|
);
|
|
}).catch(utils.failTest).done(done);
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should set the right event.sender values", function(done) {
|
|
const eventData = [
|
|
utils.mkMessage({user: userId, room: roomId}),
|
|
utils.mkMembership({
|
|
user: userId, room: roomId, mship: "join", name: "New Name",
|
|
}),
|
|
utils.mkMessage({user: userId, room: roomId}),
|
|
];
|
|
eventData[1].__prev_event = USER_MEMBERSHIP_EVENT;
|
|
setNextSyncData(eventData);
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
httpBackend.flush("/sync", 1).then(function() {
|
|
const preNameEvent = room.timeline[room.timeline.length - 3];
|
|
const postNameEvent = room.timeline[room.timeline.length - 1];
|
|
expect(preNameEvent.sender.name).toEqual(userName);
|
|
expect(postNameEvent.sender.name).toEqual("New Name");
|
|
}).catch(utils.failTest).done(done);
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should set the right room.name", function(done) {
|
|
const secondRoomNameEvent = utils.mkEvent({
|
|
user: userId, room: roomId, type: "m.room.name", content: {
|
|
name: "Room 2",
|
|
},
|
|
});
|
|
secondRoomNameEvent.__prev_event = ROOM_NAME_EVENT;
|
|
setNextSyncData([secondRoomNameEvent]);
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
let nameEmitCount = 0;
|
|
client.on("Room.name", function(rm) {
|
|
nameEmitCount += 1;
|
|
});
|
|
|
|
httpBackend.flush("/sync", 1).done(function() {
|
|
expect(nameEmitCount).toEqual(1);
|
|
expect(room.name).toEqual("Room 2");
|
|
// do another round
|
|
const thirdRoomNameEvent = utils.mkEvent({
|
|
user: userId, room: roomId, type: "m.room.name", content: {
|
|
name: "Room 3",
|
|
},
|
|
});
|
|
thirdRoomNameEvent.__prev_event = secondRoomNameEvent;
|
|
setNextSyncData([thirdRoomNameEvent]);
|
|
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
|
|
httpBackend.flush("/sync", 1).done(function() {
|
|
expect(nameEmitCount).toEqual(2);
|
|
expect(room.name).toEqual("Room 3");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should set the right room members", function(done) {
|
|
const userC = "@cee:bar";
|
|
const userD = "@dee:bar";
|
|
const eventData = [
|
|
utils.mkMembership({
|
|
user: userC, room: roomId, mship: "join", name: "C",
|
|
}),
|
|
utils.mkMembership({
|
|
user: userC, room: roomId, mship: "invite", skey: userD,
|
|
}),
|
|
];
|
|
eventData[0].__prev_event = null;
|
|
eventData[1].__prev_event = null;
|
|
setNextSyncData(eventData);
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
httpBackend.flush("/sync", 1).then(function() {
|
|
expect(room.currentState.getMembers().length).toEqual(4);
|
|
expect(room.currentState.getMember(userC).name).toEqual("C");
|
|
expect(room.currentState.getMember(userC).membership).toEqual(
|
|
"join",
|
|
);
|
|
expect(room.currentState.getMember(userD).name).toEqual(userD);
|
|
expect(room.currentState.getMember(userD).membership).toEqual(
|
|
"invite",
|
|
);
|
|
}).catch(utils.failTest).done(done);
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
});
|
|
|
|
describe("gappy sync", function() {
|
|
it("should copy the last known state to the new timeline", function(done) {
|
|
const eventData = [
|
|
utils.mkMessage({user: userId, room: roomId}),
|
|
];
|
|
setNextSyncData(eventData);
|
|
NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true;
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
|
|
httpBackend.flush("/messages", 1);
|
|
httpBackend.flush("/sync", 1).done(function() {
|
|
expect(room.timeline.length).toEqual(1);
|
|
expect(room.timeline[0].event).toEqual(eventData[0]);
|
|
expect(room.currentState.getMembers().length).toEqual(2);
|
|
expect(room.currentState.getMember(userId).name).toEqual(userName);
|
|
expect(room.currentState.getMember(userId).membership).toEqual(
|
|
"join",
|
|
);
|
|
expect(room.currentState.getMember(otherUserId).name).toEqual("Bob");
|
|
expect(room.currentState.getMember(otherUserId).membership).toEqual(
|
|
"join",
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
|
|
it("should emit a 'Room.timelineReset' event", function(done) {
|
|
const eventData = [
|
|
utils.mkMessage({user: userId, room: roomId}),
|
|
];
|
|
setNextSyncData(eventData);
|
|
NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true;
|
|
|
|
client.on("sync", function(state) {
|
|
if (state !== "PREPARED") {
|
|
return;
|
|
}
|
|
const room = client.getRoom(roomId);
|
|
|
|
let emitCount = 0;
|
|
client.on("Room.timelineReset", function(emitRoom) {
|
|
expect(emitRoom).toEqual(room);
|
|
emitCount++;
|
|
});
|
|
|
|
httpBackend.flush("/messages", 1);
|
|
httpBackend.flush("/sync", 1).done(function() {
|
|
expect(emitCount).toEqual(1);
|
|
done();
|
|
});
|
|
});
|
|
httpBackend.flush("/sync", 1);
|
|
});
|
|
});
|
|
});
|