Fix widgets getting stuck in loading states (#31314)
* Refer to ClientWidgetApi as "widget API" rather than "messaging" * Rename StopGapWidgetDriver to ElementWidgetDriver * Rename StopGapWidget to WidgetMessaging * Fix WidgetMessaging's lifetime by storing it in WidgetMessagingStore (Rather than storing just the raw ClientWidgetApi objects.) * Unfail test * use an error * cleanup start * Add docs * Prettier * link to store * remove a let * More logging, split up loop * Add a test demonstrating a regression in Call.start * Restore Call.start to a single, robust event loop * Fix test failure by resetting the messaging store * Expand on the WidgetMessaging doc comment * Add additional tests to buff up coverage * Add a test for the sticker picker opening the IM. * reduce copy paste --------- Co-authored-by: Half-Shot <will@half-shot.uk> Co-authored-by: Timo K <toger5@hotmail.de>
This commit is contained in:
@@ -48,36 +48,37 @@ import { Anonymity, PosthogAnalytics } from "../../../src/PosthogAnalytics";
|
||||
import { type SettingKey } from "../../../src/settings/Settings.tsx";
|
||||
import SdkConfig from "../../../src/SdkConfig.ts";
|
||||
import DMRoomMap from "../../../src/utils/DMRoomMap.ts";
|
||||
import { WidgetMessagingEvent, type WidgetMessaging } from "../../../src/stores/widgets/WidgetMessaging.ts";
|
||||
|
||||
const { enabledSettings } = enableCalls();
|
||||
|
||||
const setUpWidget = (call: Call): { widget: Widget; messaging: Mocked<ClientWidgetApi> } => {
|
||||
const setUpWidget = (
|
||||
call: Call,
|
||||
): { widget: Widget; messaging: Mocked<WidgetMessaging>; widgetApi: Mocked<ClientWidgetApi> } => {
|
||||
call.widget.data = { ...call.widget, skipLobby: true };
|
||||
const widget = new Widget(call.widget);
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
const messaging = {
|
||||
on: eventEmitter.on.bind(eventEmitter),
|
||||
off: eventEmitter.off.bind(eventEmitter),
|
||||
once: eventEmitter.once.bind(eventEmitter),
|
||||
emit: eventEmitter.emit.bind(eventEmitter),
|
||||
stop: jest.fn(),
|
||||
transport: {
|
||||
const widgetApi = new (class extends EventEmitter {
|
||||
transport = {
|
||||
send: jest.fn(),
|
||||
reply: jest.fn(),
|
||||
},
|
||||
} as unknown as Mocked<ClientWidgetApi>;
|
||||
};
|
||||
})() as unknown as Mocked<ClientWidgetApi>;
|
||||
const messaging = new (class extends EventEmitter {
|
||||
stop = jest.fn();
|
||||
widgetApi = widgetApi;
|
||||
})() as unknown as Mocked<WidgetMessaging>;
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, call.roomId, messaging);
|
||||
|
||||
return { widget, messaging };
|
||||
return { widget, messaging, widgetApi };
|
||||
};
|
||||
|
||||
async function connect(call: Call, messaging: Mocked<ClientWidgetApi>, startWidget = true): Promise<void> {
|
||||
async function connect(call: Call, widgetApi: Mocked<ClientWidgetApi>, startWidget = true): Promise<void> {
|
||||
async function sessionConnect() {
|
||||
await new Promise<void>((r) => {
|
||||
setTimeout(() => r(), 400);
|
||||
});
|
||||
messaging.emit(`action:${ElementWidgetActions.JoinCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
widgetApi.emit(`action:${ElementWidgetActions.JoinCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
}
|
||||
async function runTimers() {
|
||||
jest.advanceTimersByTime(500);
|
||||
@@ -87,12 +88,12 @@ async function connect(call: Call, messaging: Mocked<ClientWidgetApi>, startWidg
|
||||
await Promise.all([...(startWidget ? [call.start()] : []), runTimers()]);
|
||||
}
|
||||
|
||||
async function disconnect(call: Call, messaging: Mocked<ClientWidgetApi>): Promise<void> {
|
||||
async function disconnect(call: Call, widgetApi: Mocked<ClientWidgetApi>): Promise<void> {
|
||||
async function sessionDisconnect() {
|
||||
await new Promise<void>((r) => {
|
||||
setTimeout(() => r(), 400);
|
||||
});
|
||||
messaging.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
widgetApi.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
}
|
||||
async function runTimers() {
|
||||
jest.advanceTimersByTime(500);
|
||||
@@ -150,7 +151,8 @@ describe("JitsiCall", () => {
|
||||
describe("instance in a video room", () => {
|
||||
let call: JitsiCall;
|
||||
let widget: Widget;
|
||||
let messaging: Mocked<ClientWidgetApi>;
|
||||
let messaging: Mocked<WidgetMessaging>;
|
||||
let widgetApi: Mocked<ClientWidgetApi>;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.useFakeTimers();
|
||||
@@ -161,16 +163,16 @@ describe("JitsiCall", () => {
|
||||
if (maybeCall === null) throw new Error("Failed to create call");
|
||||
call = maybeCall;
|
||||
|
||||
({ widget, messaging } = setUpWidget(call));
|
||||
({ widget, messaging, widgetApi } = setUpWidget(call));
|
||||
|
||||
mocked(messaging.transport).send.mockImplementation(async (action, data): Promise<any> => {
|
||||
mocked(widgetApi.transport).send.mockImplementation(async (action, data): Promise<any> => {
|
||||
if (action === ElementWidgetActions.JoinCall) {
|
||||
messaging.emit(
|
||||
widgetApi.emit(
|
||||
`action:${ElementWidgetActions.JoinCall}`,
|
||||
new CustomEvent("widgetapirequest", { detail: { data } }),
|
||||
);
|
||||
} else if (action === ElementWidgetActions.HangupCall) {
|
||||
messaging.emit(
|
||||
widgetApi.emit(
|
||||
`action:${ElementWidgetActions.HangupCall}`,
|
||||
new CustomEvent("widgetapirequest", { detail: { data } }),
|
||||
);
|
||||
@@ -183,7 +185,7 @@ describe("JitsiCall", () => {
|
||||
|
||||
it("connects", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
});
|
||||
|
||||
@@ -196,27 +198,27 @@ describe("JitsiCall", () => {
|
||||
const startup = call.start();
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, messaging);
|
||||
await startup;
|
||||
await connect(call, messaging, false);
|
||||
await connect(call, widgetApi, false);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
});
|
||||
|
||||
it("fails to disconnect if the widget returns an error", async () => {
|
||||
await connect(call, messaging);
|
||||
mocked(messaging.transport).send.mockRejectedValue(new Error("never!"));
|
||||
await connect(call, widgetApi);
|
||||
mocked(widgetApi.transport).send.mockRejectedValue(new Error("never!"));
|
||||
await expect(call.disconnect()).rejects.toBeDefined();
|
||||
});
|
||||
|
||||
it("handles remote disconnection", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
|
||||
const callback = jest.fn();
|
||||
|
||||
call.on(CallEvent.ConnectionState, callback);
|
||||
|
||||
messaging.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
widgetApi.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
await waitFor(() => {
|
||||
expect(callback).toHaveBeenNthCalledWith(1, ConnectionState.Disconnected, ConnectionState.Connected);
|
||||
});
|
||||
@@ -226,14 +228,14 @@ describe("JitsiCall", () => {
|
||||
|
||||
it("disconnects", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
await call.disconnect();
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
});
|
||||
|
||||
it("disconnects when we leave the room", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
room.emit(RoomEvent.MyMembership, room, KnownMembership.Leave);
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
@@ -241,14 +243,14 @@ describe("JitsiCall", () => {
|
||||
|
||||
it("reconnects after disconnect in video rooms", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
await call.disconnect();
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
});
|
||||
|
||||
it("remains connected if we stay in the room", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
room.emit(RoomEvent.MyMembership, room, KnownMembership.Join);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
@@ -274,7 +276,7 @@ describe("JitsiCall", () => {
|
||||
|
||||
// Now, stub out client.sendStateEvent so we can test our local echo
|
||||
client.sendStateEvent.mockReset();
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.participants).toEqual(
|
||||
new Map([
|
||||
[alice, new Set(["alices_device"])],
|
||||
@@ -287,7 +289,7 @@ describe("JitsiCall", () => {
|
||||
});
|
||||
|
||||
it("updates room state when connecting and disconnecting", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
const now1 = Date.now();
|
||||
await waitFor(
|
||||
() =>
|
||||
@@ -315,7 +317,7 @@ describe("JitsiCall", () => {
|
||||
});
|
||||
|
||||
it("repeatedly updates room state while connected", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(client.sendStateEvent).toHaveBeenLastCalledWith(
|
||||
@@ -345,7 +347,7 @@ describe("JitsiCall", () => {
|
||||
const onConnectionState = jest.fn();
|
||||
call.on(CallEvent.ConnectionState, onConnectionState);
|
||||
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
await call.disconnect();
|
||||
expect(onConnectionState.mock.calls).toEqual([
|
||||
[ConnectionState.Connected, ConnectionState.Disconnected],
|
||||
@@ -360,7 +362,7 @@ describe("JitsiCall", () => {
|
||||
const onParticipants = jest.fn();
|
||||
call.on(CallEvent.Participants, onParticipants);
|
||||
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
await call.disconnect();
|
||||
expect(onParticipants.mock.calls).toEqual([
|
||||
[new Map([[alice, new Set(["alices_device"])]]), new Map()],
|
||||
@@ -373,11 +375,11 @@ describe("JitsiCall", () => {
|
||||
});
|
||||
|
||||
it("switches to spotlight layout when the widget becomes a PiP", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
ActiveWidgetStore.instance.emit(ActiveWidgetStoreEvent.Undock);
|
||||
expect(messaging.transport.send).toHaveBeenCalledWith(ElementWidgetActions.SpotlightLayout, {});
|
||||
expect(widgetApi.transport.send).toHaveBeenCalledWith(ElementWidgetActions.SpotlightLayout, {});
|
||||
ActiveWidgetStore.instance.emit(ActiveWidgetStoreEvent.Dock);
|
||||
expect(messaging.transport.send).toHaveBeenCalledWith(ElementWidgetActions.TileLayout, {});
|
||||
expect(widgetApi.transport.send).toHaveBeenCalledWith(ElementWidgetActions.TileLayout, {});
|
||||
});
|
||||
|
||||
describe("clean", () => {
|
||||
@@ -417,7 +419,7 @@ describe("JitsiCall", () => {
|
||||
});
|
||||
|
||||
it("doesn't clean up valid devices", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
await client.sendStateEvent(
|
||||
room.roomId,
|
||||
JitsiCall.MEMBER_EVENT_TYPE,
|
||||
@@ -796,7 +798,8 @@ describe("ElementCall", () => {
|
||||
describe("instance in a non-video room", () => {
|
||||
let call: ElementCall;
|
||||
let widget: Widget;
|
||||
let messaging: Mocked<ClientWidgetApi>;
|
||||
let messaging: Mocked<WidgetMessaging>;
|
||||
let widgetApi: Mocked<ClientWidgetApi>;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.useFakeTimers();
|
||||
@@ -807,81 +810,128 @@ describe("ElementCall", () => {
|
||||
if (maybeCall === null) throw new Error("Failed to create call");
|
||||
call = maybeCall;
|
||||
|
||||
({ widget, messaging } = setUpWidget(call));
|
||||
({ widget, messaging, widgetApi } = setUpWidget(call));
|
||||
});
|
||||
|
||||
afterEach(() => cleanUpCallAndWidget(call, widget));
|
||||
|
||||
// TODO refactor initial device configuration to use the EW settings.
|
||||
// Add tests for passing EW device configuration to the widget.
|
||||
it("waits for messaging when starting", async () => {
|
||||
|
||||
it("waits for messaging when starting (widget API available immediately)", async () => {
|
||||
// Temporarily remove the messaging to simulate connecting while the
|
||||
// widget is still initializing
|
||||
|
||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
|
||||
const startup = call.start({});
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, messaging);
|
||||
await startup;
|
||||
await connect(call, messaging, false);
|
||||
await connect(call, widgetApi, false);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
});
|
||||
|
||||
it("waits for messaging when starting (widget API started asynchronously)", async () => {
|
||||
// Temporarily remove the messaging to simulate connecting while the
|
||||
// widget is still initializing
|
||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||
// Also remove the widget API from said messaging until later
|
||||
let storedWidgetApi: Mocked<ClientWidgetApi> | null = null;
|
||||
Object.defineProperty(messaging, "widgetApi", {
|
||||
get() {
|
||||
return storedWidgetApi;
|
||||
},
|
||||
});
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
|
||||
const startup = call.start({});
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, messaging);
|
||||
// Yield the event loop to the Call.start promise, then simulate the
|
||||
// widget API being started asynchronously
|
||||
await Promise.resolve();
|
||||
storedWidgetApi = widgetApi;
|
||||
messaging.emit(WidgetMessagingEvent.Start, storedWidgetApi);
|
||||
await startup;
|
||||
await connect(call, widgetApi, false);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
});
|
||||
|
||||
it("waits for messaging when starting (even if messaging is replaced during startup)", async () => {
|
||||
const firstMessaging = messaging;
|
||||
// Entirely remove the widget API from this first messaging
|
||||
Object.defineProperty(firstMessaging, "widgetApi", {
|
||||
get() {
|
||||
return null;
|
||||
},
|
||||
});
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
|
||||
const startup = call.start({});
|
||||
// Now imagine that the messaging gets abandoned and replaced by an
|
||||
// entirely new messaging object
|
||||
({ widget, messaging, widgetApi } = setUpWidget(call));
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, messaging);
|
||||
await startup;
|
||||
await connect(call, widgetApi, false);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
expect(firstMessaging.listenerCount(WidgetMessagingEvent.Start)).toBe(0); // No leaks
|
||||
});
|
||||
|
||||
it("fails to disconnect if the widget returns an error", async () => {
|
||||
await connect(call, messaging);
|
||||
mocked(messaging.transport).send.mockRejectedValue(new Error("never!!1! >:("));
|
||||
await connect(call, widgetApi);
|
||||
mocked(widgetApi.transport).send.mockRejectedValue(new Error("never!!1! >:("));
|
||||
await expect(call.disconnect()).rejects.toBeDefined();
|
||||
});
|
||||
|
||||
it("handles remote disconnection", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
|
||||
messaging.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
messaging.emit(`action:${ElementWidgetActions.Close}`, new CustomEvent("widgetapirequest", {}));
|
||||
widgetApi.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
widgetApi.emit(`action:${ElementWidgetActions.Close}`, new CustomEvent("widgetapirequest", {}));
|
||||
await waitFor(() => expect(call.connectionState).toBe(ConnectionState.Disconnected), { interval: 5 });
|
||||
});
|
||||
|
||||
it("disconnects", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
await disconnect(call, messaging);
|
||||
await disconnect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
});
|
||||
|
||||
it("disconnects when we leave the room", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
room.emit(RoomEvent.MyMembership, room, KnownMembership.Leave);
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
});
|
||||
|
||||
it("remains connected if we stay in the room", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
room.emit(RoomEvent.MyMembership, room, KnownMembership.Join);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
});
|
||||
|
||||
it("disconnects if the widget dies", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
});
|
||||
|
||||
it("acknowledges mute_device widget action", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
const preventDefault = jest.fn();
|
||||
const mockEv = {
|
||||
preventDefault,
|
||||
detail: { video_enabled: false },
|
||||
};
|
||||
messaging.emit(`action:${ElementWidgetActions.DeviceMute}`, mockEv);
|
||||
expect(messaging.transport.reply).toHaveBeenCalledWith({ video_enabled: false }, {});
|
||||
widgetApi.emit(`action:${ElementWidgetActions.DeviceMute}`, mockEv);
|
||||
expect(widgetApi.transport.reply).toHaveBeenCalledWith({ video_enabled: false }, {});
|
||||
expect(preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -890,8 +940,8 @@ describe("ElementCall", () => {
|
||||
const onConnectionState = jest.fn();
|
||||
call.on(CallEvent.ConnectionState, onConnectionState);
|
||||
|
||||
await connect(call, messaging);
|
||||
await disconnect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
await disconnect(call, widgetApi);
|
||||
expect(onConnectionState.mock.calls).toEqual([
|
||||
[ConnectionState.Connected, ConnectionState.Disconnected],
|
||||
[ConnectionState.Disconnecting, ConnectionState.Connected],
|
||||
@@ -913,10 +963,10 @@ describe("ElementCall", () => {
|
||||
});
|
||||
|
||||
it("ends the call immediately if the session ended", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
const onDestroy = jest.fn();
|
||||
call.on(CallEvent.Destroy, onDestroy);
|
||||
await disconnect(call, messaging);
|
||||
await disconnect(call, widgetApi);
|
||||
// this will be called automatically
|
||||
// disconnect -> widget sends state event -> session manager notices no-one left
|
||||
client.matrixRTC.emit(
|
||||
@@ -957,7 +1007,7 @@ describe("ElementCall", () => {
|
||||
describe("instance in a video room", () => {
|
||||
let call: ElementCall;
|
||||
let widget: Widget;
|
||||
let messaging: Mocked<ClientWidgetApi>;
|
||||
let widgetApi: Mocked<ClientWidgetApi>;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.useFakeTimers();
|
||||
@@ -970,29 +1020,29 @@ describe("ElementCall", () => {
|
||||
if (maybeCall === null) throw new Error("Failed to create call");
|
||||
call = maybeCall;
|
||||
|
||||
({ widget, messaging } = setUpWidget(call));
|
||||
({ widget, widgetApi } = setUpWidget(call));
|
||||
});
|
||||
|
||||
afterEach(() => cleanUpCallAndWidget(call, widget));
|
||||
|
||||
it("doesn't end the call when the last participant leaves", async () => {
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
const onDestroy = jest.fn();
|
||||
call.on(CallEvent.Destroy, onDestroy);
|
||||
await disconnect(call, messaging);
|
||||
await disconnect(call, widgetApi);
|
||||
expect(onDestroy).not.toHaveBeenCalled();
|
||||
call.off(CallEvent.Destroy, onDestroy);
|
||||
});
|
||||
|
||||
it("handles remote disconnection and reconnect right after", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
await connect(call, messaging);
|
||||
await connect(call, widgetApi);
|
||||
expect(call.connectionState).toBe(ConnectionState.Connected);
|
||||
|
||||
messaging.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
widgetApi.emit(`action:${ElementWidgetActions.HangupCall}`, new CustomEvent("widgetapirequest", {}));
|
||||
// We should now be able to reconnect without manually starting the widget
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
await connect(call, messaging, false);
|
||||
await connect(call, widgetApi, false);
|
||||
await waitFor(() => expect(call.connectionState).toBe(ConnectionState.Connected), { interval: 5 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user