test: add tests for stream chat flutter (#335)
* feat: Added tests * feat: Added header test * feat: Added new tests * fix * feat: Added test, fmt * feat: Added delete test * added system and unread ind tests * fix: analysis * added new tests * fix: analysis * added tests * add attachment actions modal tests * fix analysis * fix analysis * add backbutton tests * fix analysis * increase timeout * add channelheader tests * add infotile tests * add reactions modal and channel unread indicator tests * add golden test for reaction bubble * add dark tests * add system message golden * add deletd message golden * add message action modal tests * update goldens * update workflow runner * update goldens * remove channel unread indicator in favor of unread indicator * move file and media screen to sample app * add channel image tesst * add channel_list_header test * add thread_header test * bump up test threashold * fix review Co-authored-by: Salvatore Giordano <salvatoregiordanoo@gmail.com>
@@ -58,7 +58,7 @@ jobs:
|
||||
./.github/workflows/scripts/validate-formatting.sh
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -102,4 +102,4 @@ jobs:
|
||||
- uses: VeryGoodOpenSource/very_good_coverage@v1.1.1
|
||||
with:
|
||||
path: packages/stream_chat_flutter/coverage/lcov.info
|
||||
min_coverage: 16
|
||||
min_coverage: 35
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:stream_chat/src/models/action.dart';
|
||||
import 'package:stream_chat/src/models/attachment_file.dart';
|
||||
@@ -10,7 +11,7 @@ part 'attachment.g.dart';
|
||||
|
||||
/// The class that contains the information about an attachment
|
||||
@JsonSerializable(includeIfNull: false)
|
||||
class Attachment {
|
||||
class Attachment extends Equatable {
|
||||
/// Constructor used for json serialization
|
||||
Attachment({
|
||||
String id,
|
||||
@@ -37,12 +38,11 @@ class Attachment {
|
||||
UploadState uploadState,
|
||||
}) : id = id ?? Uuid().v4(),
|
||||
title = title ?? file?.name,
|
||||
localUri = file?.path != null ? Uri.parse(file.path) : null {
|
||||
this.uploadState = uploadState ??
|
||||
((assetUrl != null || imageUrl != null)
|
||||
? const UploadState.success()
|
||||
: const UploadState.preparing());
|
||||
}
|
||||
localUri = file?.path != null ? Uri.parse(file.path) : null,
|
||||
uploadState = uploadState ??
|
||||
((assetUrl != null || imageUrl != null)
|
||||
? const UploadState.success()
|
||||
: const UploadState.preparing());
|
||||
|
||||
/// Create a new instance from a json
|
||||
factory Attachment.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -104,7 +104,7 @@ class Attachment {
|
||||
final AttachmentFile file;
|
||||
|
||||
/// The current upload state of the attachment
|
||||
UploadState uploadState;
|
||||
final UploadState uploadState;
|
||||
|
||||
/// Map of custom channel extraData
|
||||
@JsonKey(includeIfNull: false)
|
||||
@@ -205,49 +205,28 @@ class Attachment {
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is Attachment &&
|
||||
runtimeType == other.runtimeType &&
|
||||
type == other.type &&
|
||||
titleLink == other.titleLink &&
|
||||
title == other.title &&
|
||||
thumbUrl == other.thumbUrl &&
|
||||
text == other.text &&
|
||||
pretext == other.pretext &&
|
||||
ogScrapeUrl == other.ogScrapeUrl &&
|
||||
imageUrl == other.imageUrl &&
|
||||
footerIcon == other.footerIcon &&
|
||||
footer == other.footer &&
|
||||
fields == other.fields &&
|
||||
fallback == other.fallback &&
|
||||
color == other.color &&
|
||||
authorName == other.authorName &&
|
||||
authorLink == other.authorLink &&
|
||||
authorIcon == other.authorIcon &&
|
||||
assetUrl == other.assetUrl &&
|
||||
actions == other.actions &&
|
||||
extraData == other.extraData;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
type.hashCode ^
|
||||
titleLink.hashCode ^
|
||||
title.hashCode ^
|
||||
thumbUrl.hashCode ^
|
||||
text.hashCode ^
|
||||
pretext.hashCode ^
|
||||
ogScrapeUrl.hashCode ^
|
||||
imageUrl.hashCode ^
|
||||
footerIcon.hashCode ^
|
||||
footer.hashCode ^
|
||||
fields.hashCode ^
|
||||
fallback.hashCode ^
|
||||
color.hashCode ^
|
||||
authorName.hashCode ^
|
||||
authorLink.hashCode ^
|
||||
authorIcon.hashCode ^
|
||||
assetUrl.hashCode ^
|
||||
actions.hashCode ^
|
||||
extraData.hashCode;
|
||||
List<Object> get props => [
|
||||
id,
|
||||
type,
|
||||
titleLink,
|
||||
title,
|
||||
thumbUrl,
|
||||
text,
|
||||
pretext,
|
||||
ogScrapeUrl,
|
||||
imageUrl,
|
||||
footerIcon,
|
||||
footer,
|
||||
fields,
|
||||
fallback,
|
||||
color,
|
||||
authorName,
|
||||
authorLink,
|
||||
authorIcon,
|
||||
assetUrl,
|
||||
actions,
|
||||
file,
|
||||
uploadState,
|
||||
extraData,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:stream_chat/src/models/attachment.dart';
|
||||
import 'package:stream_chat/src/models/reaction.dart';
|
||||
@@ -41,7 +42,7 @@ enum MessageSendingStatus {
|
||||
|
||||
/// The class that contains the information about a message
|
||||
@JsonSerializable()
|
||||
class Message {
|
||||
class Message extends Equatable {
|
||||
/// Constructor used for json serialization
|
||||
Message({
|
||||
String id,
|
||||
@@ -345,6 +346,39 @@ class Message {
|
||||
pinnedBy: other.pinnedBy,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [
|
||||
id,
|
||||
text,
|
||||
type,
|
||||
attachments,
|
||||
mentionedUsers,
|
||||
reactionCounts,
|
||||
reactionScores,
|
||||
latestReactions,
|
||||
ownReactions,
|
||||
parentId,
|
||||
quotedMessage,
|
||||
quotedMessageId,
|
||||
replyCount,
|
||||
threadParticipants,
|
||||
showInChannel,
|
||||
shadowed,
|
||||
silent,
|
||||
command,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
deletedAt,
|
||||
user,
|
||||
pinned,
|
||||
pinnedAt,
|
||||
pinExpires,
|
||||
pinnedBy,
|
||||
extraData,
|
||||
status,
|
||||
skipPush,
|
||||
];
|
||||
}
|
||||
|
||||
/// A translated message
|
||||
|
||||
@@ -15,6 +15,7 @@ export './src/attachment_file_uploader.dart' show AttachmentFileUploader;
|
||||
export './src/client.dart';
|
||||
export './src/db/chat_persistence_client.dart';
|
||||
export './src/event_type.dart';
|
||||
export './src/exceptions.dart';
|
||||
export './src/extensions/rate_limit.dart';
|
||||
export './src/extensions/string_extension.dart';
|
||||
export './src/models/action.dart';
|
||||
|
||||
@@ -12,6 +12,7 @@ dependencies:
|
||||
async: ^2.4.2
|
||||
collection: ^1.14.13
|
||||
dio: ^3.0.10
|
||||
equatable: ^2.0.0
|
||||
freezed_annotation: ^0.12.0
|
||||
http_parser: ^3.1.4
|
||||
json_annotation: ^3.0.1
|
||||
|
||||
@@ -62,4 +62,7 @@ doc/api/
|
||||
fvm
|
||||
google-services.json
|
||||
example/ios/dist
|
||||
.vscode/
|
||||
.vscode/
|
||||
|
||||
# don't check in golden failure output
|
||||
**/failures/*.png
|
||||
@@ -34,12 +34,12 @@ class AttachmentActionsModal extends StatelessWidget {
|
||||
|
||||
/// Returns a new [AttachmentActionsModal]
|
||||
const AttachmentActionsModal({
|
||||
@required this.currentIndex,
|
||||
this.message,
|
||||
this.currentIndex,
|
||||
this.onShowMessage,
|
||||
this.imageDownloader,
|
||||
this.fileDownloader,
|
||||
});
|
||||
}) : assert(currentIndex != null, 'currentIndex cannot be null');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -145,19 +145,20 @@ class AttachmentActionsModal extends StatelessWidget {
|
||||
() {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
if (message.attachments.length > 1 ||
|
||||
message.text.isNotEmpty) {
|
||||
message.text?.isNotEmpty == true) {
|
||||
final remainingAttachments = [...message.attachments]
|
||||
..removeAt(currentIndex);
|
||||
channel.updateMessage(message.copyWith(
|
||||
attachments: remainingAttachments,
|
||||
));
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context)
|
||||
..pop()
|
||||
..maybePop();
|
||||
} else {
|
||||
channel.deleteMessage(message).then((value) {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
});
|
||||
channel.deleteMessage(message);
|
||||
Navigator.of(context)
|
||||
..pop()
|
||||
..maybePop();
|
||||
}
|
||||
},
|
||||
color: theme.colorTheme.accentRed,
|
||||
@@ -185,8 +186,10 @@ class AttachmentActionsModal extends StatelessWidget {
|
||||
StreamSvgIcon icon,
|
||||
VoidCallback onTap, {
|
||||
Color color,
|
||||
Key key,
|
||||
}) {
|
||||
return Material(
|
||||
key: key,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
@@ -224,7 +227,7 @@ class AttachmentActionsModal extends StatelessWidget {
|
||||
if (progress == null || progress?.toProgressIndicatorValue == 1.0) {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 500),
|
||||
Navigator.of(context).pop,
|
||||
Navigator.of(context).maybePop,
|
||||
);
|
||||
}
|
||||
return Material(
|
||||
@@ -248,6 +251,7 @@ class AttachmentActionsModal extends StatelessWidget {
|
||||
)
|
||||
: progress.toProgressIndicatorValue == 1.0
|
||||
? Container(
|
||||
key: Key('completedIcon'),
|
||||
height: 160,
|
||||
width: 160,
|
||||
child: StreamSvgIcon.check(
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'attachment/attachment.dart';
|
||||
|
||||
class ChannelFileDisplayScreen extends StatefulWidget {
|
||||
/// The sorting used for the channels matching the filters.
|
||||
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
||||
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
||||
/// Direction can be ascending or descending.
|
||||
final List<SortOption> sortOptions;
|
||||
|
||||
/// Pagination parameters
|
||||
/// limit: the number of users to return (max is 30)
|
||||
/// offset: the offset (max is 1000)
|
||||
/// message_limit: how many messages should be included to each channel
|
||||
final PaginationParams paginationParams;
|
||||
|
||||
/// The builder used when the file list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
const ChannelFileDisplayScreen({
|
||||
this.sortOptions,
|
||||
this.paginationParams,
|
||||
this.emptyBuilder,
|
||||
});
|
||||
|
||||
@override
|
||||
_ChannelFileDisplayScreenState createState() =>
|
||||
_ChannelFileDisplayScreenState();
|
||||
}
|
||||
|
||||
class _ChannelFileDisplayScreenState extends State<ChannelFileDisplayScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
||||
messageSearchBloc.search(
|
||||
filter: {
|
||||
'cid': {
|
||||
r'$in': [StreamChannel.of(context).channel.cid]
|
||||
}
|
||||
},
|
||||
messageFilter: {
|
||||
'attachments.type': {
|
||||
r'$in': ['file'],
|
||||
},
|
||||
},
|
||||
sort: widget.sortOptions,
|
||||
pagination: widget.paginationParams,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
'Files',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16.0),
|
||||
),
|
||||
leading: Center(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
width: 24.0,
|
||||
height: 24.0,
|
||||
child: StreamSvgIcon.left(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
body: _buildMediaGrid(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMediaGrid() {
|
||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
||||
|
||||
return StreamBuilder<List<GetMessageResponse>>(
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == null) {
|
||||
return Center(
|
||||
child: const CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.data.isEmpty) {
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder(context);
|
||||
}
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
StreamSvgIcon.files(
|
||||
size: 136.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
),
|
||||
SizedBox(height: 16.0),
|
||||
Text(
|
||||
'No Files',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0),
|
||||
Text(
|
||||
'Files sent in this chat will appear here',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final media = <Attachment, Message>{};
|
||||
|
||||
for (var item in snapshot.data) {
|
||||
item.message.attachments.where((e) => e.type == 'file').forEach((e) {
|
||||
media[e] = item.message;
|
||||
});
|
||||
}
|
||||
|
||||
return LazyLoadScrollView(
|
||||
onEndOfPage: () => messageSearchBloc.search(
|
||||
filter: {
|
||||
'cid': {
|
||||
r'$in': [StreamChannel.of(context).channel.cid]
|
||||
}
|
||||
},
|
||||
messageFilter: {
|
||||
'attachments.type': {
|
||||
r'$in': ['file']
|
||||
},
|
||||
},
|
||||
sort: widget.sortOptions,
|
||||
pagination: widget.paginationParams.copyWith(
|
||||
offset: messageSearchBloc.messageResponses?.length ?? 0,
|
||||
),
|
||||
),
|
||||
child: ListView.builder(
|
||||
itemBuilder: (context, position) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(1.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: FileAttachment(
|
||||
message: media.values.toList()[position],
|
||||
attachment: media.keys.toList()[position],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: media.length,
|
||||
),
|
||||
);
|
||||
},
|
||||
stream: messageSearchBloc.messagesStream,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@ class ChannelImage extends StatelessWidget {
|
||||
this.channel,
|
||||
this.constraints,
|
||||
this.onTap,
|
||||
this.showOnlineStatus = true,
|
||||
this.borderRadius,
|
||||
this.selected = false,
|
||||
this.selectionColor,
|
||||
@@ -68,8 +67,6 @@ class ChannelImage extends StatelessWidget {
|
||||
/// The function called when the image is tapped
|
||||
final VoidCallback onTap;
|
||||
|
||||
final bool showOnlineStatus;
|
||||
|
||||
final bool selected;
|
||||
|
||||
final Color selectionColor;
|
||||
@@ -81,142 +78,144 @@ class ChannelImage extends StatelessWidget {
|
||||
final streamChat = StreamChat.of(context);
|
||||
final channel = this.channel ?? StreamChannel.of(context).channel;
|
||||
return StreamBuilder<Map<String, dynamic>>(
|
||||
stream: channel.extraDataStream,
|
||||
initialData: channel.extraData,
|
||||
builder: (context, snapshot) {
|
||||
String image;
|
||||
if (snapshot.data?.containsKey('image') == true) {
|
||||
image = snapshot.data['image'];
|
||||
} else if (channel.state.members?.length == 2) {
|
||||
final otherMember = channel.state.members
|
||||
.firstWhere((member) => member.user.id != streamChat.user.id);
|
||||
return StreamBuilder<User>(
|
||||
stream: streamChat.client.state.usersStream
|
||||
.map((users) => users[otherMember.userId]),
|
||||
initialData: otherMember.user,
|
||||
builder: (context, snapshot) {
|
||||
return UserAvatar(
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
user: snapshot.data ?? otherMember.user,
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap != null ? (_) => onTap() : null,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
final images = channel.state.members
|
||||
.where((member) =>
|
||||
member.user.id != streamChat.user.id &&
|
||||
member.user.extraData['image'] != null)
|
||||
.take(4)
|
||||
.map((e) => e.user.extraData['image'] as String)
|
||||
.toList();
|
||||
return GroupImage(
|
||||
images: images,
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
}
|
||||
|
||||
Widget child = ClipRRect(
|
||||
stream: channel.extraDataStream,
|
||||
initialData: channel.extraData,
|
||||
builder: (context, snapshot) {
|
||||
String image;
|
||||
if (snapshot.data?.containsKey('image') == true) {
|
||||
image = snapshot.data['image'];
|
||||
} else if (channel.state.members?.length == 2) {
|
||||
final otherMember = channel.state.members
|
||||
.firstWhere((member) => member.user.id != streamChat.user.id);
|
||||
return StreamBuilder<User>(
|
||||
stream: streamChat.client.state.usersStream
|
||||
.map((users) => users[otherMember.userId]),
|
||||
initialData: otherMember.user,
|
||||
builder: (context, snapshot) {
|
||||
return UserAvatar(
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
user: snapshot.data ?? otherMember.user,
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap != null ? (_) => onTap() : null,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
final images = channel.state.members
|
||||
.where((member) =>
|
||||
member.user.id != streamChat.user.id &&
|
||||
member.user.extraData['image'] != null)
|
||||
.take(4)
|
||||
.map((e) => e.user.extraData['image'] as String)
|
||||
.toList();
|
||||
return GroupImage(
|
||||
images: images,
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
image != null
|
||||
? CachedNetworkImage(
|
||||
imageUrl: image,
|
||||
errorWidget: (_, __, ___) {
|
||||
return Center(
|
||||
child: Text(
|
||||
snapshot.data?.containsKey('name') ?? false
|
||||
? snapshot.data['name'][0]
|
||||
: '',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: StreamChatTheme.of(context)
|
||||
.defaultChannelImage(context, channel),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
if (selected) {
|
||||
child = ClipRRect(
|
||||
borderRadius: (borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
}
|
||||
|
||||
Widget child = ClipRRect(
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
image != null
|
||||
? CachedNetworkImage(
|
||||
imageUrl: image,
|
||||
errorWidget: (_, __, ___) {
|
||||
return Center(
|
||||
child: Text(
|
||||
snapshot.data?.containsKey('name') ?? false
|
||||
? snapshot.data['name'][0]
|
||||
: '',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: StreamChatTheme.of(context)
|
||||
.defaultChannelImage(context, channel),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
if (selected) {
|
||||
child = ClipRRect(
|
||||
key: Key('selectedImage'),
|
||||
borderRadius: (borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
color: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: child,
|
||||
),
|
||||
.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
color: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
});
|
||||
),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import 'attachment/attachment.dart';
|
||||
|
||||
class ChannelMediaDisplayScreen extends StatefulWidget {
|
||||
/// The sorting used for the channels matching the filters.
|
||||
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
||||
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
||||
/// Direction can be ascending or descending.
|
||||
final List<SortOption> sortOptions;
|
||||
|
||||
/// Pagination parameters
|
||||
/// limit: the number of users to return (max is 30)
|
||||
/// offset: the offset (max is 1000)
|
||||
/// message_limit: how many messages should be included to each channel
|
||||
final PaginationParams paginationParams;
|
||||
|
||||
/// The builder used when the file list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
final ShowMessageCallback onShowMessage;
|
||||
|
||||
const ChannelMediaDisplayScreen({
|
||||
this.sortOptions,
|
||||
this.paginationParams,
|
||||
this.emptyBuilder,
|
||||
this.onShowMessage,
|
||||
});
|
||||
|
||||
@override
|
||||
_ChannelMediaDisplayScreenState createState() =>
|
||||
_ChannelMediaDisplayScreenState();
|
||||
}
|
||||
|
||||
class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
Map<String, VideoPlayerController> controllerCache = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
||||
messageSearchBloc.search(
|
||||
filter: {
|
||||
'cid': {
|
||||
r'$in': [StreamChannel.of(context).channel.cid],
|
||||
}
|
||||
},
|
||||
messageFilter: {
|
||||
'attachments.type': {
|
||||
r'$in': ['image', 'video']
|
||||
},
|
||||
},
|
||||
sort: widget.sortOptions,
|
||||
pagination: widget.paginationParams,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
'Photos & Videos',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
leading: Center(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
width: 24.0,
|
||||
height: 24.0,
|
||||
child: StreamSvgIcon.left(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
body: _buildMediaGrid(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMediaGrid() {
|
||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
||||
|
||||
return StreamBuilder<List<GetMessageResponse>>(
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == null) {
|
||||
return Center(
|
||||
child: const CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.data.isEmpty) {
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder(context);
|
||||
}
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
StreamSvgIcon.pictures(
|
||||
size: 136.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
),
|
||||
SizedBox(height: 16.0),
|
||||
Text(
|
||||
'No Media',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0),
|
||||
Text(
|
||||
'Photos or video sent in this chat will \nappear here',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final media = <_AssetPackage>[];
|
||||
|
||||
for (var item in snapshot.data) {
|
||||
item.message.attachments
|
||||
.where((e) =>
|
||||
(e.type == 'image' || e.type == 'video') &&
|
||||
e.ogScrapeUrl == null)
|
||||
.forEach((e) {
|
||||
VideoPlayerController controller;
|
||||
if (e.type == 'video') {
|
||||
var cachedController = controllerCache[e.assetUrl];
|
||||
|
||||
if (cachedController == null) {
|
||||
controller = VideoPlayerController.network(e.assetUrl);
|
||||
controller.initialize();
|
||||
controllerCache[e.assetUrl] = controller;
|
||||
} else {
|
||||
controller = cachedController;
|
||||
}
|
||||
}
|
||||
media.add(_AssetPackage(e, item.message, controller));
|
||||
});
|
||||
}
|
||||
|
||||
return LazyLoadScrollView(
|
||||
onEndOfPage: () => messageSearchBloc.search(
|
||||
filter: {
|
||||
'cid': {
|
||||
r'$in': [StreamChannel.of(context).channel.cid]
|
||||
}
|
||||
},
|
||||
messageFilter: {
|
||||
'attachments.type': {
|
||||
r'$in': ['image', 'video']
|
||||
},
|
||||
},
|
||||
sort: widget.sortOptions,
|
||||
pagination: widget.paginationParams.copyWith(
|
||||
offset: messageSearchBloc.messageResponses?.length ?? 0,
|
||||
),
|
||||
),
|
||||
child: GridView.builder(
|
||||
gridDelegate:
|
||||
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
|
||||
itemBuilder: (context, position) {
|
||||
var channel = StreamChannel.of(context).channel;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(1.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: FullScreenMedia(
|
||||
mediaAttachments:
|
||||
media.map((e) => e.attachment).toList(),
|
||||
startIndex: position,
|
||||
message: media[position].message,
|
||||
sentAt: media[position].message.createdAt,
|
||||
userName: media[position].message.user.name,
|
||||
onShowMessage: widget.onShowMessage,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: media[position].attachment.type == 'image'
|
||||
? IgnorePointer(
|
||||
child: ImageAttachment(
|
||||
attachment: media[position].attachment,
|
||||
message: media[position].message,
|
||||
showTitle: false,
|
||||
size: Size(
|
||||
MediaQuery.of(context).size.width * 0.8,
|
||||
MediaQuery.of(context).size.height * 0.3,
|
||||
),
|
||||
),
|
||||
)
|
||||
: VideoPlayer(media[position].videoPlayer),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: media.length,
|
||||
),
|
||||
);
|
||||
},
|
||||
stream: messageSearchBloc.messagesStream,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
for (var c in controllerCache.values) {
|
||||
c.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _AssetPackage {
|
||||
Attachment attachment;
|
||||
Message message;
|
||||
VideoPlayerController videoPlayer;
|
||||
|
||||
_AssetPackage(this.attachment, this.message, this.videoPlayer);
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
import '../stream_chat_flutter.dart';
|
||||
import 'channel_name.dart';
|
||||
import 'channel_unread_indicator.dart';
|
||||
|
||||
/// 
|
||||
/// 
|
||||
@@ -98,19 +97,20 @@ class ChannelPreview extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
StreamBuilder<List<Member>>(
|
||||
stream: channel.state.membersStream,
|
||||
initialData: channel.state.members,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData ||
|
||||
snapshot.data.isEmpty ||
|
||||
!snapshot.data.any((Member e) =>
|
||||
e.user.id == channel.client.state.user.id)) {
|
||||
return SizedBox();
|
||||
}
|
||||
return ChannelUnreadIndicator(
|
||||
channel: channel,
|
||||
);
|
||||
}),
|
||||
stream: channel.state.membersStream,
|
||||
initialData: channel.state.members,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData ||
|
||||
snapshot.data.isEmpty ||
|
||||
!snapshot.data.any((Member e) =>
|
||||
e.user.id == channel.client.state.user.id)) {
|
||||
return SizedBox();
|
||||
}
|
||||
return UnreadIndicator(
|
||||
cid: channel.cid,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||
|
||||
class ChannelUnreadIndicator extends StatelessWidget {
|
||||
const ChannelUnreadIndicator({
|
||||
Key key,
|
||||
@required this.channel,
|
||||
}) : super(key: key);
|
||||
|
||||
final Channel channel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder<int>(
|
||||
stream: channel.state.unreadCountStream,
|
||||
initialData: channel.state.unreadCount,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData || snapshot.data == 0) {
|
||||
return SizedBox();
|
||||
}
|
||||
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.unreadCounterColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 5.0,
|
||||
right: 5.0,
|
||||
top: 2,
|
||||
bottom: 1,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${snapshot.data > 99 ? '99+' : snapshot.data}',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,15 @@ class InfoTile extends StatelessWidget {
|
||||
final TextStyle textStyle;
|
||||
final Color backgroundColor;
|
||||
|
||||
InfoTile(
|
||||
{this.message,
|
||||
this.child,
|
||||
this.showMessage,
|
||||
this.tileAnchor,
|
||||
this.childAnchor,
|
||||
this.textStyle,
|
||||
this.backgroundColor});
|
||||
InfoTile({
|
||||
this.message,
|
||||
this.child,
|
||||
this.showMessage,
|
||||
this.tileAnchor,
|
||||
this.childAnchor,
|
||||
this.textStyle,
|
||||
this.backgroundColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:stream_chat_flutter/src/message_action.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
@@ -18,11 +17,12 @@ import 'stream_chat_theme.dart';
|
||||
|
||||
class MessageActionsModal extends StatefulWidget {
|
||||
final Widget Function(BuildContext, Message) editMessageInputBuilder;
|
||||
final void Function(Message) onThreadReplyTap;
|
||||
final void Function(Message) onReplyTap;
|
||||
final OnMessageTap onThreadReplyTap;
|
||||
final OnMessageTap onReplyTap;
|
||||
final Message message;
|
||||
final MessageTheme messageTheme;
|
||||
final bool showReactions;
|
||||
final OnMessageTap onCopyTap;
|
||||
final bool showDeleteMessage;
|
||||
final bool showCopyMessage;
|
||||
final bool showEditMessage;
|
||||
@@ -60,6 +60,7 @@ class MessageActionsModal extends StatefulWidget {
|
||||
this.reverse = false,
|
||||
this.customActions = const [],
|
||||
this.attachmentBorderRadiusGeometry,
|
||||
this.onCopyTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -211,10 +212,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (widget.showReplyMessage &&
|
||||
(widget.message.status ==
|
||||
widget.message.status ==
|
||||
MessageSendingStatus.sent ||
|
||||
widget.message.status == null) &&
|
||||
widget.message.parentId == null)
|
||||
widget.message.status == null)
|
||||
_buildReplyButton(context),
|
||||
if (widget.showThreadReplyMessage &&
|
||||
(widget.message.status ==
|
||||
@@ -452,7 +452,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
Widget _buildCopyButton(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
await Clipboard.setData(ClipboardData(text: widget.message.text));
|
||||
widget.onCopyTap?.call(widget.message);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Padding(
|
||||
|
||||
@@ -693,7 +693,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
StreamChannel.of(context).channel.keyStroke().catchError((e) {});
|
||||
StreamChannel.of(context).channel.keyStroke()?.catchError((e) {});
|
||||
|
||||
setState(() {
|
||||
_messageIsPresent = s.trim().isNotEmpty;
|
||||
@@ -2181,10 +2181,12 @@ class MessageInputState extends State<MessageInput> {
|
||||
void _parseExistingMessage(Message message) {
|
||||
textEditingController.text = message.text;
|
||||
_messageIsPresent = true;
|
||||
for (final attachment in message?.attachments) {
|
||||
_attachments[attachment.id] = attachment.copyWith(
|
||||
uploadState: attachment.uploadState ?? UploadState.success(),
|
||||
);
|
||||
if (message.attachments != null) {
|
||||
for (final attachment in message.attachments) {
|
||||
_attachments[attachment.id] = attachment.copyWith(
|
||||
uploadState: attachment.uploadState ?? UploadState.success(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ import 'message_widget.dart';
|
||||
import 'stream_chat_theme.dart';
|
||||
|
||||
class MessageReactionsModal extends StatelessWidget {
|
||||
final Widget Function(BuildContext, Message) editMessageInputBuilder;
|
||||
final void Function(Message) onThreadTap;
|
||||
final Message message;
|
||||
final MessageTheme messageTheme;
|
||||
final bool reverse;
|
||||
@@ -30,8 +28,6 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
@required this.message,
|
||||
@required this.messageTheme,
|
||||
this.showReactions = true,
|
||||
this.onThreadTap,
|
||||
this.editMessageInputBuilder,
|
||||
this.messageShape,
|
||||
this.attachmentShape,
|
||||
this.reverse = false,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:stream_chat_flutter/src/message_action.dart';
|
||||
@@ -841,6 +842,8 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
return StreamChannel(
|
||||
channel: channel,
|
||||
child: MessageActionsModal(
|
||||
onCopyTap: (message) =>
|
||||
Clipboard.setData(ClipboardData(text: message.text)),
|
||||
attachmentBorderRadiusGeometry:
|
||||
widget.attachmentBorderRadiusGeometry,
|
||||
showUserAvatar:
|
||||
@@ -903,8 +906,6 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
widget.attachmentShape ?? _getDefaultAttachmentShape(context),
|
||||
reverse: widget.reverse,
|
||||
message: widget.message,
|
||||
editMessageInputBuilder: widget.editMessageInputBuilder,
|
||||
onThreadTap: widget.onThreadTap,
|
||||
showReactions: widget.showReactions,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
@@ -36,67 +37,70 @@ class ReactionBubble extends StatelessWidget {
|
||||
return Transform(
|
||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Transform.translate(
|
||||
offset: Offset(reverse ? offset : -offset, 0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
color: maskColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
child: Center(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
fit: StackFit.loose,
|
||||
children: [
|
||||
Transform.translate(
|
||||
offset: Offset(reverse ? offset : -offset, 0),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: totalReactions > 1 ? 4 : 0,
|
||||
),
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: borderColor,
|
||||
),
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
color: maskColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Flex(
|
||||
direction: Axis.horizontal,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (constraints.maxWidth < double.infinity)
|
||||
...reactions
|
||||
.take((constraints.maxWidth) ~/ 24)
|
||||
.map((reaction) {
|
||||
return _buildReaction(
|
||||
reactionIcons,
|
||||
reaction,
|
||||
context,
|
||||
);
|
||||
}).toList(),
|
||||
if (constraints.maxWidth == double.infinity)
|
||||
...reactions.map((reaction) {
|
||||
return _buildReaction(
|
||||
reactionIcons,
|
||||
reaction,
|
||||
context,
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: totalReactions > 1 ? 4 : 0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: borderColor,
|
||||
),
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Flex(
|
||||
direction: Axis.horizontal,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (constraints.maxWidth < double.infinity)
|
||||
...reactions
|
||||
.take((constraints.maxWidth) ~/ 24)
|
||||
.map((reaction) {
|
||||
return _buildReaction(
|
||||
reactionIcons,
|
||||
reaction,
|
||||
context,
|
||||
);
|
||||
}).toList(),
|
||||
if (constraints.maxWidth == double.infinity)
|
||||
...reactions.map((reaction) {
|
||||
return _buildReaction(
|
||||
reactionIcons,
|
||||
reaction,
|
||||
context,
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 2,
|
||||
left: reverse ? null : 13,
|
||||
right: !reverse ? null : 13,
|
||||
child: _buildReactionsTail(context),
|
||||
),
|
||||
],
|
||||
Positioned(
|
||||
bottom: 2,
|
||||
left: reverse ? null : 13,
|
||||
right: !reverse ? null : 13,
|
||||
child: _buildReactionsTail(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class UnreadIndicator extends StatelessWidget {
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${snapshot.data}',
|
||||
'${snapshot.data > 99 ? '99+' : snapshot.data}',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.white,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
export 'src/attachment/attachment.dart';
|
||||
export 'src/back_button.dart';
|
||||
export 'src/channel_header.dart';
|
||||
export 'src/channel_image.dart';
|
||||
@@ -5,39 +8,35 @@ export 'src/channel_list_header.dart';
|
||||
export 'src/channel_list_view.dart';
|
||||
export 'src/channel_name.dart';
|
||||
export 'src/channel_preview.dart';
|
||||
export 'src/connection_status_builder.dart';
|
||||
export 'src/date_divider.dart';
|
||||
export 'src/deleted_message.dart';
|
||||
export 'src/message_action.dart';
|
||||
export 'src/attachment/attachment.dart';
|
||||
export 'src/full_screen_media.dart';
|
||||
export 'src/image_header.dart';
|
||||
export 'src/image_footer.dart';
|
||||
export 'src/image_header.dart';
|
||||
export 'src/info_tile.dart';
|
||||
export 'src/mention_tile.dart';
|
||||
export 'src/message_action.dart';
|
||||
export 'src/message_input.dart';
|
||||
export 'src/message_list_view.dart';
|
||||
export 'src/message_search_item.dart';
|
||||
export 'src/message_search_list_view.dart';
|
||||
export 'src/message_text.dart';
|
||||
export 'src/message_widget.dart';
|
||||
export 'src/option_list_tile.dart';
|
||||
export 'src/reaction_picker.dart';
|
||||
export 'src/sending_indicator.dart';
|
||||
export 'src/stream_chat.dart';
|
||||
export 'src/stream_chat_theme.dart';
|
||||
export 'src/stream_neumorphic_button.dart';
|
||||
export 'src/stream_svg_icon.dart';
|
||||
export 'src/system_message.dart';
|
||||
export 'src/thread_header.dart';
|
||||
export 'src/typing_indicator.dart';
|
||||
export 'src/unread_indicator.dart';
|
||||
export 'src/user_avatar.dart';
|
||||
export 'src/user_item.dart';
|
||||
export 'src/user_item.dart';
|
||||
export 'src/user_list_view.dart';
|
||||
export 'src/user_list_view.dart';
|
||||
export 'src/utils.dart';
|
||||
export 'src/message_search_item.dart';
|
||||
export 'src/message_search_list_view.dart';
|
||||
export 'src/unread_indicator.dart';
|
||||
export 'src/option_list_tile.dart';
|
||||
export 'src/channel_file_display_screen.dart';
|
||||
export 'src/channel_media_display_screen.dart';
|
||||
export 'src/info_tile.dart';
|
||||
export 'src/stream_chat.dart';
|
||||
export 'src/connection_status_builder.dart';
|
||||
export 'src/mention_tile.dart';
|
||||
export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
@@ -55,9 +55,12 @@ flutter:
|
||||
- svgs/
|
||||
- lib/svgs/
|
||||
- animations/
|
||||
uses-material-design: true
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^4.1.3
|
||||
pedantic: ^1.9.2
|
||||
pedantic: ^1.9.2
|
||||
golden_toolkit: ^0.6.0
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
|
||||
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
|
||||
await loadAppFonts();
|
||||
return testMain();
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/src/attachment_actions_modal.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
class MockAttachmentDownloader extends Mock {
|
||||
ProgressCallback progressCallback;
|
||||
Completer<String> completer = Completer();
|
||||
|
||||
Future<String> call(
|
||||
Attachment attachment, {
|
||||
ProgressCallback progressCallback,
|
||||
}) {
|
||||
this.progressCallback = progressCallback;
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show all the actions',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: AttachmentActionsModal(
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'text.jpg',
|
||||
),
|
||||
],
|
||||
),
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(find.text('Reply'), findsOneWidget);
|
||||
expect(find.text('Show in Chat'), findsOneWidget);
|
||||
expect(find.text('Save Image'), findsOneWidget);
|
||||
expect(find.text('Delete'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should hide delete if it\'s not my message',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id2'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: AttachmentActionsModal(
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'text.jpg',
|
||||
),
|
||||
],
|
||||
),
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(find.text('Reply'), findsOneWidget);
|
||||
expect(find.text('Show in Chat'), findsOneWidget);
|
||||
expect(find.text('Save Image'), findsOneWidget);
|
||||
expect(find.text('Delete'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show save video for videos',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: Container(
|
||||
child: AttachmentActionsModal(
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'video',
|
||||
title: 'video.mp4',
|
||||
),
|
||||
],
|
||||
),
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(find.text('Save Video'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on reply should pop',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
final mockObserver = MockNavigatorObserver();
|
||||
|
||||
final message = Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'image.jpg',
|
||||
),
|
||||
],
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
navigatorObservers: [mockObserver],
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: Container(
|
||||
child: AttachmentActionsModal(
|
||||
message: message,
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('Reply'));
|
||||
verify(mockObserver.didPop(any, any));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on show in chat should call onShowMessage',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
final onShowMessage = MockVoidCallback();
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: Container(
|
||||
child: AttachmentActionsModal(
|
||||
onShowMessage: onShowMessage,
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'image.jpg',
|
||||
),
|
||||
]),
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('Show in Chat'));
|
||||
verify(onShowMessage.call()).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on delete in chat should remove the attachment',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.updateMessage(any)).thenAnswer((_) {
|
||||
return;
|
||||
});
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final message = Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'image.jpg',
|
||||
),
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'image.jpg',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: mockChannel,
|
||||
child: AttachmentActionsModal(
|
||||
message: message,
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('Delete'));
|
||||
verify(mockChannel.updateMessage(message.copyWith(
|
||||
attachments: [
|
||||
message.attachments[1],
|
||||
],
|
||||
))).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on delete in chat should remove the attachment if there is text',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.updateMessage(any)).thenAnswer((_) {
|
||||
return;
|
||||
});
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final message = Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'image.jpg',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: mockChannel,
|
||||
child: AttachmentActionsModal(
|
||||
message: message,
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('Delete'));
|
||||
verify(mockChannel.updateMessage(message.copyWith(
|
||||
attachments: [],
|
||||
))).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on delete in chat should remove the message if that\'s the only attachment and there is no text',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final mockChannel = MockChannel();
|
||||
|
||||
when(mockChannel.deleteMessage(any)).thenAnswer((_) {
|
||||
return;
|
||||
});
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final message = Message(
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'image.jpg',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: mockChannel,
|
||||
child: AttachmentActionsModal(
|
||||
message: message,
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('Delete'));
|
||||
verify(mockChannel.deleteMessage(message)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on save in chat should call image downloader',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final imageDownloader = MockAttachmentDownloader();
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
home: Container(
|
||||
child: AttachmentActionsModal(
|
||||
imageDownloader: imageDownloader,
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'image.jpg',
|
||||
),
|
||||
]),
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Save Image'));
|
||||
|
||||
imageDownloader.progressCallback(0, 100);
|
||||
await tester.pump();
|
||||
expect(find.text('0%'), findsOneWidget);
|
||||
|
||||
imageDownloader.progressCallback(50, 100);
|
||||
await tester.pump();
|
||||
expect(find.text('50%'), findsOneWidget);
|
||||
|
||||
imageDownloader.progressCallback(100, 100);
|
||||
imageDownloader.completer.complete('path');
|
||||
await tester.pump();
|
||||
expect(find.byKey(Key('completedIcon')), findsOneWidget);
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 500));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on save in chat should call file downloader',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final fileDownloader = MockAttachmentDownloader();
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
home: Container(
|
||||
child: AttachmentActionsModal(
|
||||
fileDownloader: fileDownloader,
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
attachments: [
|
||||
Attachment(
|
||||
type: 'video',
|
||||
title: 'video.mp4',
|
||||
),
|
||||
]),
|
||||
currentIndex: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Save Video'));
|
||||
|
||||
fileDownloader.progressCallback(0, 100);
|
||||
await tester.pump();
|
||||
expect(find.text('0%'), findsOneWidget);
|
||||
|
||||
fileDownloader.progressCallback(50, 100);
|
||||
await tester.pump();
|
||||
expect(find.text('50%'), findsOneWidget);
|
||||
|
||||
fileDownloader.progressCallback(100, 100);
|
||||
fileDownloader.completer.complete('path');
|
||||
await tester.pump();
|
||||
expect(find.byKey(Key('completedIcon')), findsOneWidget);
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 500));
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show file details',
|
||||
(WidgetTester tester) async {
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
|
||||
when(channel.state).thenReturn(channelState);
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChatTheme(
|
||||
data: streamTheme,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: FileAttachment(
|
||||
size: Size(
|
||||
300.0,
|
||||
300.0,
|
||||
),
|
||||
message: Message(),
|
||||
attachment: Attachment(
|
||||
type: 'file',
|
||||
title: 'example.pdf',
|
||||
extraData: {
|
||||
'mime_type': 'pdf',
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('example.pdf'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/src/back_button.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'BackButton control test',
|
||||
(WidgetTester tester) async {
|
||||
final theme = ThemeData();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: const Material(child: Text('Home')),
|
||||
routes: <String, WidgetBuilder>{
|
||||
'/next': (BuildContext context) {
|
||||
return Material(
|
||||
child: Center(
|
||||
child: StreamChatTheme(
|
||||
data: StreamChatThemeData.getDefaultTheme(theme),
|
||||
child: StreamBackButton(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// ignore: unawaited_futures
|
||||
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(StreamBackButton));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Home'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should not throw errors if cannot pop',
|
||||
(WidgetTester tester) async {
|
||||
final theme = ThemeData();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: StreamChatTheme(
|
||||
data: StreamChatThemeData.getDefaultTheme(theme),
|
||||
child: StreamBackButton(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(StreamBackButton));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(StreamBackButton), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'BackButton onPressed overrides default pop behavior',
|
||||
(WidgetTester tester) async {
|
||||
final theme = ThemeData();
|
||||
var customCallbackWasCalled = false;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: const Material(child: Text('Home')),
|
||||
routes: <String, WidgetBuilder>{
|
||||
'/next': (BuildContext context) {
|
||||
return Material(
|
||||
child: Center(
|
||||
child: StreamChatTheme(
|
||||
data: StreamChatThemeData.getDefaultTheme(theme),
|
||||
child: StreamBackButton(
|
||||
onPressed: () => customCallbackWasCalled = true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// ignore: unawaited_futures
|
||||
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Home'), findsNothing); // Start off on the second page.
|
||||
expect(
|
||||
customCallbackWasCalled,
|
||||
false,
|
||||
); // customCallbackWasCalled should still be false.
|
||||
await tester.tap(find.byType(StreamBackButton));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// We're still on the second page.
|
||||
expect(find.text('Home'), findsNothing);
|
||||
// But the custom callback is called.
|
||||
expect(customCallbackWasCalled, true);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show unread count',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: StreamChat(
|
||||
client: client,
|
||||
child: StreamBackButton(
|
||||
showUnreads: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(UnreadIndicator), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/src/channel_info.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show basic channel information',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelHeader(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
expect(find.byType(ChannelImage), findsOneWidget);
|
||||
expect(find.byType(StreamBackButton), findsOneWidget);
|
||||
expect(find.byType(ChannelInfo), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show the InfoTile message if disconnected',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelHeader(
|
||||
showConnectionStateTile: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(tester.widget<InfoTile>(find.byType(InfoTile)).showMessage, true);
|
||||
expect(tester.widget<InfoTile>(find.byType(InfoTile)).message,
|
||||
'Disconnected');
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show the InfoTile message if connecting',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
showLoading: false,
|
||||
child: Scaffold(
|
||||
body: ChannelHeader(
|
||||
showConnectionStateTile: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
await tester.pump();
|
||||
|
||||
expect(tester.widget<InfoTile>(find.byType(InfoTile)).showMessage, true);
|
||||
expect(tester.widget<InfoTile>(find.byType(InfoTile)).message,
|
||||
'Reconnecting...');
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should apply passed properties',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelHeader(
|
||||
leading: Text('leading'),
|
||||
subtitle: Text('subtitle'),
|
||||
actions: [
|
||||
Text('action'),
|
||||
],
|
||||
title: Text('title'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('test'), findsNothing);
|
||||
expect(find.byType(StreamBackButton), findsNothing);
|
||||
expect(find.byType(ChannelImage), findsNothing);
|
||||
expect(find.byType(ChannelInfo), findsNothing);
|
||||
expect(find.text('leading'), findsOneWidget);
|
||||
expect(find.text('title'), findsOneWidget);
|
||||
expect(find.text('subtitle'), findsOneWidget);
|
||||
expect(find.text('action'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'showBackButton: false should hide the StreamBackButton and '
|
||||
'showTypingIndicator: false should hide the typing indicator and '
|
||||
'showConnectionStateTile: false should be passed to the infotile',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelHeader(
|
||||
showTypingIndicator: false,
|
||||
showBackButton: false,
|
||||
showConnectionStateTile: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byType(StreamBackButton), findsNothing);
|
||||
expect(
|
||||
tester
|
||||
.widget<ChannelInfo>(find.byType(ChannelInfo))
|
||||
.showTypingIndicator,
|
||||
false);
|
||||
expect(tester.widget<InfoTile>(find.byType(InfoTile)).showMessage, false);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'should apply passed callbacks',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
|
||||
var backPressed = false;
|
||||
var imageTapped = false;
|
||||
var titleTapped = false;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelHeader(
|
||||
onBackPressed: () => backPressed = true,
|
||||
onImageTap: () => imageTapped = true,
|
||||
onTitleTap: () => titleTapped = true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
await tester.tap(find.byType(StreamBackButton));
|
||||
await tester.tap(find.byType(ChannelImage));
|
||||
await tester.tap(find.byType(ChannelName));
|
||||
|
||||
expect(backPressed, true);
|
||||
expect(imageTapped, true);
|
||||
expect(titleTapped, true);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/src/group_image.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show the image in channel.extraData',
|
||||
(tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
'image': 'imagetest',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
'image': 'imagetest',
|
||||
});
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelImage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
final image =
|
||||
tester.widget<CachedNetworkImage>(find.byType(CachedNetworkImage));
|
||||
expect(image.imageUrl, 'imagetest');
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show the the other member image',
|
||||
(tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
Member(
|
||||
userId: 'user-id2',
|
||||
user: User(
|
||||
id: 'user-id2',
|
||||
extraData: {
|
||||
'image': 'testimage',
|
||||
},
|
||||
),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id2',
|
||||
user: User(
|
||||
id: 'user-id2',
|
||||
extraData: {
|
||||
'image': 'testimage',
|
||||
},
|
||||
),
|
||||
),
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]);
|
||||
when(clientState.usersStream).thenAnswer((i) => Stream.value({
|
||||
'user-id2': User(
|
||||
id: 'user-id2',
|
||||
extraData: {
|
||||
'image': 'testimage',
|
||||
},
|
||||
),
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelImage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
final image =
|
||||
tester.widget<CachedNetworkImage>(find.byType(CachedNetworkImage));
|
||||
expect(image.imageUrl, 'testimage');
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should use a groupimage if more than 2 members',
|
||||
(tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
extraData: {
|
||||
'image': 'testimage1',
|
||||
},
|
||||
),
|
||||
),
|
||||
Member(
|
||||
userId: 'user-id2',
|
||||
user: User(
|
||||
id: 'user-id2',
|
||||
extraData: {
|
||||
'image': 'testimage2',
|
||||
},
|
||||
),
|
||||
),
|
||||
Member(
|
||||
userId: 'user-id3',
|
||||
user: User(
|
||||
id: 'user-id3',
|
||||
extraData: {
|
||||
'image': 'testimage3',
|
||||
},
|
||||
),
|
||||
),
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
extraData: {
|
||||
'image': 'testimage1',
|
||||
},
|
||||
),
|
||||
),
|
||||
Member(
|
||||
userId: 'user-id2',
|
||||
user: User(
|
||||
id: 'user-id2',
|
||||
extraData: {
|
||||
'image': 'testimage2',
|
||||
},
|
||||
),
|
||||
),
|
||||
Member(
|
||||
userId: 'user-id3',
|
||||
user: User(
|
||||
id: 'user-id3',
|
||||
extraData: {
|
||||
'image': 'testimage3',
|
||||
},
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelImage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
final image = tester.widget<GroupImage>(find.byType(GroupImage));
|
||||
expect(image.images, [
|
||||
'testimage2',
|
||||
'testimage3',
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'using select: true should show a selection border',
|
||||
(tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
'image': 'imagetest',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
'image': 'imagetest',
|
||||
});
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelImage(
|
||||
selected: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byKey(Key('selectedImage')), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'control test',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connected));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: ChannelListHeader(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final userAvatar = tester.widget<UserAvatar>(find.byType(UserAvatar));
|
||||
expect(userAvatar.user, clientState.user);
|
||||
expect(find.byType(StreamNeumorphicButton), findsOneWidget);
|
||||
expect(find.text('Stream Chat'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show the InfoTile message if disconnected',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.disconnected));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: ChannelListHeader(
|
||||
showConnectionStateTile: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Disconnected'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show the InfoTile message if connecting',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: ChannelListHeader(
|
||||
showConnectionStateTile: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Reconnecting...'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should apply passed properties',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: ChannelListHeader(
|
||||
titleBuilder: (context, status, client) {
|
||||
return Text('TITLE');
|
||||
},
|
||||
subtitle: Text('SUBTITLE'),
|
||||
leading: Text('LEADING'),
|
||||
actions: [
|
||||
Text('ACTION'),
|
||||
],
|
||||
client: client,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('TITLE'), findsOneWidget);
|
||||
expect(find.text('SUBTITLE'), findsOneWidget);
|
||||
expect(find.text('LEADING'), findsOneWidget);
|
||||
expect(find.text('ACTION'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should apply prenavigationcallback',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||
|
||||
var tapped = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: ChannelListHeader(
|
||||
preNavigationCallback: () {
|
||||
tapped = true;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byType(UserAvatar));
|
||||
expect(tapped, true);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should apply passed callbacks',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(client.wsConnectionStatusStream)
|
||||
.thenAnswer((_) => Stream.value(ConnectionStatus.connecting));
|
||||
|
||||
var tapped = 0;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: ChannelListHeader(
|
||||
onUserAvatarTap: (u) {
|
||||
tapped++;
|
||||
},
|
||||
onNewChatButtonTap: () {
|
||||
tapped++;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byType(UserAvatar));
|
||||
await tester.tap(find.byType(StreamNeumorphicButton));
|
||||
expect(tapped, 2);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show channel name',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(channelState.messages).thenReturn([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]);
|
||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ChannelName(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ void main() {
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(channel.cid).thenReturn('cid');
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
@@ -28,6 +29,9 @@ void main() {
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test name',
|
||||
});
|
||||
when(clientState.channels).thenReturn({
|
||||
channel.cid: channel,
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show basic channel information',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: DateDivider(
|
||||
dateTime: DateTime.now(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('Today'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'control test',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: DeletedMessage(
|
||||
messageTheme: MessageTheme(
|
||||
createdAt: TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
messageText: TextStyle(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('Message deleted'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'control golden light',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
when(clientState.totalUnreadCount).thenReturn(10);
|
||||
when(clientState.totalUnreadCountStream)
|
||||
.thenAnswer((i) => Stream.value(10));
|
||||
|
||||
final materialTheme = ThemeData.light();
|
||||
final theme = StreamChatThemeData.fromTheme(materialTheme);
|
||||
await tester.pumpWidgetBuilder(
|
||||
materialAppWrapper(
|
||||
theme: materialTheme,
|
||||
)(
|
||||
StreamChat(
|
||||
streamChatThemeData: theme,
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Center(
|
||||
child: DeletedMessage(
|
||||
messageTheme: theme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size.square(200),
|
||||
);
|
||||
|
||||
await screenMatchesGolden(tester, 'deleted_message_light');
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'control golden dark',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
when(clientState.totalUnreadCount).thenReturn(10);
|
||||
when(clientState.totalUnreadCountStream)
|
||||
.thenAnswer((i) => Stream.value(10));
|
||||
|
||||
final materialTheme = ThemeData.dark();
|
||||
final theme = StreamChatThemeData.fromTheme(materialTheme);
|
||||
await tester.pumpWidgetBuilder(
|
||||
materialAppWrapper(
|
||||
theme: materialTheme,
|
||||
)(
|
||||
StreamChat(
|
||||
streamChatThemeData: theme,
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Center(
|
||||
child: DeletedMessage(
|
||||
messageTheme: theme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size.square(200),
|
||||
);
|
||||
|
||||
await screenMatchesGolden(tester, 'deleted_message_dark');
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'golden customization test',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
when(clientState.totalUnreadCount).thenReturn(10);
|
||||
when(clientState.totalUnreadCountStream)
|
||||
.thenAnswer((i) => Stream.value(10));
|
||||
|
||||
final materialTheme = ThemeData.light();
|
||||
final theme = StreamChatThemeData.fromTheme(materialTheme);
|
||||
await tester.pumpWidgetBuilder(
|
||||
materialAppWrapper(
|
||||
theme: materialTheme,
|
||||
)(
|
||||
StreamChat(
|
||||
streamChatThemeData: theme,
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Center(
|
||||
child: DeletedMessage(
|
||||
messageTheme: theme.ownMessageTheme,
|
||||
reverse: true,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size.square(200),
|
||||
);
|
||||
|
||||
await screenMatchesGolden(tester, 'deleted_message_custom');
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show channel typing',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(channelState.messages).thenReturn([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]);
|
||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]));
|
||||
|
||||
when(channelState.typingEvents).thenAnswer((i) => [
|
||||
User(id: 'other-user', extraData: {'name': 'demo'})
|
||||
]);
|
||||
when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([
|
||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||
]));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: FullScreenMedia(
|
||||
mediaAttachments: [
|
||||
Attachment(
|
||||
type: 'image',
|
||||
title: 'demo image',
|
||||
imageUrl: '',
|
||||
),
|
||||
],
|
||||
message: Message(
|
||||
createdAt: DateTime.now(),
|
||||
),
|
||||
sentAt: DateTime.now(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byType(PhotoView), findsOneWidget);
|
||||
expect(find.byType(StreamSvgIcon), findsNWidgets(4));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 410 B |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show channel typing',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: Scaffold(
|
||||
body: ImageFooter(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byType(StreamSvgIcon), findsNWidgets(2));
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'control test',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: Portal(
|
||||
child: Container(
|
||||
child: InfoTile(
|
||||
showMessage: true,
|
||||
message: 'message',
|
||||
child: Text('test'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('message'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should hide when passing showMessage: false',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: Scaffold(
|
||||
body: Portal(
|
||||
child: Container(
|
||||
child: InfoTile(
|
||||
showMessage: false,
|
||||
message: 'message',
|
||||
child: Text('test'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('message'), findsNothing);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -38,16 +38,17 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.pump(Duration(milliseconds: 1000));
|
||||
expect(find.byKey(Key('MessageWidget')), findsOneWidget);
|
||||
expect(find.text('Thread Reply'), findsOneWidget);
|
||||
expect(find.text('Reply'), findsOneWidget);
|
||||
expect(find.text('Edit Message'), findsOneWidget);
|
||||
expect(find.text('Delete Message'), findsOneWidget);
|
||||
expect(find.text('Copy Message'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show some actions',
|
||||
(WidgetTester tester) async {
|
||||
@@ -84,9 +85,8 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.pump(Duration(milliseconds: 1000));
|
||||
expect(find.byKey(Key('MessageWidget')), findsOneWidget);
|
||||
expect(find.text('Reply'), findsNothing);
|
||||
expect(find.text('Thread reply'), findsNothing);
|
||||
@@ -95,4 +95,697 @@ void main() {
|
||||
expect(find.text('Copy message'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show custom actions',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
var tapped = false;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
customActions: [
|
||||
MessageAction(
|
||||
leading: Icon(Icons.check),
|
||||
title: Text('title'),
|
||||
onTap: (m) {
|
||||
tapped = true;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.check), findsOneWidget);
|
||||
expect(find.text('title'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('title'));
|
||||
|
||||
expect(tapped, true);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on reply should call the callback',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
var tapped = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
onReplyTap: (m) {
|
||||
return tapped = true;
|
||||
},
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Reply'));
|
||||
|
||||
expect(tapped, true);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on thread reply should call the callback',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
var tapped = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
onThreadReplyTap: (m) {
|
||||
return tapped = true;
|
||||
},
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Thread Reply'));
|
||||
|
||||
expect(tapped, true);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on edit should show the edit bottom sheet',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Edit Message'));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(MessageInput), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on edit should show use the custom builder',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
editMessageInputBuilder: (context, m) {
|
||||
return Text('test');
|
||||
},
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Edit Message'));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on copy should use the callback',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
var tapped = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
onCopyTap: (m) => tapped = true,
|
||||
message: Message(
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Copy Message'));
|
||||
|
||||
expect(tapped, true);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on resend should call send message',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
status: MessageSendingStatus.failed,
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Resend'));
|
||||
|
||||
verify(channel.sendMessage(any)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on resend should call update message if editing the message',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
status: MessageSendingStatus.failed_update,
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Resend Edited Message'));
|
||||
|
||||
verify(channel.updateMessage(any)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on flag message should show the dialog',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
id: 'testid',
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Flag Message'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Flag Message'), findsNWidgets(2));
|
||||
|
||||
await tester.tap(find.text('FLAG'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
verify(client.flagMessage('testid')).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'if flagging a message throws an error the error dialog should appear',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(client.flagMessage(any)).thenThrow(ApiError(
|
||||
'{}',
|
||||
500,
|
||||
));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
id: 'testid',
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Flag Message'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Flag Message'), findsNWidgets(2));
|
||||
|
||||
await tester.tap(find.text('FLAG'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Something went wrong'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'if flagging an already flagged message no error should appear',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(client.flagMessage(any)).thenThrow(ApiError(
|
||||
'{"code":4}',
|
||||
400,
|
||||
));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
id: 'testid',
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Flag Message'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Flag Message'), findsNWidgets(2));
|
||||
|
||||
await tester.tap(find.text('FLAG'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Message flagged'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on delete message should call client.delete',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
id: 'testid',
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Delete Message'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Delete message'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('DELETE'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
verify(channel.deleteMessage(any)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'tapping on delete message should call client.delete',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.deleteMessage(any)).thenThrow(ApiError(
|
||||
'{}',
|
||||
500,
|
||||
));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
theme: themeData,
|
||||
home: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Container(
|
||||
child: MessageActionsModal(
|
||||
message: Message(
|
||||
id: 'testid',
|
||||
text: 'test',
|
||||
user: User(
|
||||
id: 'user-id',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Delete Message'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Delete message'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('DELETE'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Something went wrong'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'checks message input features',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(channelState.messages).thenReturn([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]);
|
||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]));
|
||||
|
||||
when(channelState.typingEvents).thenAnswer((i) => [
|
||||
User(id: 'other-user', extraData: {'name': 'demo'})
|
||||
]);
|
||||
when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([
|
||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||
]));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: MessageInput(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byType(TextField), findsOneWidget);
|
||||
expect(find.byType(StreamSvgIcon), findsNWidgets(8));
|
||||
expect(find.byKey(Key('messageInputText')), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show one thumbs from the picker',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: MessageReactionsModal(
|
||||
message: Message(
|
||||
id: 'test',
|
||||
text: 'test message',
|
||||
user: User(
|
||||
id: 'test-user',
|
||||
),
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pump(Duration(milliseconds: 1000));
|
||||
|
||||
expect(find.byKey(Key('MessageWidget')), findsOneWidget);
|
||||
expect(find.byKey(Key('StreamSvgIcon-Icon_thumbs_up_reaction.svg')),
|
||||
findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show two reactions',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
final testUserId = 'test user';
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
streamChatThemeData: streamTheme,
|
||||
client: client,
|
||||
child: MessageReactionsModal(
|
||||
message: Message(
|
||||
text: 'test message',
|
||||
user: User(
|
||||
id: 'test-user',
|
||||
),
|
||||
latestReactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: testUserId),
|
||||
),
|
||||
Reaction(
|
||||
type: 'love',
|
||||
user: User(id: testUserId),
|
||||
),
|
||||
],
|
||||
),
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
await tester.pump(Duration(milliseconds: 1000));
|
||||
expect(find.byKey(Key('MessageWidget')), findsOneWidget);
|
||||
expect(find.byKey(Key('StreamSvgIcon-Icon_thumbs_up_reaction.svg')),
|
||||
findsNWidgets(2));
|
||||
expect(find.byKey(Key('StreamSvgIcon-Icon_love_reaction.svg')),
|
||||
findsNWidgets(2));
|
||||
expect(find.text(testUserId.split(' ')[0]), findsNWidgets(2));
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'control test',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
final message = Message(
|
||||
id: 'test',
|
||||
text: 'test message',
|
||||
user: User(
|
||||
id: 'test-user',
|
||||
),
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: MessageReactionsModal(
|
||||
message: message,
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pump(Duration(milliseconds: 1000));
|
||||
|
||||
expect(find.byType(MessageWidget), findsOneWidget);
|
||||
final messageWidget =
|
||||
tester.widget<MessageWidget>(find.byType(MessageWidget));
|
||||
expect(messageWidget.message, message);
|
||||
expect(messageWidget.messageTheme, streamTheme.ownMessageTheme);
|
||||
expect(messageWidget.attachmentBorderRadiusGeometry, null);
|
||||
expect(messageWidget.showUserAvatar, DisplayWidget.show);
|
||||
expect(messageWidget.reverse, false);
|
||||
expect(messageWidget.attachmentShape, null);
|
||||
expect(messageWidget.shape, null);
|
||||
expect(messageWidget.onUserAvatarTap, null);
|
||||
expect(messageWidget.showReactions, false);
|
||||
expect(messageWidget.showUsername, false);
|
||||
expect(messageWidget.showThreadReplyIndicator, false);
|
||||
expect(messageWidget.showTimestamp, false);
|
||||
expect(messageWidget.translateUserAvatar, false);
|
||||
expect(messageWidget.showSendingIndicator, false);
|
||||
|
||||
expect(find.byType(ReactionBubble), findsNothing);
|
||||
|
||||
//only one avatar (the message one)
|
||||
expect(find.byType(UserAvatar), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should apply passed parameters',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
final message = Message(
|
||||
id: 'test',
|
||||
text: 'test message',
|
||||
user: User(
|
||||
id: 'test-user',
|
||||
),
|
||||
latestReactions: [
|
||||
Reaction(
|
||||
messageId: 'test',
|
||||
user: User(id: 'testid'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
final onUserAvatarTap = (u) => print('ok');
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: streamTheme,
|
||||
child: MessageReactionsModal(
|
||||
message: message,
|
||||
messageTheme: streamTheme.ownMessageTheme,
|
||||
showUserAvatar: DisplayWidget.gone,
|
||||
reverse: true,
|
||||
attachmentBorderRadiusGeometry: BorderRadius.circular(1),
|
||||
attachmentShape: RoundedRectangleBorder(),
|
||||
showReactions: false,
|
||||
messageShape: RoundedRectangleBorder(),
|
||||
onUserAvatarTap: onUserAvatarTap,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pump(Duration(milliseconds: 1000));
|
||||
|
||||
expect(find.byType(MessageWidget), findsOneWidget);
|
||||
final messageWidget =
|
||||
tester.widget<MessageWidget>(find.byType(MessageWidget));
|
||||
expect(messageWidget.message, message);
|
||||
expect(messageWidget.messageTheme, streamTheme.ownMessageTheme);
|
||||
expect(messageWidget.attachmentBorderRadiusGeometry,
|
||||
BorderRadius.circular(1));
|
||||
expect(messageWidget.showUserAvatar, DisplayWidget.gone);
|
||||
expect(messageWidget.reverse, true);
|
||||
expect(messageWidget.showReactions, false);
|
||||
expect(messageWidget.attachmentShape, RoundedRectangleBorder());
|
||||
expect(messageWidget.shape, RoundedRectangleBorder());
|
||||
expect(messageWidget.showReactions, false);
|
||||
expect(messageWidget.showUsername, false);
|
||||
expect(messageWidget.showThreadReplyIndicator, false);
|
||||
expect(messageWidget.showTimestamp, false);
|
||||
expect(messageWidget.translateUserAvatar, false);
|
||||
expect(messageWidget.showSendingIndicator, false);
|
||||
|
||||
final userAvatar = tester.widget<UserAvatar>(find.byType(UserAvatar));
|
||||
expect(userAvatar.onTap, onUserAvatarTap);
|
||||
|
||||
expect(find.byType(ReactionBubble), findsOneWidget);
|
||||
expect(find.byType(UserAvatar), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show correct message text',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
final themeData = ThemeData();
|
||||
final streamTheme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: MessageText(
|
||||
message: Message(
|
||||
text: 'demo',
|
||||
),
|
||||
messageTheme: streamTheme.otherMessageTheme),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byType(MarkdownBody), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
@@ -8,3 +9,9 @@ class MockClientState extends Mock implements ClientState {}
|
||||
class MockChannel extends Mock implements Channel {}
|
||||
|
||||
class MockChannelState extends Mock implements ChannelClientState {}
|
||||
|
||||
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
|
||||
|
||||
class MockVoidCallback extends Mock {
|
||||
void call();
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
import 'simple_frame.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
testGoldens(
|
||||
'it should show no reactions',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StreamChatTheme(
|
||||
await tester.pumpWidgetBuilder(
|
||||
SimpleFrame(
|
||||
child: StreamChatTheme(
|
||||
data: StreamChatThemeData(),
|
||||
child: Container(
|
||||
child: ReactionBubble(
|
||||
@@ -24,15 +26,173 @@ void main() {
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size(100, 100),
|
||||
);
|
||||
|
||||
expect(find.byKey(Key('StreamSvgIcon-Icon_thumbs_up_reaction.svg')),
|
||||
findsNothing);
|
||||
await screenMatchesGolden(tester, 'reaction_bubble_0');
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show a like',
|
||||
testGoldens(
|
||||
'it should show a like - light theme',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData.light();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
await tester.pumpWidgetBuilder(
|
||||
StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: theme,
|
||||
child: Container(
|
||||
child: ReactionBubble(
|
||||
reactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
],
|
||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size(100, 100),
|
||||
);
|
||||
await screenMatchesGolden(tester, 'reaction_bubble_like_light');
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'it should show a like - dark theme',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData.dark();
|
||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidgetBuilder(
|
||||
StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
||||
child: Container(
|
||||
color: Colors.black,
|
||||
child: ReactionBubble(
|
||||
reactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
],
|
||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size(100, 100),
|
||||
);
|
||||
await screenMatchesGolden(tester, 'reaction_bubble_like_dark');
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'it should show three reactions - light theme',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData.light();
|
||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidgetBuilder(
|
||||
StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
||||
child: Container(
|
||||
color: Colors.black,
|
||||
child: ReactionBubble(
|
||||
reactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
],
|
||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size(140, 140),
|
||||
);
|
||||
await screenMatchesGolden(tester, 'reaction_bubble_3_light');
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'it should show three reactions - dark theme',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData.dark();
|
||||
final theme = StreamChatThemeData.getDefaultTheme(themeData);
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidgetBuilder(
|
||||
StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
||||
child: Container(
|
||||
color: Colors.black,
|
||||
child: ReactionBubble(
|
||||
reactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
],
|
||||
borderColor: theme.ownMessageTheme.reactionsBorderColor,
|
||||
backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor,
|
||||
maskColor: theme.ownMessageTheme.reactionsMaskColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size(140, 140),
|
||||
);
|
||||
await screenMatchesGolden(tester, 'reaction_bubble_3_dark');
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'it should show two reactions with customized ui',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
@@ -41,74 +201,40 @@ void main() {
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
||||
child: Container(
|
||||
child: ReactionBubble(
|
||||
reactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
],
|
||||
borderColor: Colors.black,
|
||||
backgroundColor: Colors.white,
|
||||
maskColor: Colors.white,
|
||||
),
|
||||
await tester.pumpWidgetBuilder(
|
||||
StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
||||
child: Container(
|
||||
child: ReactionBubble(
|
||||
reactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
Reaction(
|
||||
type: 'love',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
Reaction(
|
||||
type: 'unknown',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
],
|
||||
borderColor: Colors.red,
|
||||
backgroundColor: Colors.blue,
|
||||
maskColor: Colors.green,
|
||||
highlightOwnReactions: true,
|
||||
reverse: true,
|
||||
flipTail: true,
|
||||
tailCirclesSpacing: 4,
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size(200, 200),
|
||||
);
|
||||
|
||||
expect(find.byKey(Key('StreamSvgIcon-Icon_thumbs_up_reaction.svg')),
|
||||
findsOneWidget);
|
||||
},
|
||||
);
|
||||
testWidgets(
|
||||
'it should show two reactions',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final themeData = ThemeData();
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: themeData,
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData),
|
||||
child: Container(
|
||||
child: ReactionBubble(
|
||||
reactions: [
|
||||
Reaction(
|
||||
type: 'like',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
Reaction(
|
||||
type: 'love',
|
||||
user: User(id: 'test'),
|
||||
),
|
||||
],
|
||||
borderColor: Colors.black,
|
||||
backgroundColor: Colors.white,
|
||||
maskColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byKey(Key('StreamSvgIcon-Icon_thumbs_up_reaction.svg')),
|
||||
findsOneWidget);
|
||||
expect(find.byKey(Key('StreamSvgIcon-Icon_love_reaction.svg')),
|
||||
findsOneWidget);
|
||||
await screenMatchesGolden(tester, 'reaction_bubble_2');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SimpleFrame extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const SimpleFrame({Key key, @required this.child}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFFFFF),
|
||||
border: Border.all(color: const Color(0xFF9E9E9E)),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show total unread count',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
when(clientState.totalUnreadCount).thenReturn(10);
|
||||
when(clientState.totalUnreadCountStream)
|
||||
.thenAnswer((i) => Stream.value(10));
|
||||
|
||||
var tapped = false;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: SystemMessage(
|
||||
onMessageTap: (m) => tapped = true,
|
||||
message: Message(
|
||||
text: 'demo message',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
await tester.tap(find.byType(SystemMessage));
|
||||
|
||||
expect(find.text('demo message'), findsOneWidget);
|
||||
expect(tapped, true);
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'control golden light',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
when(clientState.totalUnreadCount).thenReturn(10);
|
||||
when(clientState.totalUnreadCountStream)
|
||||
.thenAnswer((i) => Stream.value(10));
|
||||
|
||||
await tester.pumpWidgetBuilder(
|
||||
materialAppWrapper(
|
||||
theme: ThemeData.light(),
|
||||
)(
|
||||
StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Center(
|
||||
child: SystemMessage(
|
||||
message: Message(
|
||||
text: 'demo message',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size.square(200),
|
||||
);
|
||||
|
||||
await screenMatchesGolden(tester, 'system_message_light');
|
||||
},
|
||||
);
|
||||
|
||||
testGoldens(
|
||||
'control golden dark',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
when(clientState.totalUnreadCount).thenReturn(10);
|
||||
when(clientState.totalUnreadCountStream)
|
||||
.thenAnswer((i) => Stream.value(10));
|
||||
|
||||
await tester.pumpWidgetBuilder(
|
||||
materialAppWrapper(
|
||||
theme: ThemeData.dark(),
|
||||
)(
|
||||
StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
child: Center(
|
||||
child: SystemMessage(
|
||||
message: Message(
|
||||
text: 'demo message',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
surfaceSize: Size.square(200),
|
||||
);
|
||||
|
||||
await screenMatchesGolden(tester, 'system_message_dark');
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'control test',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ThreadHeader(
|
||||
parent: Message(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('with '), findsOneWidget);
|
||||
expect(find.byType(ChannelName), findsOneWidget);
|
||||
expect(find.byType(StreamBackButton), findsOneWidget);
|
||||
expect(find.text('Thread Reply'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should apply passed props',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.initialized).thenAnswer((_) => Future.value(true));
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.unreadCount).thenReturn(1);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1));
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
|
||||
var tapped = false;
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: ThreadHeader(
|
||||
parent: Message(),
|
||||
subtitle: Text('subtitle'),
|
||||
leading: Text('leading'),
|
||||
title: Text('title'),
|
||||
onTitleTap: () {
|
||||
tapped = true;
|
||||
},
|
||||
actions: [
|
||||
Text('action'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('title'), findsOneWidget);
|
||||
await tester.tap(find.text('title'));
|
||||
expect(tapped, true);
|
||||
expect(find.text('subtitle'), findsOneWidget);
|
||||
expect(find.text('action'), findsOneWidget);
|
||||
expect(find.text('leading'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show channel typing',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.isMuted).thenReturn(false);
|
||||
when(channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(channelState.messages).thenReturn([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]);
|
||||
when(channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]));
|
||||
|
||||
when(channelState.typingEvents).thenAnswer((i) => [
|
||||
User(id: 'other-user', extraData: {'name': 'demo'})
|
||||
]);
|
||||
when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([
|
||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||
User(id: 'other-user', extraData: {'name': 'demo'}),
|
||||
]));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: TypingIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byKey(Key('typings')), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'it should show total unread count',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
|
||||
when(clientState.totalUnreadCount).thenReturn(10);
|
||||
when(clientState.totalUnreadCountStream)
|
||||
.thenAnswer((i) => Stream.value(10));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: UnreadIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('10'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show nothing if no unread messages',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
when(channel.cid).thenReturn('cid');
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(clientState.channels).thenReturn({
|
||||
channel.cid: channel,
|
||||
});
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channelState.unreadCount).thenReturn(0);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(0));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: UnreadIndicator(
|
||||
cid: channel.cid,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.byType(SizedBox), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'it should show 99+ if more than 99 unreads',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
when(channel.cid).thenReturn('cid');
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(client.state).thenReturn(clientState);
|
||||
when(clientState.user).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(clientState.channels).thenReturn({
|
||||
channel.cid: channel,
|
||||
});
|
||||
when(channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(channel.state).thenReturn(channelState);
|
||||
when(channel.client).thenReturn(client);
|
||||
when(channelState.unreadCount).thenReturn(100);
|
||||
when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(100));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: Scaffold(
|
||||
body: UnreadIndicator(
|
||||
cid: channel.cid,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('99+'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||