@@ -48,7 +48,7 @@ import { Anonymity, PosthogAnalytics } from "../../../src/PosthogAnalytics";
4848import { type SettingKey } from "../../../src/settings/Settings.tsx" ;
4949import SdkConfig from "../../../src/SdkConfig.ts" ;
5050import DMRoomMap from "../../../src/utils/DMRoomMap.ts" ;
51- import { type WidgetMessaging } from "../../../src/stores/widgets/WidgetMessaging.ts" ;
51+ import { WidgetMessagingEvent , type WidgetMessaging } from "../../../src/stores/widgets/WidgetMessaging.ts" ;
5252
5353const { enabledSettings } = enableCalls ( ) ;
5454
@@ -724,20 +724,67 @@ describe("ElementCall", () => {
724724 } ) ;
725725
726726 afterEach ( ( ) => cleanUpCallAndWidget ( call , widget ) ) ;
727+
727728 // TODO refactor initial device configuration to use the EW settings.
728729 // Add tests for passing EW device configuration to the widget.
729- it ( "waits for messaging when starting" , async ( ) => {
730+
731+ it ( "waits for messaging when starting (widget API available immediately)" , async ( ) => {
730732 // Temporarily remove the messaging to simulate connecting while the
731733 // widget is still initializing
734+ WidgetMessagingStore . instance . stopMessaging ( widget , room . roomId ) ;
735+ expect ( call . connectionState ) . toBe ( ConnectionState . Disconnected ) ;
736+
737+ const startup = call . start ( { } ) ;
738+ WidgetMessagingStore . instance . storeMessaging ( widget , room . roomId , messaging ) ;
739+ await startup ;
740+ await connect ( call , widgetApi , false ) ;
741+ expect ( call . connectionState ) . toBe ( ConnectionState . Connected ) ;
742+ } ) ;
732743
744+ it ( "waits for messaging when starting (widget API started asynchronously)" , async ( ) => {
745+ // Temporarily remove the messaging to simulate connecting while the
746+ // widget is still initializing
733747 WidgetMessagingStore . instance . stopMessaging ( widget , room . roomId ) ;
748+ // Also remove the widget API from said messaging until later
749+ let storedWidgetApi : Mocked < ClientWidgetApi > | null = null ;
750+ Object . defineProperty ( messaging , "widgetApi" , {
751+ get ( ) {
752+ return storedWidgetApi ;
753+ } ,
754+ } ) ;
755+ expect ( call . connectionState ) . toBe ( ConnectionState . Disconnected ) ;
756+
757+ const startup = call . start ( { } ) ;
758+ WidgetMessagingStore . instance . storeMessaging ( widget , room . roomId , messaging ) ;
759+ // Yield the event loop to the Call.start promise, then simulate the
760+ // widget API being started asynchronously
761+ await Promise . resolve ( ) ;
762+ storedWidgetApi = widgetApi ;
763+ messaging . emit ( WidgetMessagingEvent . Start , storedWidgetApi ) ;
764+ await startup ;
765+ await connect ( call , widgetApi , false ) ;
766+ expect ( call . connectionState ) . toBe ( ConnectionState . Connected ) ;
767+ } ) ;
768+
769+ it ( "waits for messaging when starting (even if messaging is replaced during startup)" , async ( ) => {
770+ const firstMessaging = messaging ;
771+ // Entirely remove the widget API from this first messaging
772+ Object . defineProperty ( firstMessaging , "widgetApi" , {
773+ get ( ) {
774+ return null ;
775+ } ,
776+ } ) ;
734777 expect ( call . connectionState ) . toBe ( ConnectionState . Disconnected ) ;
735778
736779 const startup = call . start ( { } ) ;
780+ // Now imagine that the messaging gets abandoned and replaced by an
781+ // entirely new messaging object
782+ ( { widget, messaging, widgetApi } = setUpWidget ( call ) ) ;
737783 WidgetMessagingStore . instance . storeMessaging ( widget , room . roomId , messaging ) ;
738784 await startup ;
739785 await connect ( call , widgetApi , false ) ;
740786 expect ( call . connectionState ) . toBe ( ConnectionState . Connected ) ;
787+ expect ( firstMessaging . listenerCount ( WidgetMessagingEvent . Start ) ) . toBe ( 0 ) ; // No leaks
741788 } ) ;
742789
743790 it ( "fails to disconnect if the widget returns an error" , async ( ) => {
0 commit comments