1
+ import 'package:checks/checks.dart' ;
2
+ import 'package:test/scaffolding.dart' ;
3
+ import 'package:zulip/api/model/events.dart' ;
4
+ import 'package:zulip/api/model/model.dart' ;
5
+ import 'package:zulip/api/route/messages.dart' ;
6
+ import 'package:zulip/model/message_list.dart' ;
7
+ import 'package:zulip/model/narrow.dart' ;
8
+ import 'package:zulip/model/store.dart' ;
9
+ import '../api/fake_api.dart' ;
10
+ import '../api/model/model_checks.dart' ;
11
+ import '../model/binding.dart' ;
12
+ import '../model/test_store.dart' ;
13
+ import '../example_data.dart' as eg;
14
+
15
+ const int userId = 1 ;
16
+ const int streamId = 2 ;
17
+
18
+ Future <PerAccountStore > setupStore (ZulipStream stream) async {
19
+ await TestZulipBinding .instance.globalStore.add (eg.selfAccount, eg.initialSnapshot ());
20
+
21
+ final store = await TestZulipBinding .instance.globalStore.perAccount (eg.selfAccount.id);
22
+ store.addUser (eg.user (userId: userId));
23
+ store.addStream (stream);
24
+ return store;
25
+ }
26
+
27
+ Future <MessageListView > messageListViewWithMessages (List <Message > messages, PerAccountStore store, Narrow narrow) async {
28
+ final messageList = MessageListView .init (store: store, narrow: narrow);
29
+
30
+ final connection = store.connection as FakeApiConnection ;
31
+
32
+ connection.prepare (json: GetMessagesResult (
33
+ anchor: messages.first.id,
34
+ foundNewest: true ,
35
+ foundOldest: true ,
36
+ foundAnchor: true ,
37
+ historyLimited: false ,
38
+ messages: messages,
39
+ ).toJson ());
40
+
41
+ await messageList.fetch ();
42
+
43
+ check (messageList.messages.length).equals (messages.length);
44
+
45
+ return messageList;
46
+ }
47
+
48
+ void main () async {
49
+ TestZulipBinding .ensureInitialized ();
50
+
51
+ const narrow = StreamNarrow (streamId);
52
+ final stream = eg.stream (streamId: streamId);
53
+
54
+ group ('update message tests' , () {
55
+
56
+ test ('find message in message list returns index of message' , () async {
57
+ addTearDown (TestZulipBinding .instance.reset);
58
+
59
+ final store = await setupStore (stream);
60
+
61
+ final m1 = eg.streamMessage (id: 792 , stream: stream);
62
+ final m2 = eg.streamMessage (id: 793 , stream: stream);
63
+ final m3 = eg.streamMessage (id: 794 , stream: stream);
64
+
65
+ final messageList = await messageListViewWithMessages ([m1, m2, m3], store, narrow);
66
+
67
+ var idx = messageList.findMessageWithId (793 );
68
+ check (idx).equals (1 );
69
+
70
+ idx = messageList.findMessageWithId (999 );
71
+ check (idx).equals (- 1 );
72
+ });
73
+
74
+ test ('update events are correctly applied to message when it is in the stream' , () async {
75
+ addTearDown (TestZulipBinding .instance.reset);
76
+ final store = await setupStore (stream);
77
+
78
+ const oldContent = "<p>Hello, world</p>" ;
79
+ const newContent = "<p>Hello, edited</p>" ;
80
+ const newTimestamp = 99999 ;
81
+
82
+ List <String > oldFlags = [];
83
+ List <String > newFlags = ["starred" ];
84
+
85
+ final mockMessage = eg.streamMessage (id: 243 , stream: stream, content: oldContent, flags: oldFlags);
86
+ final messageList = await messageListViewWithMessages ([mockMessage], store, narrow);
87
+
88
+ final updateEvent = UpdateMessageEvent (
89
+ id: 1 ,
90
+ messageId: mockMessage.id,
91
+ messageIds: [mockMessage.id],
92
+ flags: newFlags,
93
+ renderedContent: newContent,
94
+ editTimestamp: newTimestamp,
95
+ isMeMessage: true ,
96
+ userId: userId
97
+ );
98
+
99
+ final message = messageList.messages[0 ];
100
+ check (message)
101
+ ..content.equals (oldContent)
102
+ ..flags.deepEquals (oldFlags)
103
+ ..isMeMessage.equals (false );
104
+
105
+ var listenersNotified = false ;
106
+
107
+ messageList.addListener (() { listenersNotified = true ; });
108
+ messageList.maybeUpdateMessage (updateEvent);
109
+
110
+ final updatedMessage = messageList.messages[0 ];
111
+ check (updatedMessage).identicalTo (message);
112
+ check (listenersNotified).equals (true );
113
+
114
+ check (message)
115
+ ..content.equals (newContent)
116
+ ..lastEditTimestamp.equals (newTimestamp)
117
+ ..flags.equals (newFlags)
118
+ ..isMeMessage.equals (true );
119
+ });
120
+
121
+ test ('update event is ignored when message is not in the message list' , () async {
122
+ addTearDown (TestZulipBinding .instance.reset);
123
+ final store = await setupStore (stream);
124
+
125
+ const oldContent = "<p>Hello, world</p>" ;
126
+ const newContent = "<p>Hello, edited</p>" ;
127
+ const newTimestamp = 99999 ;
128
+
129
+ final mockMessage = eg.streamMessage (id: 243 , stream: stream, content: oldContent);
130
+ final messageList = await messageListViewWithMessages ([mockMessage], store, narrow);
131
+
132
+ final updateEvent = UpdateMessageEvent (
133
+ id: 1 ,
134
+ messageId: 972 ,
135
+ messageIds: [972 ],
136
+ flags: mockMessage.flags,
137
+ renderedContent: newContent,
138
+ editTimestamp: newTimestamp,
139
+ userId: userId,
140
+ );
141
+
142
+ final message = messageList.messages[0 ];
143
+ check (message).content.equals (oldContent);
144
+
145
+ var listenersNotified = false ;
146
+
147
+ messageList.addListener (() { listenersNotified = true ; });
148
+ messageList.maybeUpdateMessage (updateEvent);
149
+
150
+ check (listenersNotified).equals (false );
151
+ check (message).content.equals (oldContent);
152
+
153
+ });
154
+ test ('rendering-only update does not change timestamp' , () async {
155
+ addTearDown (TestZulipBinding .instance.reset);
156
+ final store = await setupStore (stream);
157
+
158
+ const oldContent = "<p>Hello, world</p>" ;
159
+ const newContent = "<p>Hello, world</p> <div>Some link preview</div>" ;
160
+ const newTimestamp = 99999 ;
161
+
162
+ final mockMessage = eg.streamMessage (id: 972 , stream: stream, content: oldContent);
163
+ final messageList = await messageListViewWithMessages ([mockMessage], store, narrow);
164
+
165
+ final updateEvent = UpdateMessageEvent (
166
+ id: 1 ,
167
+ messageId: 972 ,
168
+ messageIds: [972 ],
169
+ flags: mockMessage.flags,
170
+ renderedContent: newContent,
171
+ editTimestamp: newTimestamp,
172
+ renderingOnly: true ,
173
+ userId: null ,
174
+ );
175
+
176
+ final message = messageList.messages[0 ];
177
+ messageList.maybeUpdateMessage (updateEvent);
178
+ check (message)
179
+ ..content.equals (newContent)
180
+ ..lastEditTimestamp.isNull ();
181
+ });
182
+
183
+ test ('rendering-only update does not change timestamp (for old server versions)' , () async {
184
+ addTearDown (TestZulipBinding .instance.reset);
185
+ final store = await setupStore (stream);
186
+
187
+ const oldContent = "<p>Hello, world</p>" ;
188
+ const newContent = "<p>Hello, world</p> <div>Some link preview</div>" ;
189
+ const newTimestamp = 99999 ;
190
+
191
+ final mockMessage = eg.streamMessage (id: 972 , stream: stream, content: oldContent);
192
+ final messageList = await messageListViewWithMessages ([mockMessage], store, narrow);
193
+
194
+ final updateEvent = UpdateMessageEvent (
195
+ id: 1 ,
196
+ messageId: 972 ,
197
+ messageIds: [972 ],
198
+ flags: mockMessage.flags,
199
+ renderedContent: newContent,
200
+ editTimestamp: newTimestamp,
201
+ userId: null ,
202
+ );
203
+
204
+ final message = messageList.messages[0 ];
205
+ messageList.maybeUpdateMessage (updateEvent);
206
+ check (message)
207
+ ..content.equals (newContent)
208
+ ..lastEditTimestamp.isNull ();
209
+ });
210
+
211
+
212
+ });
213
+ }
0 commit comments