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

Support MSC4157: delayed events via Widget API (#4311)

This commit is contained in:
Andrew Ferrazzutti
2024-08-01 10:17:52 -04:00
committed by GitHub
parent 89a9a7fa38
commit e10c362ef0
6 changed files with 341 additions and 15 deletions

View File

@ -268,7 +268,8 @@ describe("MSC4108SignInWithQR", () => {
it("should abort if device doesn't come up by timeout", async () => {
jest.spyOn(global, "setTimeout").mockImplementation((fn) => {
(<Function>fn)();
return -1;
// TODO: mock timers properly
return -1 as any;
});
jest.spyOn(Date, "now").mockImplementation(() => {
return 12345678 + mocked(setTimeout).mock.calls.length * 1000;
@ -320,7 +321,8 @@ describe("MSC4108SignInWithQR", () => {
it("should not send secrets if user cancels", async () => {
jest.spyOn(global, "setTimeout").mockImplementation((fn) => {
(<Function>fn)();
return -1;
// TODO: mock timers properly
return -1 as any;
});
await Promise.all([ourLogin.negotiateProtocols(), opponentLogin.negotiateProtocols()]);

View File

@ -32,7 +32,7 @@ import {
IOpenIDCredentials,
} from "matrix-widget-api";
import { createRoomWidgetClient, MsgType } from "../../src/matrix";
import { createRoomWidgetClient, MsgType, UpdateDelayedEventAction } from "../../src/matrix";
import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "../../src/client";
import { SyncState } from "../../src/sync";
import { ICapabilities } from "../../src/embedded";
@ -59,8 +59,26 @@ class MockWidgetApi extends EventEmitter {
public requestCapabilityToReceiveState = jest.fn();
public requestCapabilityToSendToDevice = jest.fn();
public requestCapabilityToReceiveToDevice = jest.fn();
public sendRoomEvent = jest.fn(() => ({ event_id: `$${Math.random()}` }));
public sendStateEvent = jest.fn();
public sendRoomEvent = jest.fn(
(eventType: string, content: unknown, roomId?: string, delay?: number, parentDelayId?: string) =>
delay === undefined && parentDelayId === undefined
? { event_id: `$${Math.random()}` }
: { delay_id: `id-${Math.random()}` },
);
public sendStateEvent = jest.fn(
(
eventType: string,
stateKey: string,
content: unknown,
roomId?: string,
delay?: number,
parentDelayId?: string,
) =>
delay === undefined && parentDelayId === undefined
? { event_id: `$${Math.random()}` }
: { delay_id: `id-${Math.random()}` },
);
public updateDelayedEvent = jest.fn();
public sendToDevice = jest.fn();
public requestOpenIDConnectToken = jest.fn(() => {
return testOIDCToken;
@ -125,6 +143,17 @@ describe("RoomWidgetClient", () => {
);
});
it("send handles wrong field in response", async () => {
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
widgetApi.sendRoomEvent.mockResolvedValueOnce({
room_id: "!1:example.org",
delay_id: `id-${Math.random}`,
});
await expect(
client.sendEvent("!1:example.org", "org.matrix.rageshake_request", { request_id: 123 }),
).rejects.toThrow();
});
it("receives", async () => {
const event = new MatrixEvent({
type: "org.matrix.rageshake_request",
@ -160,6 +189,199 @@ describe("RoomWidgetClient", () => {
});
});
describe("delayed events", () => {
describe("when supported", () => {
const doesServerSupportUnstableFeatureMock = jest.fn((feature) =>
Promise.resolve(feature === "org.matrix.msc4140"),
);
beforeAll(() => {
MatrixClient.prototype.doesServerSupportUnstableFeature = doesServerSupportUnstableFeatureMock;
});
afterAll(() => {
doesServerSupportUnstableFeatureMock.mockReset();
});
it("sends delayed message events", async () => {
await makeClient({ sendDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
await client._unstable_sendDelayedEvent(
"!1:example.org",
{ delay: 2000 },
null,
"org.matrix.rageshake_request",
{ request_id: 123 },
);
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
"org.matrix.rageshake_request",
{ request_id: 123 },
"!1:example.org",
2000,
undefined,
);
});
it("sends child action delayed message events", async () => {
await makeClient({ sendDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
const parentDelayId = `id-${Math.random()}`;
await client._unstable_sendDelayedEvent(
"!1:example.org",
{ parent_delay_id: parentDelayId },
null,
"org.matrix.rageshake_request",
{ request_id: 123 },
);
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
"org.matrix.rageshake_request",
{ request_id: 123 },
"!1:example.org",
undefined,
parentDelayId,
);
});
it("sends delayed state events", async () => {
await makeClient({
sendDelayedEvents: true,
sendState: [{ eventType: "org.example.foo", stateKey: "bar" }],
});
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
await client._unstable_sendDelayedStateEvent(
"!1:example.org",
{ delay: 2000 },
"org.example.foo",
{ hello: "world" },
"bar",
);
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
"org.example.foo",
"bar",
{ hello: "world" },
"!1:example.org",
2000,
undefined,
);
});
it("sends child action delayed state events", async () => {
await makeClient({
sendDelayedEvents: true,
sendState: [{ eventType: "org.example.foo", stateKey: "bar" }],
});
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
const parentDelayId = `fg-${Math.random()}`;
await client._unstable_sendDelayedStateEvent(
"!1:example.org",
{ parent_delay_id: parentDelayId },
"org.example.foo",
{ hello: "world" },
"bar",
);
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
"org.example.foo",
"bar",
{ hello: "world" },
"!1:example.org",
undefined,
parentDelayId,
);
});
it("send delayed message events handles wrong field in response", async () => {
await makeClient({ sendDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
widgetApi.sendRoomEvent.mockResolvedValueOnce({
room_id: "!1:example.org",
event_id: `$${Math.random()}`,
});
await expect(
client._unstable_sendDelayedEvent(
"!1:example.org",
{ delay: 2000 },
null,
"org.matrix.rageshake_request",
{ request_id: 123 },
),
).rejects.toThrow();
});
it("send delayed state events handles wrong field in response", async () => {
await makeClient({
sendDelayedEvents: true,
sendState: [{ eventType: "org.example.foo", stateKey: "bar" }],
});
widgetApi.sendStateEvent.mockResolvedValueOnce({
room_id: "!1:example.org",
event_id: `$${Math.random()}`,
});
await expect(
client._unstable_sendDelayedStateEvent(
"!1:example.org",
{ delay: 2000 },
"org.example.foo",
{ hello: "world" },
"bar",
),
).rejects.toThrow();
});
it("updates delayed events", async () => {
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
for (const action of [
UpdateDelayedEventAction.Cancel,
UpdateDelayedEventAction.Restart,
UpdateDelayedEventAction.Send,
]) {
await client._unstable_updateDelayedEvent("id", action);
expect(widgetApi.updateDelayedEvent).toHaveBeenCalledWith("id", action);
}
});
});
describe("when unsupported", () => {
it("fails to send delayed message events", async () => {
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
await expect(
client._unstable_sendDelayedEvent(
"!1:example.org",
{ delay: 2000 },
null,
"org.matrix.rageshake_request",
{ request_id: 123 },
),
).rejects.toThrow("Server does not support");
});
it("fails to send delayed state events", async () => {
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
await expect(
client._unstable_sendDelayedStateEvent(
"!1:example.org",
{ delay: 2000 },
"org.example.foo",
{ hello: "world" },
"bar",
),
).rejects.toThrow("Server does not support");
});
it("fails to update delayed state events", async () => {
await makeClient({});
for (const action of [
UpdateDelayedEventAction.Cancel,
UpdateDelayedEventAction.Restart,
UpdateDelayedEventAction.Send,
]) {
await expect(client._unstable_updateDelayedEvent("id", action)).rejects.toThrow(
"Server does not support",
);
}
});
});
});
describe("initialization", () => {
it("requests permissions for specific message types", async () => {
await makeClient({ sendMessage: [MsgType.Text], receiveMessage: [MsgType.Text] });
@ -211,6 +433,17 @@ describe("RoomWidgetClient", () => {
);
});
it("send handles incorrect response", async () => {
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
widgetApi.sendStateEvent.mockResolvedValueOnce({
room_id: "!1:example.org",
delay_id: `id-${Math.random}`,
});
await expect(
client.sendStateEvent("!1:example.org", "org.example.foo", { hello: "world" }, "bar"),
).rejects.toThrow();
});
it("receives", async () => {
await makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");