@@ -17,8 +17,8 @@ function generateMockLogs(
17
17
) : readonly WorkspaceAgentLog [ ] {
18
18
return Array . from ( { length : logCount } , ( _ , i ) => {
19
19
// Make sure that the logs generated each have unique timestamps, so
20
- // that we can test whether they're being sorted properly before being
21
- // returned by the hook
20
+ // that we can test whether the hook is sorting them properly as it's
21
+ // receiving them over time
22
22
const logDate = new Date ( baseDate . getTime ( ) + i * millisecondsInOneMinute ) ;
23
23
return {
24
24
id : i ,
@@ -32,25 +32,22 @@ function generateMockLogs(
32
32
33
33
// A mutable object holding the most recent mock WebSocket publisher. The inner
34
34
// value will change as the hook opens/closes new connections
35
- type PublisherResult = {
36
- current : MockWebSocketPublisher ;
37
- } ;
35
+ type PublisherResult = { current : MockWebSocketPublisher ; } ;
38
36
39
37
type MountHookResult = Readonly < {
40
- // Note: the value of `current` should be readonly, but the `current`
41
- // property itself should be mutable
42
- hookResult : {
43
- current : readonly WorkspaceAgentLog [ ] ;
44
- } ;
45
- rerender : ( props : { enabled : boolean } ) => void ;
38
+ rerender : ( props : { agentId : string , enabled : boolean } ) => void ;
46
39
publisherResult : PublisherResult ;
40
+
41
+ // Note: the `current` property is only "halfway" readonly; the value is
42
+ // readonly, but the key is still mutable
43
+ hookResult : { current : readonly WorkspaceAgentLog [ ] ; } ;
47
44
} > ;
48
45
49
- function mountHook ( ) : MountHookResult {
46
+ function mountHook ( initialAgentId : string ) : MountHookResult {
50
47
// Have to cheat the types a little bit to avoid a chicken-and-the-egg
51
48
// scenario. publisherResult will be initialized with an undefined current
52
- // value, but it'll be guaranteed not to be undefined by the time this
53
- // function returns.
49
+ // value, but it'll be guaranteed not to be undefined by the time mountHook
50
+ // returns its value .
54
51
const publisherResult : Partial < PublisherResult > = { current : undefined } ;
55
52
const useAgentLogs = createUseAgentLogs ( ( agentId , params ) => {
56
53
return new OneWayWebSocket ( {
@@ -68,8 +65,8 @@ function mountHook(): MountHookResult {
68
65
} ) ;
69
66
70
67
const { result, rerender } = renderHook (
71
- ( { enabled } ) => useAgentLogs ( MockWorkspaceAgent , enabled ) ,
72
- { initialProps : { enabled : true } } ,
68
+ ( { agentId , enabled } ) => useAgentLogs ( agentId , enabled ) ,
69
+ { initialProps : { agentId : initialAgentId , enabled : true } , } ,
73
70
) ;
74
71
75
72
return {
@@ -81,10 +78,11 @@ function mountHook(): MountHookResult {
81
78
82
79
describe ( "useAgentLogs" , ( ) => {
83
80
it ( "Automatically sorts logs that are received out of order" , async ( ) => {
84
- const { hookResult, publisherResult } = mountHook ( ) ;
81
+ const { hookResult, publisherResult } = mountHook ( MockWorkspaceAgent . id ) ;
85
82
const logs = generateMockLogs ( 10 , new Date ( "september 9, 1999" ) ) ;
83
+ const reversed = logs . toReversed ( ) ;
86
84
87
- for ( const log of logs . toReversed ( ) ) {
85
+ for ( const log of reversed ) {
88
86
act ( ( ) => {
89
87
publisherResult . current . publishMessage (
90
88
new MessageEvent < string > ( "message" , {
@@ -97,16 +95,30 @@ describe("useAgentLogs", () => {
97
95
} ) ;
98
96
99
97
it ( "Automatically closes the socket connection when the hook is disabled" , async ( ) => {
100
- const { publisherResult, rerender } = mountHook ( ) ;
98
+ const { publisherResult, rerender } = mountHook ( MockWorkspaceAgent . id ) ;
101
99
expect ( publisherResult . current . isConnectionOpen ( ) ) . toBe ( true ) ;
102
- rerender ( { enabled : false } ) ;
100
+ rerender ( { agentId : MockWorkspaceAgent . id , enabled : false } ) ;
103
101
await waitFor ( ( ) => {
104
102
expect ( publisherResult . current . isConnectionOpen ( ) ) . toBe ( false ) ;
105
103
} )
106
104
} ) ;
107
105
106
+ it ( "Automatically closes the old connection when the agent ID changes" , ( ) => {
107
+ const { publisherResult, rerender } = mountHook ( MockWorkspaceAgent . id ) ;
108
+ const publisher1 = publisherResult . current ;
109
+ expect ( publisher1 . isConnectionOpen ( ) ) . toBe ( true ) ;
110
+
111
+ const newAgentId = `${ MockWorkspaceAgent . id } -2` ;
112
+ rerender ( { agentId : newAgentId , enabled : true } ) ;
113
+
114
+ const publisher2 = publisherResult . current ;
115
+ expect ( publisher1 . isConnectionOpen ( ) ) . toBe ( false ) ;
116
+ expect ( publisher2 . isConnectionOpen ( ) ) . toBe ( true ) ;
117
+ } ) ;
118
+
108
119
it ( "Clears logs when hook becomes disabled (protection to avoid duplicate logs when hook goes back to being re-enabled)" , async ( ) => {
109
- const { hookResult, publisherResult, rerender } = mountHook ( ) ;
120
+ const { hookResult, publisherResult, rerender }
121
+ = mountHook ( MockWorkspaceAgent . id ) ;
110
122
111
123
// Send initial logs so that we have something to clear out later
112
124
const initialLogs = generateMockLogs ( 3 , new Date ( "april 5, 1997" ) ) ;
@@ -118,12 +130,12 @@ describe("useAgentLogs", () => {
118
130
119
131
// Disable the hook (and have the hook close the connection behind the
120
132
// scenes)
121
- rerender ( { enabled : false } ) ;
133
+ rerender ( { agentId : MockWorkspaceAgent . id , enabled : false } ) ;
122
134
await waitFor ( ( ) => expect ( hookResult . current ) . toHaveLength ( 0 ) ) ;
123
135
124
136
// Re-enable the hook (creating an entirely new connection), and send
125
137
// new logs
126
- rerender ( { enabled : true } ) ;
138
+ rerender ( { agentId : MockWorkspaceAgent . id , enabled : true } ) ;
127
139
const newLogs = generateMockLogs ( 3 , new Date ( "october 3, 2005" ) ) ;
128
140
const newEvent = new MessageEvent < string > ( "message" , {
129
141
data : JSON . stringify ( newLogs ) ,
0 commit comments