Skip to content

Commit f5c4dfd

Browse files
committed
Merge branch 'master' into af/code-quality
2 parents 35f08f4 + 120a290 commit f5c4dfd

File tree

10 files changed

+364
-164
lines changed

10 files changed

+364
-164
lines changed

.github/workflows/build.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
uses: actions/setup-node@v4
1717
with:
1818
cache: "yarn"
19+
node-version-file: .node-version
1920

2021
- name: Install NPM packages
2122
run: yarn install --frozen-lockfile

.github/workflows/release.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ jobs:
2828
uses: actions/setup-node@v4
2929
with:
3030
cache: "yarn"
31+
node-version-file: .node-version
3132

3233
- name: 🛠️ Setup
3334
run: yarn install --pure-lockfile

.node-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@
4545
"@babel/preset-env": "^7.11.5",
4646
"@babel/preset-typescript": "^7.10.4",
4747
"@casualbot/jest-sonar-reporter": "^2.2.7",
48-
"@stylistic/eslint-plugin": "^2.10.1",
48+
"@stylistic/eslint-plugin": "^3.1.0",
4949
"@testing-library/dom": "^8.0.0",
5050
"@types/jest": "^29.5.12",
51-
"@types/node": "^18.16.0",
52-
"@typescript-eslint/eslint-plugin": "^8.0.0",
53-
"@typescript-eslint/parser": "^8.0.0",
51+
"@types/node": "^22.18.13",
52+
"@typescript-eslint/eslint-plugin": "^8.46.2",
53+
"@typescript-eslint/parser": "^8.46.2",
5454
"browserify": "^17.0.0",
55-
"eslint": "^8.0.0",
55+
"eslint": "^8.57.1",
5656
"eslint-config-google": "^0.14.0",
5757
"eslint-config-prettier": "^9.0.0",
5858
"eslint-plugin-babel": "^5.3.1",
@@ -65,7 +65,7 @@
6565
"rimraf": "^3.0.2",
6666
"tinyify": "^3.0.0",
6767
"ts-node": "^10.9.1",
68-
"typescript": "^5.0.4"
68+
"typescript": "^5.9.3"
6969
},
7070
"dependencies": {
7171
"@types/events": "^3.0.0",

src/ClientWidgetApi.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -690,25 +690,31 @@ export class ClientWidgetApi extends EventEmitter {
690690
});
691691
}
692692

693+
let updateDelayedEvent: (delayId: string) => Promise<void>;
693694
switch (request.data.action) {
694695
case UpdateDelayedEventAction.Cancel:
696+
updateDelayedEvent = this.driver.cancelScheduledDelayedEvent;
697+
break;
695698
case UpdateDelayedEventAction.Restart:
699+
updateDelayedEvent = this.driver.restartScheduledDelayedEvent;
700+
break;
696701
case UpdateDelayedEventAction.Send:
697-
this.driver
698-
.updateDelayedEvent(request.data.delay_id, request.data.action)
699-
.then(() => {
700-
return this.transport.reply<IWidgetApiAcknowledgeResponseData>(request, {});
701-
})
702-
.catch((e: unknown) => {
703-
console.error("error updating delayed event: ", e);
704-
this.handleDriverError(e, request, "Error updating delayed event");
705-
});
702+
updateDelayedEvent = this.driver.sendScheduledDelayedEvent;
706703
break;
707704
default:
708705
return this.transport.reply<IWidgetApiErrorResponseData>(request, {
709706
error: { message: "Invalid request - unsupported action" },
710707
});
711708
}
709+
updateDelayedEvent
710+
.call(this.driver, request.data.delay_id)
711+
.then(() => {
712+
return this.transport.reply<IWidgetApiAcknowledgeResponseData>(request, {});
713+
})
714+
.catch((e: unknown) => {
715+
console.error("error updating delayed event: ", e);
716+
this.handleDriverError(e, request, "Error updating delayed event");
717+
});
712718
}
713719

