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

Poll model - page /relations results (#3073)

* first cut poll model

* process incoming poll relations

* allow alt event types in relations model

* allow alt event types in relations model

* remove unneccesary checks on remove relation

* comment

* Revert "allow alt event types in relations model"

This reverts commit e578d84464.

* Revert "Revert "allow alt event types in relations model""

This reverts commit 515db7a8bc.

* basic handling for new poll relations

* tests

* test room.processPollEvents

* join processBeaconEvents and poll events in client

* tidy and set 23 copyrights

* use rooms instance of matrixClient

* tidy

* more copyright

* simplify processPollEvent code

* throw when poll start event has no roomId

* updates for events-sdk move

* more type changes for events-sdk changes

* page poll relation results

* validate poll end event senders

* reformatted copyright

* undo more comment reformatting

* test paging

* use correct pollstartevent type

* emit after updating _isFetchingResponses state

* make rootEvent public readonly

* fix poll end validation logic to allow poll creator to end poll regardless of redaction
This commit is contained in:
Kerry
2023-02-02 09:44:40 +13:00
committed by GitHub
parent 2800681bb1
commit 4e8affafcc
2 changed files with 187 additions and 32 deletions

View File

@ -46,7 +46,7 @@ describe("Poll", () => {
jest.clearAllMocks();
jest.setSystemTime(now);
mockClient.relations.mockResolvedValue({ events: [] });
mockClient.relations.mockReset().mockResolvedValue({ events: [] });
maySendRedactionForEventSpy.mockClear().mockReturnValue(true);
});
@ -95,8 +95,17 @@ describe("Poll", () => {
it("calls relations api and emits", async () => {
const poll = new Poll(basePollStartEvent, mockClient, room);
const emitSpy = jest.spyOn(poll, "emit");
const responses = await poll.getResponses();
expect(mockClient.relations).toHaveBeenCalledWith(roomId, basePollStartEvent.getId(), "m.reference");
const fetchResponsePromise = poll.getResponses();
expect(poll.isFetchingResponses).toBe(true);
const responses = await fetchResponsePromise;
expect(poll.isFetchingResponses).toBe(false);
expect(mockClient.relations).toHaveBeenCalledWith(
roomId,
basePollStartEvent.getId(),
"m.reference",
undefined,
{ from: undefined },
);
expect(emitSpy).toHaveBeenCalledWith(PollEvent.Responses, responses);
});
@ -133,6 +142,48 @@ describe("Poll", () => {
expect(responses.getRelations()).toEqual([stableResponseEvent, unstableResponseEvent]);
});
describe("with multiple pages of relations", () => {
const makeResponses = (count = 1, timestamp = now): MatrixEvent[] =>
new Array(count)
.fill("x")
.map((_x, index) =>
makeRelatedEvent(
{ type: M_POLL_RESPONSE.stable!, sender: "@bob@server.org" },
timestamp + index,
),
);
it("page relations responses", async () => {
const responseEvents = makeResponses(6);
mockClient.relations
.mockResolvedValueOnce({
events: responseEvents.slice(0, 2),
nextBatch: "test-next-1",
})
.mockResolvedValueOnce({
events: responseEvents.slice(2, 4),
nextBatch: "test-next-2",
})
.mockResolvedValueOnce({
events: responseEvents.slice(4),
});
const poll = new Poll(basePollStartEvent, mockClient, room);
jest.spyOn(poll, "emit");
const responses = await poll.getResponses();
expect(mockClient.relations.mock.calls).toEqual([
[roomId, basePollStartEvent.getId(), "m.reference", undefined, { from: undefined }],
[roomId, basePollStartEvent.getId(), "m.reference", undefined, { from: "test-next-1" }],
[roomId, basePollStartEvent.getId(), "m.reference", undefined, { from: "test-next-2" }],
]);
expect(poll.emit).toHaveBeenCalledTimes(3);
expect(poll.isFetchingResponses).toBeFalsy();
expect(responses.getRelations().length).toEqual(6);
});
});
describe("with poll end event", () => {
const stablePollEndEvent = makeRelatedEvent({ type: M_POLL_END.stable!, sender: "@bob@server.org" });
const unstablePollEndEvent = makeRelatedEvent({ type: M_POLL_END.unstable!, sender: "@bob@server.org" });
@ -156,17 +207,6 @@ describe("Poll", () => {
expect(poll.emit).toHaveBeenCalledWith(PollEvent.End);
});
it("does not set poll end event when sent by a user without redaction rights", async () => {
const poll = new Poll(basePollStartEvent, mockClient, room);
maySendRedactionForEventSpy.mockReturnValue(false);
jest.spyOn(poll, "emit");
await poll.getResponses();
expect(maySendRedactionForEventSpy).toHaveBeenCalledWith(basePollStartEvent, "@bob@server.org");
expect(poll.isEnded).toBe(false);
expect(poll.emit).not.toHaveBeenCalledWith(PollEvent.End);
});
it("sets poll end event when endevent sender also created the poll, but does not have redaction rights", async () => {
const pollStartEvent = new MatrixEvent({
...PollStartEvent.from("What?", ["a", "b"], M_POLL_KIND_DISCLOSED.name).serialize(),
@ -316,6 +356,89 @@ describe("Poll", () => {
expect(responses.getRelations()).toEqual([responseEventAtEnd, responseEventBeforeEnd]);
});
it("does not set poll end event when sent by invalid user", async () => {
maySendRedactionForEventSpy.mockReturnValue(false);
const stablePollEndEvent = makeRelatedEvent({ type: M_POLL_END.stable!, sender: "@charlie:server.org" });
const responseEventAfterEnd = makeRelatedEvent({ type: M_POLL_RESPONSE.name }, now + 1000);
mockClient.relations.mockResolvedValue({
events: [responseEventAfterEnd],
});
const poll = new Poll(basePollStartEvent, mockClient, room);
await poll.getResponses();
jest.spyOn(poll, "emit");
poll.onNewRelation(stablePollEndEvent);
// didn't end, didn't refilter responses
expect(poll.emit).not.toHaveBeenCalled();
expect(poll.isEnded).toBeFalsy();
expect(maySendRedactionForEventSpy).toHaveBeenCalledWith(basePollStartEvent, "@charlie:server.org");
});
it("does not set poll end event when an earlier end event already exists", async () => {
const earlierPollEndEvent = makeRelatedEvent(
{ type: M_POLL_END.stable!, sender: "@valid:server.org" },
now,
);
const laterPollEndEvent = makeRelatedEvent(
{ type: M_POLL_END.stable!, sender: "@valid:server.org" },
now + 2000,
);
const poll = new Poll(basePollStartEvent, mockClient, room);
await poll.getResponses();
poll.onNewRelation(earlierPollEndEvent);
// first end event set correctly
expect(poll.isEnded).toBeTruthy();
// reset spy count
jest.spyOn(poll, "emit").mockClear();
poll.onNewRelation(laterPollEndEvent);
// didn't set new end event, didn't refilter responses
expect(poll.emit).not.toHaveBeenCalled();
expect(poll.isEnded).toBeTruthy();
});
it("replaces poll end event and refilters when an older end event already exists", async () => {
const earlierPollEndEvent = makeRelatedEvent(
{ type: M_POLL_END.stable!, sender: "@valid:server.org" },
now,
);
const laterPollEndEvent = makeRelatedEvent(
{ type: M_POLL_END.stable!, sender: "@valid:server.org" },
now + 2000,
);
const responseEventBeforeEnd = makeRelatedEvent({ type: M_POLL_RESPONSE.name }, now - 1000);
const responseEventAtEnd = makeRelatedEvent({ type: M_POLL_RESPONSE.name }, now);
const responseEventAfterEnd = makeRelatedEvent({ type: M_POLL_RESPONSE.name }, now + 1000);
mockClient.relations.mockResolvedValue({
events: [responseEventAfterEnd, responseEventAtEnd, responseEventBeforeEnd, laterPollEndEvent],
});
const poll = new Poll(basePollStartEvent, mockClient, room);
const responses = await poll.getResponses();
// all responses have a timestamp < laterPollEndEvent
expect(responses.getRelations().length).toEqual(3);
// first end event set correctly
expect(poll.isEnded).toBeTruthy();
// reset spy count
jest.spyOn(poll, "emit").mockClear();
// add a valid end event with earlier timestamp
poll.onNewRelation(earlierPollEndEvent);
// emitted new end event
expect(poll.emit).toHaveBeenCalledWith(PollEvent.End);
// filtered responses and emitted
expect(poll.emit).toHaveBeenCalledWith(PollEvent.Responses, responses);
expect(responses.getRelations()).toEqual([responseEventAtEnd, responseEventBeforeEnd]);
});
it("sets poll end event and refilters responses based on timestamp", async () => {
const stablePollEndEvent = makeRelatedEvent({ type: M_POLL_END.stable!, sender: userId });
const responseEventBeforeEnd = makeRelatedEvent({ type: M_POLL_RESPONSE.name }, now - 1000);