Skip to content

Commit

Permalink
Bug: no validation and confirmation on comments (#2349)
Browse files Browse the repository at this point in the history
* Bug: no validation and confirmation on comments

* add more tests

* fixing coverage

* fixing coverage

* minor changes

* making requested changes

* minor changes

* adding more meaningful comments and refactoring

* fixing previous docs
  • Loading branch information
Dante291 authored Jan 27, 2024
1 parent 98557c0 commit f314e5c
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 31 deletions.
32 changes: 24 additions & 8 deletions lib/services/comment_service.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:talawa/enums/enums.dart';
import 'package:talawa/locator.dart';
import 'package:talawa/services/database_mutation_functions.dart';
import 'package:talawa/services/navigation_service.dart';
import 'package:talawa/utils/comment_queries.dart';
import 'package:talawa/utils/post_queries.dart';

Expand All @@ -12,8 +14,10 @@ import 'package:talawa/utils/post_queries.dart';
class CommentService {
CommentService() {
_dbFunctions = locator<DataBaseMutationFunctions>();
_navigationService = locator<NavigationService>();
}
late DataBaseMutationFunctions _dbFunctions;
late NavigationService _navigationService;

/// This function is used to add comment on the post.
///
Expand All @@ -26,14 +30,26 @@ class CommentService {
/// None
Future<void> createComments(String postId, String text) async {
final String createCommentQuery = CommentQueries().createComment();
final result = await _dbFunctions.gqlAuthMutation(
createCommentQuery,
variables: {
'postId': postId, //Add your variables here
'text': text,
},
);
return result;

try {
await _dbFunctions.gqlAuthMutation(
createCommentQuery,
variables: {
'postId': postId, //Add your variables here
'text': text,
},
);

_navigationService.showTalawaErrorSnackBar(
"Comment sent",
MessageType.info,
);
} on Exception catch (_) {
_navigationService.showTalawaErrorSnackBar(
"Something went wrong",
MessageType.error,
);
}
}

/// This function is used to get all comments on the post.
Expand Down
80 changes: 66 additions & 14 deletions lib/views/after_auth_screens/feed/individual_post.dart
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
// ignore_for_file: talawa_good_doc_comments, talawa_api_doc
import 'package:flutter/material.dart';
import 'package:talawa/models/comment/comment_model.dart';
import 'package:talawa/models/post/post_model.dart';
import 'package:talawa/services/size_config.dart';
import 'package:talawa/utils/app_localization.dart';
import 'package:talawa/view_model/widgets_view_models/comments_view_model.dart';
import 'package:talawa/views/base_view.dart';
import 'package:talawa/widgets/post_widget.dart';

// Global State, should be removed in next few iterations

/// Comment view model.
late CommentsViewModel _commentViewModel;

/// IndividualPostView returns a widget that has mutable state _IndividualPostViewState.
class IndividualPostView extends StatefulWidget {
const IndividualPostView({super.key, required this.post});

/// Individual Post.
final Post post;

@override
_IndividualPostViewState createState() => _IndividualPostViewState();
}

/// _IndividualPostViewState returns a widget to show Individual Post View state. This widget
/// includes to send the comment on the post, shows list of all users liked and commented on the post.
class _IndividualPostViewState extends State<IndividualPostView> {
final TextEditingController _controller = TextEditingController();
bool _isCommentValid = false;

@override
Widget build(BuildContext context) {
Expand All @@ -41,9 +44,17 @@ class _IndividualPostViewState extends State<IndividualPostView> {
key: const Key('indi_post_tf_key'),
controller: _controller,
textInputAction: TextInputAction.send,
onSubmitted: (msg) {
_commentViewModel.createComment(msg);
_controller.text = "";
onChanged: (msg) {
if (msg.isEmpty && _isCommentValid == true) {
setState(() {
_isCommentValid = false;
});
}
if (msg.isEmpty == false && _isCommentValid == false) {
setState(() {
_isCommentValid = true;
});
}
},
textAlign: TextAlign.start,
decoration: InputDecoration(
Expand All @@ -59,14 +70,31 @@ class _IndividualPostViewState extends State<IndividualPostView> {
),
// Button to send the comment.
TextButton(
onPressed: () {
_commentViewModel.createComment(_controller.text);
_controller.text = "";
},
key: const Key('sendButton'),
style: _isCommentValid == false
? ButtonStyle(
overlayColor:
MaterialStateProperty.all(Colors.transparent),
)
: null,
//check if button is enabled when comment is valid
onPressed: _isCommentValid
? () {
_commentViewModel.createComment(_controller.text);
_controller.text = "";

setState(() {
_isCommentValid = false;
});
}
: null,
child: Text(
AppLocalizations.of(context)!.strictTranslate(
"Send",
),
style: !_isCommentValid
? const TextStyle(color: Colors.grey)
: null,
),
),
],
Expand All @@ -79,7 +107,9 @@ class _IndividualPostViewState extends State<IndividualPostView> {
post: widget.post,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
padding: EdgeInsets.symmetric(
horizontal: SizeConfig.screenHeight! * 0.010,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expand All @@ -104,9 +134,17 @@ class _IndividualPostViewState extends State<IndividualPostView> {
}
}

/// Generates a `Padding` widget with customizable vertical padding around a text element.
///
/// **params**:
/// * `context`: The build context in which the padding method is called.
/// * `text`: The text on which padding should be applied.
///
/// **returns**:
/// * `Padding`: Padding widget with vertical padding applied to the provided text.
Padding buildPadding(BuildContext context, String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
padding: EdgeInsets.symmetric(vertical: SizeConfig.screenHeight! * 0.006),
child: Text(
AppLocalizations.of(context)!.strictTranslate(text),
style: Theme.of(context).textTheme.titleLarge,
Expand All @@ -121,6 +159,7 @@ class IndividualPageLikeSection extends StatelessWidget {
required this.usersLiked,
});

/// Represents a list of users who have liked a post.
final List<LikedBy> usersLiked;

@override
Expand All @@ -147,14 +186,20 @@ class IndividualPageLikeSection extends StatelessWidget {
}
}

/// IndividualPostCommentSection returns a widget that show the list of all the users commented on the post.
/// Widget representing the comment section of an individual post.
///
/// The `IndividualPostCommentSection` widget displays a list of comments on a post.
class IndividualPostCommentSection extends StatelessWidget {
const IndividualPostCommentSection({
super.key,
required this.comments,
required this.postID,
});

/// List of comments on a post.
final List<Comments> comments;

/// ID of a post with associated comments.
final String postID;

@override
Expand Down Expand Up @@ -186,6 +231,7 @@ class CommentTemplate extends StatelessWidget {
required this.comment,
});

/// Instance of comment.
final Comment comment;

@override
Expand Down Expand Up @@ -228,7 +274,13 @@ class CommentTemplate extends StatelessWidget {
}
}

/// likedUserCircleAvatar returns a widget of the individual user liked the post.
/// Generates a Circle Avatar representing a user who liked the post.
///
/// **params**:
/// * `user`: The user who liked the post, represented by the `LikedBy` class.
///
/// **returns**:
/// * `Widget`: Circle Avatar of the user who liked the post.
Widget likedUserCircleAvatar(LikedBy user) {
return const Padding(
padding: EdgeInsets.only(right: 10.0, bottom: 16.0),
Expand Down
29 changes: 29 additions & 0 deletions test/service_tests/comment_service_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:mockito/mockito.dart';
import 'package:talawa/enums/enums.dart';
import 'package:talawa/locator.dart';
import 'package:talawa/services/comment_service.dart';
import 'package:talawa/services/database_mutation_functions.dart';
Expand Down Expand Up @@ -43,6 +44,34 @@ void main() {
'hey Ayush here!',
);
});
test('test for createComments when throws exception', () async {
final dataBaseMutationFunctions = locator<DataBaseMutationFunctions>();

final query = CommentQueries().createComment();
when(
dataBaseMutationFunctions.gqlAuthMutation(
query,
variables: {
'postId': 'ayush post',
'text': 'hey Ayush here!',
},
),
).thenThrow(Exception('Your error message here'));

final service = CommentService();

await service.createComments(
'ayush post',
'hey Ayush here!',
);

verify(
navigationService.showTalawaErrorSnackBar(
"Something went wrong",
MessageType.error,
),
).called(1);
});
test('test for getCommentsForPost', () async {
final dataBaseMutationFunctions = locator<DataBaseMutationFunctions>();
final String getCommmentQuery =
Expand Down
84 changes: 75 additions & 9 deletions test/views/after_auth_screens/feed/individual_post_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,22 +186,88 @@ void main() {
});

group("testing Individual Post View ", () {
testWidgets("test IndividualPostWidget", (WidgetTester tester) async {
testWidgets("Check if Send button is disabled",
(WidgetTester tester) async {
await tester.pumpWidget(createIndividualPostViewWidget(post));
await tester.pumpAndSettle();

expect(find.byKey(const Key('indi_post_tf_key')), findsOneWidget);
await tester.enterText(
find.byKey(const Key('indi_post_tf_key')),
'Test Comment',
);
final textFieldFinder = find.byKey(const Key('indi_post_tf_key'));
final textButtonFinder = find.byKey(const Key('sendButton'));
expect(tester.widget<TextButton>(textButtonFinder).enabled, isFalse);

// Clear the text field
await tester.enterText(textFieldFinder, '');
await tester.pumpAndSettle();
// The button should be disabled after clearing the text
expect(tester.widget<TextButton>(textButtonFinder).enabled, isFalse);

expect(find.byType(NewsPost), findsOneWidget);
expect(find.byType(IndividualPageLikeSection), findsOneWidget);
expect(find.byType(IndividualPostCommentSection), findsOneWidget);
});
testWidgets("Check if Send button is enabled and working",
(WidgetTester tester) async {
await tester.pumpWidget(createIndividualPostViewWidget(post));
await tester.pumpAndSettle();

final textFieldFinder = find.byKey(const Key('indi_post_tf_key'));
final textButtonFinder = find.byKey(const Key('sendButton'));
expect(tester.widget<TextButton>(textButtonFinder).enabled, isFalse);

// Clear the text field
await tester.enterText(textFieldFinder, '');
await tester.pumpAndSettle();
// The button should be disabled after clearing the text
expect(tester.widget<TextButton>(textButtonFinder).enabled, isFalse);

//verify that the text is entered in the text field
// Enter non-empty text
await tester.enterText(textFieldFinder, 'Test Comment');
await tester.pumpAndSettle();
// The button should now be enabled
expect(tester.widget<TextButton>(textButtonFinder).enabled, isTrue);

// Verify that the text field is cleared
expect(find.text('Test Comment'), findsOneWidget);

// Tap the send button.
await tester.tap(find.byType(TextButton));
// Tap the send button
await tester.tap(textButtonFinder);
await tester.pumpAndSettle();

expect(find.byType(NewsPost), findsOneWidget);
expect(find.byType(IndividualPageLikeSection), findsOneWidget);
expect(find.byType(IndividualPostCommentSection), findsOneWidget);
});
testWidgets("Checking if state changes when text is cleared",
(WidgetTester tester) async {
await tester.pumpWidget(createIndividualPostViewWidget(post));
await tester.pumpAndSettle();

final textFieldFinder = find.byKey(const Key('indi_post_tf_key'));
final textButtonFinder = find.byKey(const Key('sendButton'));
expect(tester.widget<TextButton>(textButtonFinder).enabled, isFalse);

// Clear the text field
await tester.enterText(textFieldFinder, '');
await tester.pumpAndSettle();
// The button should be disabled after clearing the text
expect(tester.widget<TextButton>(textButtonFinder).enabled, isFalse);

// Enter non-empty text
await tester.enterText(textFieldFinder, 'Test Comment');
await tester.pumpAndSettle();
// The button should now be enabled
expect(tester.widget<TextButton>(textButtonFinder).enabled, isTrue);

// Verify that the text field is cleared
expect(find.text('Test Comment'), findsOneWidget);

// Testing if state changes back
await tester.enterText(textFieldFinder, '');
await tester.pumpAndSettle();
expect(tester.widget<TextButton>(textButtonFinder).enabled, isFalse);

// Tap the send button
await tester.tap(textButtonFinder);
await tester.pumpAndSettle();

expect(find.byType(NewsPost), findsOneWidget);
Expand Down

0 comments on commit f314e5c

Please sign in to comment.