You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-07 23:02:56 +03:00
test typescriptification - timeline-window, scheduler, etc (#2539)
* spec/unit/user.spec.js -> spec/unit/user.spec.ts * fix ts in user.spec * renamed: spec/unit/timeline-window.spec.js -> spec/unit/timeline-window.spec.ts * overdo it fixing types in timeline-window.spec * renamed spec/unit/sync-accumulator.spec.js spec/unit/sync-accumulator.spec.ts * fix ts in sync-accumalator.spec * spec/unit/scheduler.spec.js -> spec/unit/scheduler.spec.ts * fix ts in scheduler.spec * missed types in timeline-window spec
This commit is contained in:
@@ -45,8 +45,8 @@ describe("MatrixScheduler", function() {
|
||||
queueFn = function() {
|
||||
return "one_big_queue";
|
||||
};
|
||||
const deferA = defer();
|
||||
const deferB = defer();
|
||||
const deferA = defer<Record<string, boolean>>();
|
||||
const deferB = defer<Record<string, boolean>>();
|
||||
let yieldedA = false;
|
||||
scheduler.setProcessFunction(function(event) {
|
||||
if (yieldedA) {
|
||||
@@ -70,84 +70,84 @@ describe("MatrixScheduler", function() {
|
||||
});
|
||||
|
||||
it("should invoke the retryFn on failure and wait the amount of time specified",
|
||||
async function() {
|
||||
const waitTimeMs = 1500;
|
||||
const retryDefer = defer();
|
||||
retryFn = function() {
|
||||
retryDefer.resolve();
|
||||
return waitTimeMs;
|
||||
};
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
async function() {
|
||||
const waitTimeMs = 1500;
|
||||
const retryDefer = defer();
|
||||
retryFn = function() {
|
||||
retryDefer.resolve();
|
||||
return waitTimeMs;
|
||||
};
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
|
||||
let procCount = 0;
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
if (procCount === 1) {
|
||||
expect(ev).toEqual(eventA);
|
||||
return deferred.promise;
|
||||
} else if (procCount === 2) {
|
||||
let procCount = 0;
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
if (procCount === 1) {
|
||||
expect(ev).toEqual(eventA);
|
||||
return deferred.promise;
|
||||
} else if (procCount === 2) {
|
||||
// don't care about this deferred
|
||||
return new Promise();
|
||||
}
|
||||
expect(procCount).toBeLessThan(3);
|
||||
return new Promise(() => {});
|
||||
}
|
||||
expect(procCount).toBeLessThan(3);
|
||||
});
|
||||
|
||||
scheduler.queueEvent(eventA);
|
||||
// as queueing doesn't start processing synchronously anymore (see commit bbdb5ac)
|
||||
// wait just long enough before it does
|
||||
await Promise.resolve();
|
||||
expect(procCount).toEqual(1);
|
||||
deferred.reject({});
|
||||
await retryDefer.promise;
|
||||
expect(procCount).toEqual(1);
|
||||
jest.advanceTimersByTime(waitTimeMs);
|
||||
await Promise.resolve();
|
||||
expect(procCount).toEqual(2);
|
||||
});
|
||||
|
||||
scheduler.queueEvent(eventA);
|
||||
// as queueing doesn't start processing synchronously anymore (see commit bbdb5ac)
|
||||
// wait just long enough before it does
|
||||
await Promise.resolve();
|
||||
expect(procCount).toEqual(1);
|
||||
deferred.reject({});
|
||||
await retryDefer.promise;
|
||||
expect(procCount).toEqual(1);
|
||||
jest.advanceTimersByTime(waitTimeMs);
|
||||
await Promise.resolve();
|
||||
expect(procCount).toEqual(2);
|
||||
});
|
||||
|
||||
it("should give up if the retryFn on failure returns -1 and try the next event",
|
||||
async function() {
|
||||
async function() {
|
||||
// Queue A & B.
|
||||
// Reject A and return -1 on retry.
|
||||
// Expect B to be tried next and the promise for A to be rejected.
|
||||
retryFn = function() {
|
||||
return -1;
|
||||
};
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
retryFn = function() {
|
||||
return -1;
|
||||
};
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
|
||||
const deferA = defer();
|
||||
const deferB = defer();
|
||||
let procCount = 0;
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
if (procCount === 1) {
|
||||
expect(ev).toEqual(eventA);
|
||||
return deferA.promise;
|
||||
} else if (procCount === 2) {
|
||||
expect(ev).toEqual(eventB);
|
||||
return deferB.promise;
|
||||
}
|
||||
expect(procCount).toBeLessThan(3);
|
||||
});
|
||||
const deferA = defer();
|
||||
const deferB = defer();
|
||||
let procCount = 0;
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
if (procCount === 1) {
|
||||
expect(ev).toEqual(eventA);
|
||||
return deferA.promise;
|
||||
} else if (procCount === 2) {
|
||||
expect(ev).toEqual(eventB);
|
||||
return deferB.promise;
|
||||
}
|
||||
expect(procCount).toBeLessThan(3);
|
||||
});
|
||||
|
||||
const globalA = scheduler.queueEvent(eventA);
|
||||
scheduler.queueEvent(eventB);
|
||||
// as queueing doesn't start processing synchronously anymore (see commit bbdb5ac)
|
||||
// wait just long enough before it does
|
||||
await Promise.resolve();
|
||||
expect(procCount).toEqual(1);
|
||||
deferA.reject({});
|
||||
try {
|
||||
await globalA;
|
||||
} catch (err) {
|
||||
const globalA = scheduler.queueEvent(eventA);
|
||||
scheduler.queueEvent(eventB);
|
||||
// as queueing doesn't start processing synchronously anymore (see commit bbdb5ac)
|
||||
// wait just long enough before it does
|
||||
await Promise.resolve();
|
||||
expect(procCount).toEqual(2);
|
||||
}
|
||||
});
|
||||
expect(procCount).toEqual(1);
|
||||
deferA.reject({});
|
||||
try {
|
||||
await globalA;
|
||||
} catch (err) {
|
||||
await Promise.resolve();
|
||||
expect(procCount).toEqual(2);
|
||||
}
|
||||
});
|
||||
|
||||
it("should treat each queue separately", function(done) {
|
||||
// Queue messages A B C D.
|
||||
@@ -175,7 +175,7 @@ describe("MatrixScheduler", function() {
|
||||
const expectOrder = [
|
||||
eventA.getId(), eventB.getId(), eventD.getId(),
|
||||
];
|
||||
const deferA = defer();
|
||||
const deferA = defer<void>();
|
||||
scheduler.setProcessFunction(function(event) {
|
||||
const id = expectOrder.shift();
|
||||
expect(id).toEqual(event.getId());
|
||||
@@ -191,7 +191,7 @@ describe("MatrixScheduler", function() {
|
||||
|
||||
// wait a bit then resolve A and we should get D (not C) next.
|
||||
setTimeout(function() {
|
||||
deferA.resolve({});
|
||||
deferA.resolve();
|
||||
}, 1000);
|
||||
jest.advanceTimersByTime(1000);
|
||||
});
|
||||
@@ -336,28 +336,29 @@ describe("MatrixScheduler", function() {
|
||||
errcode: "M_LIMIT_EXCEEDED", retry_after_ms: 5000,
|
||||
}),
|
||||
);
|
||||
expect(res >= 500).toBe(true, "Didn't wait long enough.");
|
||||
expect(res >= 500).toBe(true);
|
||||
});
|
||||
|
||||
it("should give up after 5 attempts", function() {
|
||||
const res = MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 5, {},
|
||||
eventA, 5, new MatrixError({}),
|
||||
);
|
||||
expect(res).toBe(-1, "Didn't give up.");
|
||||
expect(res).toBe(-1);
|
||||
});
|
||||
|
||||
it("should do exponential backoff", function() {
|
||||
const error = new MatrixError({});
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 1, {},
|
||||
eventA, 1, error,
|
||||
)).toEqual(2000);
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 2, {},
|
||||
eventA, 2, error,
|
||||
)).toEqual(4000);
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 3, {},
|
||||
eventA, 3, error,
|
||||
)).toEqual(8000);
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 4, {},
|
||||
eventA, 4, error,
|
||||
)).toEqual(16000);
|
||||
});
|
||||
});
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Copyright 2017 Vector Creations 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.
|
||||
@@ -384,6 +384,10 @@ describe("SyncAccumulator", function() {
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
jest.spyOn(global.Date, 'now').mockRestore();
|
||||
});
|
||||
|
||||
it("should copy summary properties", function() {
|
||||
sa.accumulate(createSyncResponseWithSummary({
|
||||
"m.heroes": ["@alice:bar"],
|
||||
@@ -413,25 +417,19 @@ describe("SyncAccumulator", function() {
|
||||
const delta = 1000;
|
||||
const startingTs = 1000;
|
||||
|
||||
const oldDateNow = Date.now;
|
||||
try {
|
||||
Date.now = jest.fn();
|
||||
Date.now.mockReturnValue(startingTs);
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(startingTs);
|
||||
|
||||
sa.accumulate(RES_WITH_AGE);
|
||||
sa.accumulate(RES_WITH_AGE);
|
||||
|
||||
Date.now.mockReturnValue(startingTs + delta);
|
||||
jest.spyOn(global.Date, 'now').mockReturnValue(startingTs + delta);
|
||||
|
||||
const output = sa.getJSON();
|
||||
expect(output.roomsData.join["!foo:bar"].timeline.events[0].unsigned.age).toEqual(
|
||||
RES_WITH_AGE.rooms.join["!foo:bar"].timeline.events[0].unsigned.age + delta,
|
||||
);
|
||||
expect(Object.keys(output.roomsData.join["!foo:bar"].timeline.events[0])).toEqual(
|
||||
Object.keys(RES_WITH_AGE.rooms.join["!foo:bar"].timeline.events[0]),
|
||||
);
|
||||
} finally {
|
||||
Date.now = oldDateNow;
|
||||
}
|
||||
const output = sa.getJSON();
|
||||
expect(output.roomsData.join["!foo:bar"].timeline.events[0].unsigned.age).toEqual(
|
||||
RES_WITH_AGE.rooms.join["!foo:bar"].timeline.events[0].unsigned.age + delta,
|
||||
);
|
||||
expect(Object.keys(output.roomsData.join["!foo:bar"].timeline.events[0])).toEqual(
|
||||
Object.keys(RES_WITH_AGE.rooms.join["!foo:bar"].timeline.events[0]),
|
||||
);
|
||||
});
|
||||
|
||||
it("should mangle age without adding extra keys", () => {
|
@@ -1,456 +0,0 @@
|
||||
import { EventTimeline } from "../../src/models/event-timeline";
|
||||
import { TimelineIndex, TimelineWindow } from "../../src/timeline-window";
|
||||
import * as utils from "../test-utils/test-utils";
|
||||
|
||||
const ROOM_ID = "roomId";
|
||||
const USER_ID = "userId";
|
||||
|
||||
/*
|
||||
* create a timeline with a bunch (default 3) events.
|
||||
* baseIndex is 1 by default.
|
||||
*/
|
||||
function createTimeline(numEvents, baseIndex) {
|
||||
if (numEvents === undefined) {
|
||||
numEvents = 3;
|
||||
}
|
||||
if (baseIndex === undefined) {
|
||||
baseIndex = 1;
|
||||
}
|
||||
|
||||
// XXX: this is a horrid hack
|
||||
const timelineSet = { room: { roomId: ROOM_ID } };
|
||||
timelineSet.room.getUnfilteredTimelineSet = function() {
|
||||
return timelineSet;
|
||||
};
|
||||
|
||||
const timeline = new EventTimeline(timelineSet);
|
||||
|
||||
// add the events after the baseIndex first
|
||||
addEventsToTimeline(timeline, numEvents - baseIndex, false);
|
||||
|
||||
// then add those before the baseIndex
|
||||
addEventsToTimeline(timeline, baseIndex, true);
|
||||
|
||||
expect(timeline.getBaseIndex()).toEqual(baseIndex);
|
||||
return timeline;
|
||||
}
|
||||
|
||||
function addEventsToTimeline(timeline, numEvents, toStartOfTimeline) {
|
||||
for (let i = 0; i < numEvents; i++) {
|
||||
timeline.addEvent(
|
||||
utils.mkMessage({
|
||||
room: ROOM_ID, user: USER_ID,
|
||||
event: true,
|
||||
}),
|
||||
{ toStartOfTimeline },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* create a pair of linked timelines
|
||||
*/
|
||||
function createLinkedTimelines() {
|
||||
const tl1 = createTimeline();
|
||||
const tl2 = createTimeline();
|
||||
tl1.setNeighbouringTimeline(tl2, EventTimeline.FORWARDS);
|
||||
tl2.setNeighbouringTimeline(tl1, EventTimeline.BACKWARDS);
|
||||
return [tl1, tl2];
|
||||
}
|
||||
|
||||
describe("TimelineIndex", function() {
|
||||
describe("minIndex", function() {
|
||||
it("should return the min index relative to BaseIndex", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
expect(timelineIndex.minIndex()).toEqual(-1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("maxIndex", function() {
|
||||
it("should return the max index relative to BaseIndex", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
expect(timelineIndex.maxIndex()).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("advance", function() {
|
||||
it("should advance up to the end of the timeline", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.advance(3);
|
||||
expect(result).toEqual(2);
|
||||
expect(timelineIndex.index).toEqual(2);
|
||||
});
|
||||
|
||||
it("should retreat back to the start of the timeline", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.advance(-2);
|
||||
expect(result).toEqual(-1);
|
||||
expect(timelineIndex.index).toEqual(-1);
|
||||
});
|
||||
|
||||
it("should advance into the next timeline", function() {
|
||||
const timelines = createLinkedTimelines();
|
||||
const tl1 = timelines[0];
|
||||
const tl2 = timelines[1];
|
||||
|
||||
// initialise the index pointing at the end of the first timeline
|
||||
const timelineIndex = new TimelineIndex(tl1, 2);
|
||||
|
||||
const result = timelineIndex.advance(1);
|
||||
expect(result).toEqual(1);
|
||||
expect(timelineIndex.timeline).toBe(tl2);
|
||||
|
||||
// we expect the index to be the zero (ie, the same as the
|
||||
// BaseIndex), because the BaseIndex points at the second event,
|
||||
// and we've advanced past the first.
|
||||
expect(timelineIndex.index).toEqual(0);
|
||||
});
|
||||
|
||||
it("should retreat into the previous timeline", function() {
|
||||
const timelines = createLinkedTimelines();
|
||||
const tl1 = timelines[0];
|
||||
const tl2 = timelines[1];
|
||||
|
||||
// initialise the index pointing at the start of the second
|
||||
// timeline
|
||||
const timelineIndex = new TimelineIndex(tl2, -1);
|
||||
|
||||
const result = timelineIndex.advance(-1);
|
||||
expect(result).toEqual(-1);
|
||||
expect(timelineIndex.timeline).toBe(tl1);
|
||||
expect(timelineIndex.index).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("retreat", function() {
|
||||
it("should retreat up to the start of the timeline", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.retreat(2);
|
||||
expect(result).toEqual(1);
|
||||
expect(timelineIndex.index).toEqual(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("TimelineWindow", function() {
|
||||
/**
|
||||
* create a dummy eventTimelineSet and client, and a TimelineWindow
|
||||
* attached to them.
|
||||
*/
|
||||
let timelineSet;
|
||||
let client;
|
||||
function createWindow(timeline, opts) {
|
||||
timelineSet = { getTimelineForEvent: () => null };
|
||||
client = {};
|
||||
client.getEventTimeline = function(timelineSet0, eventId0) {
|
||||
expect(timelineSet0).toBe(timelineSet);
|
||||
return Promise.resolve(timeline);
|
||||
};
|
||||
|
||||
return new TimelineWindow(client, timelineSet, opts);
|
||||
}
|
||||
|
||||
describe("load", function() {
|
||||
it("should initialise from the live timeline", function() {
|
||||
const liveTimeline = createTimeline();
|
||||
const room = {};
|
||||
room.getLiveTimeline = function() {
|
||||
return liveTimeline;
|
||||
};
|
||||
|
||||
const timelineWindow = new TimelineWindow(undefined, room);
|
||||
return timelineWindow.load(undefined, 2).then(function() {
|
||||
const expectedEvents = liveTimeline.getEvents().slice(1);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
});
|
||||
});
|
||||
|
||||
it("should initialise from a specific event", function() {
|
||||
const timeline = createTimeline();
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
const timelineSet = { getTimelineForEvent: () => null };
|
||||
const client = {};
|
||||
client.getEventTimeline = function(timelineSet0, eventId0) {
|
||||
expect(timelineSet0).toBe(timelineSet);
|
||||
expect(eventId0).toEqual(eventId);
|
||||
return Promise.resolve(timeline);
|
||||
};
|
||||
|
||||
const timelineWindow = new TimelineWindow(client, timelineSet);
|
||||
return timelineWindow.load(eventId, 3).then(function() {
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
});
|
||||
});
|
||||
|
||||
it("canPaginate should return false until load has returned", function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok1", EventTimeline.BACKWARDS);
|
||||
timeline.setPaginationToken("toktok2", EventTimeline.FORWARDS);
|
||||
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
const timelineSet = { getTimelineForEvent: () => null };
|
||||
const client = {};
|
||||
|
||||
const timelineWindow = new TimelineWindow(client, timelineSet);
|
||||
|
||||
client.getEventTimeline = function(timelineSet0, eventId0) {
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
return Promise.resolve(timeline);
|
||||
};
|
||||
|
||||
return timelineWindow.load(eventId, 3).then(function() {
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("pagination", function() {
|
||||
it("should be able to advance across the initial timeline", function() {
|
||||
const timeline = createTimeline();
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(timeline);
|
||||
|
||||
return timelineWindow.load(eventId, 1).then(function() {
|
||||
const expectedEvents = [timeline.getEvents()[1]];
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
const expectedEvents = timeline.getEvents().slice(1);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(false);
|
||||
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("should advance into next timeline", function() {
|
||||
const tls = createLinkedTimelines();
|
||||
const eventId = tls[0].getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(tls[0], { windowLimit: 5 });
|
||||
|
||||
return timelineWindow.load(eventId, 3).then(function() {
|
||||
const expectedEvents = tls[0].getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
const expectedEvents = tls[0].getEvents()
|
||||
.concat(tls[1].getEvents().slice(0, 2));
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
// the windowLimit should have made us drop an event from
|
||||
// tls[0]
|
||||
const expectedEvents = tls[0].getEvents().slice(1)
|
||||
.concat(tls[1].getEvents());
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("should retreat into previous timeline", function() {
|
||||
const tls = createLinkedTimelines();
|
||||
const eventId = tls[1].getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(tls[1], { windowLimit: 5 });
|
||||
|
||||
return timelineWindow.load(eventId, 3).then(function() {
|
||||
const expectedEvents = tls[1].getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
const expectedEvents = tls[0].getEvents().slice(1, 3)
|
||||
.concat(tls[1].getEvents());
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
// the windowLimit should have made us drop an event from
|
||||
// tls[1]
|
||||
const expectedEvents = tls[0].getEvents()
|
||||
.concat(tls[1].getEvents().slice(0, 2));
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("should make forward pagination requests", function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.FORWARDS);
|
||||
|
||||
const timelineWindow = createWindow(timeline, { windowLimit: 5 });
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
client.paginateEventTimeline = function(timeline0, opts) {
|
||||
expect(timeline0).toBe(timeline);
|
||||
expect(opts.backwards).toBe(false);
|
||||
expect(opts.limit).toEqual(2);
|
||||
|
||||
addEventsToTimeline(timeline, 3, false);
|
||||
return Promise.resolve(true);
|
||||
};
|
||||
|
||||
return timelineWindow.load(eventId, 3).then(function() {
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
const expectedEvents = timeline.getEvents().slice(0, 5);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
});
|
||||
});
|
||||
|
||||
it("should make backward pagination requests", function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.BACKWARDS);
|
||||
|
||||
const timelineWindow = createWindow(timeline, { windowLimit: 5 });
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
client.paginateEventTimeline = function(timeline0, opts) {
|
||||
expect(timeline0).toBe(timeline);
|
||||
expect(opts.backwards).toBe(true);
|
||||
expect(opts.limit).toEqual(2);
|
||||
|
||||
addEventsToTimeline(timeline, 3, true);
|
||||
return Promise.resolve(true);
|
||||
};
|
||||
|
||||
return timelineWindow.load(eventId, 3).then(function() {
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
const expectedEvents = timeline.getEvents().slice(1, 6);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
});
|
||||
});
|
||||
|
||||
it("should limit the number of unsuccessful pagination requests", function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.FORWARDS);
|
||||
|
||||
const timelineWindow = createWindow(timeline, { windowLimit: 5 });
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
let paginateCount = 0;
|
||||
client.paginateEventTimeline = function(timeline0, opts) {
|
||||
expect(timeline0).toBe(timeline);
|
||||
expect(opts.backwards).toBe(false);
|
||||
expect(opts.limit).toEqual(2);
|
||||
paginateCount += 1;
|
||||
return Promise.resolve(true);
|
||||
};
|
||||
|
||||
return timelineWindow.load(eventId, 3).then(function() {
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2, true, 3);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(false);
|
||||
expect(paginateCount).toEqual(3);
|
||||
const expectedEvents = timeline.getEvents().slice(0, 3);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
439
spec/unit/timeline-window.spec.ts
Normal file
439
spec/unit/timeline-window.spec.ts
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
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 { MatrixClient } from "../../src/client";
|
||||
import { EventTimelineSet } from "../../src/models/event-timeline-set";
|
||||
import { Room } from "../../src/models/room";
|
||||
import { EventTimeline } from "../../src/models/event-timeline";
|
||||
import { TimelineIndex, TimelineWindow } from "../../src/timeline-window";
|
||||
import { mkMessage } from "../test-utils/test-utils";
|
||||
|
||||
const ROOM_ID = "roomId";
|
||||
const USER_ID = "userId";
|
||||
const mockClient = {
|
||||
getEventTimeline: jest.fn(),
|
||||
paginateEventTimeline: jest.fn(),
|
||||
} as unknown as MockedObject<MatrixClient>;
|
||||
|
||||
/*
|
||||
* create a timeline with a bunch (default 3) events.
|
||||
* baseIndex is 1 by default.
|
||||
*/
|
||||
function createTimeline(numEvents = 3, baseIndex = 1): EventTimeline {
|
||||
const room = new Room(ROOM_ID, mockClient, USER_ID);
|
||||
const timelineSet = new EventTimelineSet(room);
|
||||
jest.spyOn(timelineSet.room, 'getUnfilteredTimelineSet').mockReturnValue(timelineSet);
|
||||
|
||||
const timeline = new EventTimeline(timelineSet);
|
||||
|
||||
// add the events after the baseIndex first
|
||||
addEventsToTimeline(timeline, numEvents - baseIndex, false);
|
||||
|
||||
// then add those before the baseIndex
|
||||
addEventsToTimeline(timeline, baseIndex, true);
|
||||
|
||||
expect(timeline.getBaseIndex()).toEqual(baseIndex);
|
||||
return timeline;
|
||||
}
|
||||
|
||||
function addEventsToTimeline(timeline: EventTimeline, numEvents: number, toStartOfTimeline: boolean) {
|
||||
for (let i = 0; i < numEvents; i++) {
|
||||
timeline.addEvent(
|
||||
mkMessage({
|
||||
room: ROOM_ID, user: USER_ID,
|
||||
event: true,
|
||||
}),
|
||||
{ toStartOfTimeline },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* create a pair of linked timelines
|
||||
*/
|
||||
function createLinkedTimelines(): [EventTimeline, EventTimeline] {
|
||||
const tl1 = createTimeline();
|
||||
const tl2 = createTimeline();
|
||||
tl1.setNeighbouringTimeline(tl2, EventTimeline.FORWARDS);
|
||||
tl2.setNeighbouringTimeline(tl1, EventTimeline.BACKWARDS);
|
||||
return [tl1, tl2];
|
||||
}
|
||||
|
||||
describe("TimelineIndex", function() {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockClient.getEventTimeline.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
describe("minIndex", function() {
|
||||
it("should return the min index relative to BaseIndex", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
expect(timelineIndex.minIndex()).toEqual(-1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("maxIndex", function() {
|
||||
it("should return the max index relative to BaseIndex", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
expect(timelineIndex.maxIndex()).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("advance", function() {
|
||||
it("should advance up to the end of the timeline", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.advance(3);
|
||||
expect(result).toEqual(2);
|
||||
expect(timelineIndex.index).toEqual(2);
|
||||
});
|
||||
|
||||
it("should retreat back to the start of the timeline", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.advance(-2);
|
||||
expect(result).toEqual(-1);
|
||||
expect(timelineIndex.index).toEqual(-1);
|
||||
});
|
||||
|
||||
it("should advance into the next timeline", function() {
|
||||
const timelines = createLinkedTimelines();
|
||||
const tl1 = timelines[0];
|
||||
const tl2 = timelines[1];
|
||||
|
||||
// initialise the index pointing at the end of the first timeline
|
||||
const timelineIndex = new TimelineIndex(tl1, 2);
|
||||
|
||||
const result = timelineIndex.advance(1);
|
||||
expect(result).toEqual(1);
|
||||
expect(timelineIndex.timeline).toBe(tl2);
|
||||
|
||||
// we expect the index to be the zero (ie, the same as the
|
||||
// BaseIndex), because the BaseIndex points at the second event,
|
||||
// and we've advanced past the first.
|
||||
expect(timelineIndex.index).toEqual(0);
|
||||
});
|
||||
|
||||
it("should retreat into the previous timeline", function() {
|
||||
const timelines = createLinkedTimelines();
|
||||
const tl1 = timelines[0];
|
||||
const tl2 = timelines[1];
|
||||
|
||||
// initialise the index pointing at the start of the second
|
||||
// timeline
|
||||
const timelineIndex = new TimelineIndex(tl2, -1);
|
||||
|
||||
const result = timelineIndex.advance(-1);
|
||||
expect(result).toEqual(-1);
|
||||
expect(timelineIndex.timeline).toBe(tl1);
|
||||
expect(timelineIndex.index).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("retreat", function() {
|
||||
it("should retreat up to the start of the timeline", function() {
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.retreat(2);
|
||||
expect(result).toEqual(1);
|
||||
expect(timelineIndex.index).toEqual(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("TimelineWindow", function() {
|
||||
/**
|
||||
* create a dummy eventTimelineSet and client, and a TimelineWindow
|
||||
* attached to them.
|
||||
*/
|
||||
function createWindow(timeline: EventTimeline, opts?: {
|
||||
windowLimit?: number;
|
||||
}): [TimelineWindow, EventTimelineSet] {
|
||||
const timelineSet = { getTimelineForEvent: () => null } as unknown as EventTimelineSet;
|
||||
mockClient.getEventTimeline.mockResolvedValue(timeline);
|
||||
|
||||
return [new TimelineWindow(mockClient, timelineSet, opts), timelineSet];
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockClient.getEventTimeline.mockResolvedValue(undefined);
|
||||
mockClient.paginateEventTimeline.mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
describe("load", function() {
|
||||
it("should initialise from the live timeline", async function() {
|
||||
const liveTimeline = createTimeline();
|
||||
const room = new Room(ROOM_ID, mockClient, USER_ID);
|
||||
const timelineSet = new EventTimelineSet(room);
|
||||
jest.spyOn(timelineSet, 'getLiveTimeline').mockReturnValue(liveTimeline);
|
||||
|
||||
const timelineWindow = new TimelineWindow(mockClient, timelineSet);
|
||||
await timelineWindow.load(undefined, 2);
|
||||
|
||||
expect(timelineSet.getLiveTimeline).toHaveBeenCalled();
|
||||
|
||||
const expectedEvents = liveTimeline.getEvents().slice(1);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
});
|
||||
|
||||
it("should initialise from a specific event", async function() {
|
||||
const timeline = createTimeline();
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
const timelineSet = { getTimelineForEvent: () => null } as unknown as EventTimelineSet;
|
||||
mockClient.getEventTimeline.mockResolvedValue(timeline);
|
||||
|
||||
const timelineWindow = new TimelineWindow(mockClient, timelineSet);
|
||||
await timelineWindow.load(eventId, 3);
|
||||
expect(mockClient.getEventTimeline).toHaveBeenCalledWith(timelineSet, eventId);
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
});
|
||||
|
||||
it("canPaginate should return false until load has returned", async function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok1", EventTimeline.BACKWARDS);
|
||||
timeline.setPaginationToken("toktok2", EventTimeline.FORWARDS);
|
||||
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
const timelineSet = { getTimelineForEvent: () => null } as unknown as EventTimelineSet;
|
||||
mockClient.getEventTimeline.mockResolvedValue(timeline);
|
||||
|
||||
const timelineWindow = new TimelineWindow(mockClient, timelineSet);
|
||||
|
||||
const timelineWindowLoadPromise = timelineWindow.load(eventId, 3);
|
||||
|
||||
// cannot paginate before load is complete
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS)).toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS)).toBe(false);
|
||||
|
||||
// wait for load
|
||||
await timelineWindowLoadPromise;
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
// can paginate now
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pagination", function() {
|
||||
it("should be able to advance across the initial timeline", async function() {
|
||||
const timeline = createTimeline();
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
const [timelineWindow] = createWindow(timeline);
|
||||
|
||||
await timelineWindow.load(eventId, 1);
|
||||
|
||||
const expectedEvents = [timeline.getEvents()[1]];
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
|
||||
expect(await timelineWindow.paginate(EventTimeline.FORWARDS, 2)).toBe(true);
|
||||
const expectedEventsAfterPagination = timeline.getEvents().slice(1);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEventsAfterPagination);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
|
||||
// cant paginate forward anymore
|
||||
expect(await timelineWindow.paginate(EventTimeline.FORWARDS, 2)).toBe(false);
|
||||
|
||||
// paginate back again
|
||||
expect(await timelineWindow.paginate(EventTimeline.BACKWARDS, 2)).toBe(true);
|
||||
|
||||
const expectedEvents3 = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents3);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
expect(await timelineWindow.paginate(EventTimeline.BACKWARDS, 2)).toBe(false);
|
||||
});
|
||||
|
||||
it("should advance into next timeline", async function() {
|
||||
const tls = createLinkedTimelines();
|
||||
const eventId = tls[0].getEvents()[1].getId();
|
||||
const [timelineWindow] = createWindow(tls[0], { windowLimit: 5 });
|
||||
|
||||
await timelineWindow.load(eventId, 3);
|
||||
const expectedEvents = tls[0].getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
|
||||
expect(await timelineWindow.paginate(EventTimeline.FORWARDS, 2)).toBe(true);
|
||||
const expectedEvents2 = tls[0].getEvents()
|
||||
.concat(tls[1].getEvents().slice(0, 2));
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents2);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
|
||||
expect(await timelineWindow.paginate(EventTimeline.FORWARDS, 2)).toBe(true);
|
||||
|
||||
// the windowLimit should have made us drop an event from
|
||||
// tls[0]
|
||||
const expectedEvents3 = tls[0].getEvents().slice(1)
|
||||
.concat(tls[1].getEvents());
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents3);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
expect(await timelineWindow.paginate(EventTimeline.FORWARDS, 2)).toBe(false);
|
||||
});
|
||||
|
||||
it("should retreat into previous timeline", async function() {
|
||||
const tls = createLinkedTimelines();
|
||||
const eventId = tls[1].getEvents()[1].getId();
|
||||
const [timelineWindow] = createWindow(tls[1], { windowLimit: 5 });
|
||||
|
||||
await timelineWindow.load(eventId, 3);
|
||||
|
||||
const expectedEvents = tls[1].getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
|
||||
expect(await timelineWindow.paginate(EventTimeline.BACKWARDS, 2)).toBe(true);
|
||||
const expectedEvents2 = tls[0].getEvents().slice(1, 3)
|
||||
.concat(tls[1].getEvents());
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents2);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(false);
|
||||
|
||||
expect(await timelineWindow.paginate(EventTimeline.BACKWARDS, 2)).toBe(true);
|
||||
// the windowLimit should have made us drop an event from
|
||||
// tls[1]
|
||||
const expectedEvents3 = tls[0].getEvents()
|
||||
.concat(tls[1].getEvents().slice(0, 2));
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents3);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
expect(await timelineWindow.paginate(EventTimeline.BACKWARDS, 2)).toBe(false);
|
||||
});
|
||||
|
||||
it("should make forward pagination requests", async function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.FORWARDS);
|
||||
|
||||
const [timelineWindow] = createWindow(timeline, { windowLimit: 5 });
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
mockClient.paginateEventTimeline.mockImplementation(async (_t, _opts) => {
|
||||
addEventsToTimeline(timeline, 3, false);
|
||||
return true;
|
||||
});
|
||||
|
||||
await timelineWindow.load(eventId, 3);
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS)).toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS)).toBe(true);
|
||||
|
||||
expect(await timelineWindow.paginate(EventTimeline.FORWARDS, 2)).toBe(true);
|
||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledWith(timeline, { backwards: false, limit: 2 });
|
||||
const expectedEvents2 = timeline.getEvents().slice(0, 5);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents2);
|
||||
});
|
||||
|
||||
it("should make backward pagination requests", async function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.BACKWARDS);
|
||||
|
||||
const [timelineWindow] = createWindow(timeline, { windowLimit: 5 });
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
mockClient.paginateEventTimeline.mockImplementation(async (_t, _opts) => {
|
||||
addEventsToTimeline(timeline, 3, true);
|
||||
return true;
|
||||
});
|
||||
|
||||
await timelineWindow.load(eventId, 3);
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS)).toBe(true);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS)).toBe(false);
|
||||
|
||||
expect(await timelineWindow.paginate(EventTimeline.BACKWARDS, 2)).toBe(true);
|
||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledWith(timeline, { backwards: true, limit: 2 });
|
||||
|
||||
const expectedEvents2 = timeline.getEvents().slice(1, 6);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents2);
|
||||
});
|
||||
|
||||
it("should limit the number of unsuccessful pagination requests", async function() {
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.FORWARDS);
|
||||
|
||||
const [timelineWindow] = createWindow(timeline, { windowLimit: 5 });
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
mockClient.paginateEventTimeline.mockImplementation(async (_t, _opts) => {
|
||||
return true;
|
||||
});
|
||||
|
||||
await timelineWindow.load(eventId, 3);
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
expect(await timelineWindow.paginate(EventTimeline.FORWARDS, 2, true, 3)).toBe(false);
|
||||
|
||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledWith(timeline, { backwards: false, limit: 2 });
|
||||
|
||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(3);
|
||||
const expectedEvents2 = timeline.getEvents().slice(0, 3);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents2);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(false);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.FORWARDS))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,16 +1,32 @@
|
||||
import { User } from "../../src/models/user";
|
||||
import * as utils from "../test-utils/test-utils";
|
||||
/*
|
||||
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 { User, UserEvent } from "../../src/models/user";
|
||||
import { mkEvent } from "../test-utils/test-utils";
|
||||
|
||||
describe("User", function() {
|
||||
const userId = "@alice:bar";
|
||||
let user;
|
||||
let user: User;
|
||||
|
||||
beforeEach(function() {
|
||||
user = new User(userId);
|
||||
});
|
||||
|
||||
describe("setPresenceEvent", function() {
|
||||
const event = utils.mkEvent({
|
||||
const event = mkEvent({
|
||||
type: "m.presence", content: {
|
||||
presence: "online",
|
||||
user_id: userId,
|
||||
@@ -22,7 +38,7 @@ describe("User", function() {
|
||||
|
||||
it("should emit 'User.displayName' if the display name changes", function() {
|
||||
let emitCount = 0;
|
||||
user.on("User.displayName", function(ev, usr) {
|
||||
user.on(UserEvent.DisplayName, function(ev, usr) {
|
||||
emitCount += 1;
|
||||
});
|
||||
user.setPresenceEvent(event);
|
||||
@@ -33,7 +49,7 @@ describe("User", function() {
|
||||
|
||||
it("should emit 'User.avatarUrl' if the avatar URL changes", function() {
|
||||
let emitCount = 0;
|
||||
user.on("User.avatarUrl", function(ev, usr) {
|
||||
user.on(UserEvent.AvatarUrl, function(ev, usr) {
|
||||
emitCount += 1;
|
||||
});
|
||||
user.setPresenceEvent(event);
|
||||
@@ -44,7 +60,7 @@ describe("User", function() {
|
||||
|
||||
it("should emit 'User.presence' if the presence changes", function() {
|
||||
let emitCount = 0;
|
||||
user.on("User.presence", function(ev, usr) {
|
||||
user.on(UserEvent.Presence, function(ev, usr) {
|
||||
emitCount += 1;
|
||||
});
|
||||
user.setPresenceEvent(event);
|
Reference in New Issue
Block a user