-
Notifications
You must be signed in to change notification settings - Fork 312
autocomplete: Add user avatars to user-mention autocompletes #590
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,18 +6,25 @@ import 'package:zulip/api/model/model.dart'; | |
import 'package:zulip/api/route/messages.dart'; | ||
import 'package:zulip/model/compose.dart'; | ||
import 'package:zulip/model/narrow.dart'; | ||
import 'package:zulip/model/store.dart'; | ||
import 'package:zulip/widgets/message_list.dart'; | ||
import 'package:zulip/widgets/store.dart'; | ||
|
||
import '../api/fake_api.dart'; | ||
import '../example_data.dart' as eg; | ||
import '../model/binding.dart'; | ||
import '../model/test_store.dart'; | ||
import 'content_test.dart'; | ||
|
||
/// Simulates loading a [MessageListPage] and tapping to focus the compose input. | ||
/// | ||
/// Also adds [users] to the [PerAccountStore], | ||
/// so they can show up in autocomplete. | ||
/// | ||
/// Also sets [debugNetworkImageHttpClientProvider] to return a constant image. | ||
/// | ||
/// The caller must set [debugNetworkImageHttpClientProvider] back to null | ||
/// before the end of the test. | ||
Future<Finder> setupToComposeInput(WidgetTester tester, { | ||
required List<User> users, | ||
}) async { | ||
|
@@ -39,6 +46,8 @@ Future<Finder> setupToComposeInput(WidgetTester tester, { | |
messages: [message], | ||
).toJson()); | ||
|
||
prepareBoringImageHttpClient(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this call, it becomes part of the interface of this (It's regrettable that that reset can't be done using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found a closed issue upstream. This one also seemed closer to this, but wasn't sure to add it to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for looking! Found the one I was thinking of (which it turns out is one I filed 🙂), and added a comment: 68116a2 Looks like there was a backlink to it in the second issue you found: |
||
|
||
await tester.pumpWidget( | ||
MaterialApp( | ||
localizationsDelegates: ZulipLocalizations.localizationsDelegates, | ||
|
@@ -65,10 +74,26 @@ void main() { | |
TestZulipBinding.ensureInitialized(); | ||
|
||
group('ComposeAutocomplete', () { | ||
|
||
Finder findNetworkImage(String url) { | ||
return find.byWidgetPredicate((widget) => switch(widget) { | ||
Image(image: NetworkImage(url: var imageUrl)) when imageUrl == url | ||
=> true, | ||
_ => false, | ||
}); | ||
} | ||
|
||
void checkUserShown(User user, PerAccountStore store, {required bool expected}) { | ||
check(find.text(user.fullName).evaluate().length).equals(expected ? 1 : 0); | ||
final avatarFinder = | ||
findNetworkImage(store.tryResolveUrl(user.avatarUrl!).toString()); | ||
check(avatarFinder.evaluate().length).equals(expected ? 1 : 0); | ||
} | ||
|
||
testWidgets('options appear, disappear, and change correctly', (WidgetTester tester) async { | ||
final user1 = eg.user(userId: 1, fullName: 'User One'); | ||
final user2 = eg.user(userId: 2, fullName: 'User Two'); | ||
final user3 = eg.user(userId: 3, fullName: 'User Three'); | ||
final user1 = eg.user(userId: 1, fullName: 'User One', avatarUrl: 'user1.png'); | ||
final user2 = eg.user(userId: 2, fullName: 'User Two', avatarUrl: 'user2.png'); | ||
final user3 = eg.user(userId: 3, fullName: 'User Three', avatarUrl: 'user3.png'); | ||
final composeInputFinder = await setupToComposeInput(tester, users: [user1, user2, user3]); | ||
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id); | ||
|
||
|
@@ -77,34 +102,37 @@ void main() { | |
await tester.enterText(composeInputFinder, 'hello @user '); | ||
await tester.enterText(composeInputFinder, 'hello @user t'); | ||
await tester.pumpAndSettle(); // async computation; options appear | ||
|
||
// "User Two" and "User Three" appear, but not "User One" | ||
check(tester.widgetList(find.text('User One'))).isEmpty(); | ||
tester.widget(find.text('User Two')); | ||
tester.widget(find.text('User Three')); | ||
checkUserShown(user1, store, expected: false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This version is fine. But for other situations where the shared context is more verbose than just
|
||
checkUserShown(user2, store, expected: true); | ||
checkUserShown(user3, store, expected: true); | ||
|
||
// Finishing autocomplete updates compose box; causes options to disappear | ||
await tester.tap(find.text('User Three')); | ||
await tester.pump(); | ||
check(tester.widget<TextField>(composeInputFinder).controller!.text) | ||
.contains(mention(user3, users: store.users)); | ||
check(tester.widgetList(find.text('User One'))).isEmpty(); | ||
check(tester.widgetList(find.text('User Two'))).isEmpty(); | ||
check(tester.widgetList(find.text('User Three'))).isEmpty(); | ||
checkUserShown(user1, store, expected: false); | ||
checkUserShown(user2, store, expected: false); | ||
checkUserShown(user3, store, expected: false); | ||
|
||
// Then a new autocomplete intent brings up options again | ||
// TODO(#226): Remove this extra edit when this bug is fixed. | ||
await tester.enterText(composeInputFinder, 'hello @user tw'); | ||
await tester.enterText(composeInputFinder, 'hello @user two'); | ||
await tester.pumpAndSettle(); // async computation; options appear | ||
tester.widget(find.text('User Two')); | ||
checkUserShown(user2, store, expected: true); | ||
|
||
// Removing autocomplete intent causes options to disappear | ||
// TODO(#226): Remove one of these edits when this bug is fixed. | ||
await tester.enterText(composeInputFinder, ''); | ||
await tester.enterText(composeInputFinder, ' '); | ||
check(tester.widgetList(find.text('User One'))).isEmpty(); | ||
check(tester.widgetList(find.text('User Two'))).isEmpty(); | ||
check(tester.widgetList(find.text('User Three'))).isEmpty(); | ||
checkUserShown(user1, store, expected: false); | ||
checkUserShown(user2, store, expected: false); | ||
checkUserShown(user3, store, expected: false); | ||
|
||
debugNetworkImageHttpClientProvider = null; | ||
}); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,21 @@ import 'dialog_checks.dart'; | |
import 'message_list_checks.dart'; | ||
import 'page_checks.dart'; | ||
|
||
/// Set [debugNetworkImageHttpClientProvider] to return a constant image. | ||
/// | ||
/// Returns the [FakeImageHttpClient] that handles the requests. | ||
/// | ||
/// The caller must set [debugNetworkImageHttpClientProvider] back to null | ||
/// before the end of the test. | ||
FakeImageHttpClient prepareBoringImageHttpClient() { | ||
sm-sayedi marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: prefix for this commit: f3aebc9 autocomplete [nfc]: Pull should be (And then the "nfc" part means that on top of that, it has only an NFC effect on even the test code.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh and looking again, it's not really about the autocomplete tests in particular, so I'll make it |
||
final httpClient = FakeImageHttpClient(); | ||
debugNetworkImageHttpClientProvider = () => httpClient; | ||
httpClient.request.response | ||
..statusCode = HttpStatus.ok | ||
..content = kSolidBlueAvatar; | ||
return httpClient; | ||
} | ||
|
||
void main() { | ||
// For testing a new content feature: | ||
// | ||
|
@@ -64,21 +79,6 @@ void main() { | |
}); | ||
} | ||
|
||
/// Set [debugNetworkImageHttpClientProvider] to return a constant image. | ||
/// | ||
/// Returns the [FakeImageHttpClient] that handles the requests. | ||
/// | ||
/// The caller must set [debugNetworkImageHttpClientProvider] back to null | ||
/// before the end of the test. | ||
FakeImageHttpClient prepareBoringImageHttpClient() { | ||
final httpClient = FakeImageHttpClient(); | ||
debugNetworkImageHttpClientProvider = () => httpClient; | ||
httpClient.request.response | ||
..statusCode = HttpStatus.ok | ||
..content = kSolidBlueAvatar; | ||
return httpClient; | ||
} | ||
|
||
group('Heading', () { | ||
testWidgets('plain h6', (tester) async { | ||
await prepareContentBare(tester, | ||
|
Uh oh!
There was an error while loading. Please reload this page.