11import 'package:checks/checks.dart' ;
22import 'package:flutter/material.dart' ;
3+ import 'package:flutter_checks/flutter_checks.dart' ;
34import 'package:flutter_test/flutter_test.dart' ;
5+ import 'package:zulip/api/model/events.dart' ;
46import 'package:zulip/api/model/model.dart' ;
57import 'package:zulip/api/route/messages.dart' ;
68import 'package:zulip/api/route/channels.dart' ;
9+ import 'package:zulip/api/route/realm.dart' ;
710import 'package:zulip/model/compose.dart' ;
11+ import 'package:zulip/model/emoji.dart' ;
812import 'package:zulip/model/localizations.dart' ;
913import 'package:zulip/model/narrow.dart' ;
1014import 'package:zulip/model/store.dart' ;
@@ -28,7 +32,7 @@ import 'test_app.dart';
2832/// The caller must set [debugNetworkImageHttpClientProvider] back to null
2933/// before the end of the test.
3034Future <Finder > setupToComposeInput (WidgetTester tester, {
31- required List <User > users,
35+ List <User > users = const [] ,
3236}) async {
3337 TypingNotifier .debugEnable = false ;
3438 addTearDown (TypingNotifier .debugReset);
@@ -108,19 +112,20 @@ Future<Finder> setupToTopicInput(WidgetTester tester, {
108112 return finder;
109113}
110114
111- void main () {
112- TestZulipBinding .ensureInitialized ();
115+ Finder findNetworkImage (String url) {
116+ return find.byWidgetPredicate ((widget) => switch (widget) {
117+ Image (image: NetworkImage (url: var imageUrl)) when imageUrl == url
118+ => true ,
119+ _ => false ,
120+ });
121+ }
113122
114- group ( 'ComposeAutocomplete' , () {
123+ typedef ExpectedEmoji = ( String label, EmojiDisplay display);
115124
116- Finder findNetworkImage (String url) {
117- return find.byWidgetPredicate ((widget) => switch (widget) {
118- Image (image: NetworkImage (url: var imageUrl)) when imageUrl == url
119- => true ,
120- _ => false ,
121- });
122- }
125+ void main () {
126+ TestZulipBinding .ensureInitialized ();
123127
128+ group ('@-mentions' , () {
124129 void checkUserShown (User user, PerAccountStore store, {required bool expected}) {
125130 check (find.text (user.fullName).evaluate ().length).equals (expected ? 1 : 0 );
126131 final avatarFinder =
@@ -174,6 +179,98 @@ void main() {
174179 });
175180 });
176181
182+ group ('emoji' , () {
183+ void checkEmojiShown (ExpectedEmoji option, {required bool expected}) {
184+ final (label, display) = option;
185+ final labelSubject = check (find.text (label));
186+ expected ? labelSubject.findsOne () : labelSubject.findsNothing ();
187+
188+ final Subject <Finder > displaySubject;
189+ switch (display) {
190+ case UnicodeEmojiDisplay ():
191+ displaySubject = check (find.text (display.emojiUnicode));
192+ case ImageEmojiDisplay ():
193+ displaySubject = check (findNetworkImage (display.resolvedUrl.toString ()));
194+ case TextEmojiDisplay ():
195+ // We test this case in the "text emoji" test below,
196+ // but that doesn't use this helper method.
197+ throw UnimplementedError ();
198+ }
199+ expected ? displaySubject.findsOne (): displaySubject.findsNothing ();
200+ }
201+
202+ testWidgets ('show, update, choose' , (tester) async {
203+ final composeInputFinder = await setupToComposeInput (tester);
204+ final store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
205+ store.setServerEmojiData (ServerEmojiData (codeToNames: {
206+ '1f4a4' : ['zzz' , 'sleepy' ], // (just 'zzz' in real data)
207+ }));
208+ await store.handleEvent (RealmEmojiUpdateEvent (id: 1 , realmEmoji: {
209+ '1' : eg.realmEmojiItem (emojiCode: '1' , emojiName: 'buzzing' ),
210+ }));
211+
212+ final zulipOption = ('zulip' , store.emojiDisplayFor (
213+ emojiType: ReactionType .zulipExtraEmoji,
214+ emojiCode: 'zulip' , emojiName: 'zulip' ));
215+ final buzzingOption = ('buzzing' , store.emojiDisplayFor (
216+ emojiType: ReactionType .realmEmoji,
217+ emojiCode: '1' , emojiName: 'buzzing' ));
218+ final zzzOption = ('zzz, sleepy' , store.emojiDisplayFor (
219+ emojiType: ReactionType .unicodeEmoji,
220+ emojiCode: '1f4a4' , emojiName: 'zzz' ));
221+
222+ // Enter a query; options appear, of all three emoji types.
223+ // TODO(#226): Remove this extra edit when this bug is fixed.
224+ await tester.enterText (composeInputFinder, 'hi :' );
225+ await tester.enterText (composeInputFinder, 'hi :z' );
226+ await tester.pump ();
227+ checkEmojiShown (expected: true , zzzOption);
228+ checkEmojiShown (expected: true , buzzingOption);
229+ checkEmojiShown (expected: true , zulipOption);
230+
231+ // Edit query; options change.
232+ await tester.enterText (composeInputFinder, 'hi :zz' );
233+ await tester.pump ();
234+ checkEmojiShown (expected: true , zzzOption);
235+ checkEmojiShown (expected: true , buzzingOption);
236+ checkEmojiShown (expected: false , zulipOption);
237+
238+ // Choosing an option enters result and closes autocomplete.
239+ await tester.tap (find.text ('buzzing' ));
240+ await tester.pump ();
241+ check (tester.widget <TextField >(composeInputFinder).controller! .text)
242+ .equals ('hi :buzzing:' );
243+ checkEmojiShown (expected: false , zzzOption);
244+ checkEmojiShown (expected: false , buzzingOption);
245+ checkEmojiShown (expected: false , zulipOption);
246+
247+ debugNetworkImageHttpClientProvider = null ;
248+ });
249+
250+ testWidgets ('text emoji means just show text' , (tester) async {
251+ final composeInputFinder = await setupToComposeInput (tester);
252+ final store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
253+ await store.handleEvent (UserSettingsUpdateEvent (id: 1 ,
254+ property: UserSettingName .emojiset, value: Emojiset .text));
255+
256+ // TODO(#226): Remove this extra edit when this bug is fixed.
257+ await tester.enterText (composeInputFinder, 'hi :' );
258+ await tester.enterText (composeInputFinder, 'hi :z' );
259+ await tester.pump ();
260+
261+ // The emoji's name appears. (And only once.)
262+ check (find.text ('zulip' )).findsOne ();
263+
264+ // But no emoji image appears.
265+ check (find.byWidgetPredicate ((widget) => switch (widget) {
266+ Image (image: NetworkImage ()) => true ,
267+ _ => false ,
268+ })).findsNothing ();
269+
270+ debugNetworkImageHttpClientProvider = null ;
271+ });
272+ });
273+
177274 group ('TopicAutocomplete' , () {
178275 void checkTopicShown (GetStreamTopicsEntry topic, PerAccountStore store, {required bool expected}) {
179276 check (find.text (topic.name).evaluate ().length).equals (expected ? 1 : 0 );
0 commit comments