714720
private async handleSendToDevice(request: ISendToDeviceFromWidgetActionRequest): Promise<void> {

src/WidgetApi.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -483,15 +483,38 @@ export class WidgetApi extends EventEmitter {
483483
/**
484484
* @experimental This currently relies on an unstable MSC (MSC4157).
485485
*/
486-
public updateDelayedEvent(
487-
delayId: string,
488-
action: UpdateDelayedEventAction,
489-
): Promise<IUpdateDelayedEventFromWidgetResponseData> {
486+
public cancelScheduledDelayedEvent(delayId: string): Promise<IUpdateDelayedEventFromWidgetResponseData> {
490487
return this.transport.send<IUpdateDelayedEventFromWidgetRequestData, IUpdateDelayedEventFromWidgetResponseData>(
491488
WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
492489
{
493490
delay_id: delayId,
494-
action,
491+
action: UpdateDelayedEventAction.Cancel,
492+
},
493+
);
494+
}
495+
496+
/**
497+
* @deprecated This currently relies on an unstable MSC (MSC4157).
498+
*/
499+
public restartScheduledDelayedEvent(delayId: string): Promise<IUpdateDelayedEventFromWidgetResponseData> {
500+
return this.transport.send<IUpdateDelayedEventFromWidgetRequestData, IUpdateDelayedEventFromWidgetResponseData>(
501+
WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
502+
{
503+
delay_id: delayId,
504+
action: UpdateDelayedEventAction.Restart,
505+
},
506+
);
507+
}
508+
509+
/**
510+
* @deprecated This currently relies on an unstable MSC (MSC4157).
511+
*/
512+
public sendScheduledDelayedEvent(delayId: string): Promise<IUpdateDelayedEventFromWidgetResponseData> {
513+
return this.transport.send<IUpdateDelayedEventFromWidgetRequestData, IUpdateDelayedEventFromWidgetResponseData>(
514+
WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
515+
{
516+
delay_id: delayId,
517+
action: UpdateDelayedEventAction.Send,
495518
},
496519
);
497520
}

src/driver/WidgetDriver.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
IRoomAccountData,
2424
ITurnServer,
2525
IWidgetApiErrorResponseDataDetails,
26-
UpdateDelayedEventAction,
2726
} from "..";
2827

2928
export interface ISendEventDetails {
@@ -142,10 +141,32 @@ export abstract class WidgetDriver {
142141

143142
/**
144143
* @experimental Part of MSC4140 & MSC4157
145-
* Run the specified {@link action} for the delayed event matching the provided {@link delayId}.
146-
* @throws Rejected when there is no matching delayed event, or when the action failed to run.
144+
* Cancel the scheduled delivery of the delayed event matching the provided {@link delayId}.
145+
* @throws Rejected when there is no matching delayed event,
146+
* or when the delayed event failed to be cancelled.
147147
*/
148-
public updateDelayedEvent(delayId: string, action: UpdateDelayedEventAction): Promise<void> {
148+
public cancelScheduledDelayedEvent(delayId: string): Promise<void> {
149+
return Promise.reject(new Error("Failed to override function"));
150+
}
151+
152+
/**
153+
* @experimental Part of MSC4140 & MSC4157
154+
* Restart the scheduled delivery of the delayed event matching the provided {@link delayId}.
155+
* @throws Rejected when there is no matching delayed event,
156+
* or when the delayed event failed to be restarted.
157+
*/
158+
public restartScheduledDelayedEvent(delayId: string): Promise<void> {
159+
return Promise.reject(new Error("Failed to override function"));
160+
}
161+
162+
/**
163+
* @experimental Part of MSC4140 & MSC4157
164+
* Immediately send the delayed event matching the provided {@link delayId},
165+
* instead of waiting for its scheduled delivery.
166+
* @throws Rejected when there is no matching delayed event,
167+
* or when the delayed event failed to be sent.
168+
*/
169+
public sendScheduledDelayedEvent(delayId: string): Promise<void> {
149170
return Promise.reject(new Error("Failed to override function"));
150171
}
151172

test/ClientWidgetApi-test.ts

Lines changed: 128 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ describe("ClientWidgetApi", () => {
128128
readEventRelations: jest.fn(),
129129
sendEvent: jest.fn(),
130130
sendDelayedEvent: jest.fn(),
131-
updateDelayedEvent: jest.fn(),
131+
cancelScheduledDelayedEvent: jest.fn(),
132+
restartScheduledDelayedEvent: jest.fn(),
133+
sendScheduledDelayedEvent: jest.fn(),
132134
sendToDevice: jest.fn(),
133135
askOpenID: jest.fn(),
134136
readRoomAccountData: jest.fn(),
@@ -903,7 +905,57 @@ describe("ClientWidgetApi", () => {
903905
});
904906

905907
describe("update_delayed_event action", () => {
906-
it("fails to update delayed events", async () => {
908+
it("fails to cancel delayed events", async () => {
909+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
910+
api: WidgetApiDirection.FromWidget,
911+
widgetId: "test",
912+
requestId: "0",
913+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
914+
data: {
915+
delay_id: "f",
916+
action: UpdateDelayedEventAction.Cancel,
917+
},
918+
};
919+
920+
await loadIframe([]); // Without the required capability
921+
922+
emitEvent(new CustomEvent("", { detail: event }));
923+
924+
await waitFor(() => {
925+
expect(transport.reply).toBeCalledWith(event, {
926+
error: { message: expect.any(String) },
927+
});
928+
});
929+
930+
expect(driver.cancelScheduledDelayedEvent).not.toBeCalled();
931+
});
932+
933+
it("fails to restart delayed events", async () => {
934+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
935+
api: WidgetApiDirection.FromWidget,
936+
widgetId: "test",
937+
requestId: "0",
938+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
939+
data: {
940+
delay_id: "f",
941+
action: UpdateDelayedEventAction.Restart,
942+
},
943+
};
944+
945+
await loadIframe([]); // Without the required capability
946+
947+
emitEvent(new CustomEvent("", { detail: event }));
948+
949+
await waitFor(() => {
950+
expect(transport.reply).toBeCalledWith(event, {
951+
error: { message: expect.any(String) },
952+
});
953+
});
954+
955+
expect(driver.restartScheduledDelayedEvent).not.toBeCalled();
956+
});
957+
958+
it("fails to send delayed events", async () => {
907959
const event: IUpdateDelayedEventFromWidgetActionRequest = {
908960
api: WidgetApiDirection.FromWidget,
909961
widgetId: "test",
@@ -925,7 +977,7 @@ describe("ClientWidgetApi", () => {
925977
});
926978
});
927979

928-
expect(driver.updateDelayedEvent).not.toBeCalled();
980+
expect(driver.sendScheduledDelayedEvent).not.toBeCalled();
929981
});
930982

931983
it("fails to update delayed events with unsupported action", async () => {
@@ -950,42 +1002,88 @@ describe("ClientWidgetApi", () => {
9501002
});
9511003
});
9521004

953-
expect(driver.updateDelayedEvent).not.toBeCalled();
1005+
expect(driver.cancelScheduledDelayedEvent).not.toBeCalled();
1006+
expect(driver.restartScheduledDelayedEvent).not.toBeCalled();
1007+
expect(driver.sendScheduledDelayedEvent).not.toBeCalled();
9541008
});
9551009

956-
it("updates delayed events", async () => {
957-
driver.updateDelayedEvent.mockResolvedValue(undefined);
1010+
it("can cancel delayed events", async () => {
1011+
driver.cancelScheduledDelayedEvent.mockResolvedValue(undefined);
9581012

959-
for (const action of [
960-
UpdateDelayedEventAction.Cancel,
961-
UpdateDelayedEventAction.Restart,
962-
UpdateDelayedEventAction.Send,
963-
]) {
964-
const event: IUpdateDelayedEventFromWidgetActionRequest = {
965-
api: WidgetApiDirection.FromWidget,
966-
widgetId: "test",
967-
requestId: "0",
968-
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
969-
data: {
970-
delay_id: "f",
971-
action,
972-
},
973-
};
1013+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
1014+
api: WidgetApiDirection.FromWidget,
1015+
widgetId: "test",
1016+
requestId: "0",
1017+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
1018+
data: {
1019+
delay_id: "f",
1020+
action: UpdateDelayedEventAction.Cancel,
1021+
},
1022+
};
9741023

975-
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
1024+
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
9761025

977-
emitEvent(new CustomEvent("", { detail: event }));
1026+
emitEvent(new CustomEvent("", { detail: event }));
9781027

979-
await waitFor(() => {
980-
expect(transport.reply).toHaveBeenCalledWith(event, {});
981-
});
1028+
await waitFor(() => {
1029+
expect(transport.reply).toHaveBeenCalledWith(event, {});
1030+
});
1031+
1032+
expect(driver.cancelScheduledDelayedEvent).toHaveBeenCalledWith(event.data.delay_id);
1033+
});
1034+
1035+
it("can restart delayed events", async () => {
1036+
driver.restartScheduledDelayedEvent.mockResolvedValue(undefined);
1037+
1038+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
1039+
api: WidgetApiDirection.FromWidget,
1040+
widgetId: "test",
1041+
requestId: "0",
1042+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
1043+
data: {
1044+
delay_id: "f",
1045+
action: UpdateDelayedEventAction.Restart,
1046+
},
1047+
};
1048+
1049+
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
1050+
1051+
emitEvent(new CustomEvent("", { detail: event }));
1052+
1053+
await waitFor(() => {
1054+
expect(transport.reply).toHaveBeenCalledWith(event, {});
1055+
});
1056+
1057+
expect(driver.restartScheduledDelayedEvent).toHaveBeenCalledWith(event.data.delay_id);
1058+
});
1059+
1060+
it("can send delayed events", async () => {
1061+
driver.sendScheduledDelayedEvent.mockResolvedValue(undefined);
1062+
1063+
const event: IUpdateDelayedEventFromWidgetActionRequest = {
1064+
api: WidgetApiDirection.FromWidget,
1065+
widgetId: "test",
1066+
requestId: "0",
1067+
action: WidgetApiFromWidgetAction.MSC4157UpdateDelayedEvent,
1068+
data: {
1069+
delay_id: "f",
1070+
action: UpdateDelayedEventAction.Send,
1071+
},
1072+
};
1073+
1074+
await loadIframe(["org.matrix.msc4157.update_delayed_event"]);
1075+
1076+
emitEvent(new CustomEvent("", { detail: event }));
1077+
1078+
await waitFor(() => {
1079+
expect(transport.reply).toHaveBeenCalledWith(event, {});
1080+
});
9821081

983-
expect(driver.updateDelayedEvent).toHaveBeenCalledWith(event.data.delay_id, event.data.action);
984-
}
1082+
expect(driver.sendScheduledDelayedEvent).toHaveBeenCalledWith(event.data.delay_id);
9851083
});
9861084

9871085
it("should reject requests when the driver throws an exception", async () => {
988-
driver.updateDelayedEvent.mockRejectedValue(new Error("M_BAD_JSON: Content must be a JSON object"));
1086+
driver.sendScheduledDelayedEvent.mockRejectedValue(new Error("M_BAD_JSON: Content must be a JSON object"));
9891087

9901088
const event: IUpdateDelayedEventFromWidgetActionRequest = {
9911089
api: WidgetApiDirection.FromWidget,
@@ -1012,7 +1110,7 @@ describe("ClientWidgetApi", () => {
10121110
it("should reject with Matrix API error response thrown by driver", async () => {
10131111
driver.processError.mockImplementation(processCustomMatrixError);
10141112

1015-
driver.updateDelayedEvent.mockRejectedValue(
1113+
driver.sendScheduledDelayedEvent.mockRejectedValue(
10161114
new CustomMatrixError("failed to update delayed event", 400, "M_NOT_JSON", {
10171115
reason: "Content must be a JSON object.",
10181116
}),

0 commit comments

Comments
 (0)