From 48a035edc953d042b6c175b80d27a067aae361cc Mon Sep 17 00:00:00 2001 From: DatDang Date: Tue, 17 Dec 2024 17:13:14 +0700 Subject: [PATCH] TF-3341 Integration test web socket for mobile --- backend-docker/jmap.properties | 4 +- .../web_socket_update_ui_scenario.dart | 84 +++++++++++++++++++ .../tests/web_socket/web_socket_test.dart | 25 ++++++ .../utils/scenario_utils_mixin.dart | 72 ++++++++++++++-- .../patrol-integration-test-with-docker.sh | 2 + ...trol-local-integration-test-with-docker.sh | 2 + 6 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 integration_test/scenarios/web_socket_update_ui_scenario.dart create mode 100644 integration_test/tests/web_socket/web_socket_test.dart diff --git a/backend-docker/jmap.properties b/backend-docker/jmap.properties index 68282ebaa..7473fd757 100644 --- a/backend-docker/jmap.properties +++ b/backend-docker/jmap.properties @@ -1 +1,3 @@ -url.prefix=https://a872-222-252-23-73.ngrok-free.app \ No newline at end of file +authentication.strategy.rfc8621=BasicAuthenticationStrategy,com.linagora.tmail.james.jmap.ticket.TicketAuthenticationStrategy +url.prefix= +websocket.url.prefix= \ No newline at end of file diff --git a/integration_test/scenarios/web_socket_update_ui_scenario.dart b/integration_test/scenarios/web_socket_update_ui_scenario.dart new file mode 100644 index 000000000..e4783544f --- /dev/null +++ b/integration_test/scenarios/web_socket_update_ui_scenario.dart @@ -0,0 +1,84 @@ +import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'; + +import '../base/base_scenario.dart'; +import '../models/provisioning_email.dart'; +import '../utils/scenario_utils_mixin.dart'; +import 'login_with_basic_auth_scenario.dart'; + +class WebSocketUpdateUiScenario extends BaseScenario with ScenarioUtilsMixin { + const WebSocketUpdateUiScenario(super.$, { + required this.loginWithBasicAuthScenario, + required this.subject, + required this.content, + }); + + final LoginWithBasicAuthScenario loginWithBasicAuthScenario; + final String subject; + final String content; + + @override + Future execute() async { + await loginWithBasicAuthScenario.execute(); + + await provisionEmail( + [ProvisioningEmail( + toEmail: loginWithBasicAuthScenario.email, + subject: subject, + content: content, + )], + refreshEmailView: false, + ); + await $.pumpAndSettle(); + await _expectEmailVisible(loginWithBasicAuthScenario.email); + await _expectEmailUnreadWithSubject(subject); + await _expectEmailUnstarredWithSubject(subject); + + await simulateUpdateFlagsOfEmailsWithSubjectsFromOutsideCurrentClient( + subjects: [subject], + isRead: true, + ); + await $.pumpAndSettle(); + await _expectEmailReadWithSubject(subject); + + await simulateUpdateFlagsOfEmailsWithSubjectsFromOutsideCurrentClient( + subjects: [subject], + isStar: true, + ); + await $.pumpAndSettle(); + await _expectEmailStarredWithSubject(subject); + } + + Future _expectEmailVisible(String email) => expectViewVisible($(email)); + + Future _expectEmailUnreadWithSubject(String subject) => expectViewVisible( + $(EmailTileBuilder) + .which( + (widget) => widget.presentationEmail.subject == subject + && !widget.presentationEmail.hasRead + ), + ); + + Future _expectEmailReadWithSubject(String subject) => expectViewVisible( + $(EmailTileBuilder) + .which( + (widget) => widget.presentationEmail.subject == subject + && widget.presentationEmail.hasRead + ), + ); + + Future _expectEmailUnstarredWithSubject(String subject) => expectViewVisible( + $(EmailTileBuilder) + .which( + (widget) => widget.presentationEmail.subject == subject + && !widget.presentationEmail.hasStarred + ), + ); + + Future _expectEmailStarredWithSubject(String subject) => expectViewVisible( + $(EmailTileBuilder) + .which( + (widget) => widget.presentationEmail.subject == subject + && widget.presentationEmail.hasStarred + ), + ); +} \ No newline at end of file diff --git a/integration_test/tests/web_socket/web_socket_test.dart b/integration_test/tests/web_socket/web_socket_test.dart new file mode 100644 index 000000000..0168229ab --- /dev/null +++ b/integration_test/tests/web_socket/web_socket_test.dart @@ -0,0 +1,25 @@ +import '../../base/test_base.dart'; +import '../../scenarios/login_with_basic_auth_scenario.dart'; +import '../../scenarios/web_socket_update_ui_scenario.dart'; + +void main() { + TestBase().runPatrolTest( + description: 'Should see thread view updated per web socket message', + test: ($) async { + final loginWithBasicAuthScenario = LoginWithBasicAuthScenario($, + username: const String.fromEnvironment('USERNAME'), + hostUrl: const String.fromEnvironment('BASIC_AUTH_URL'), + email: const String.fromEnvironment('BASIC_AUTH_EMAIL'), + password: const String.fromEnvironment('PASSWORD'), + ); + + final webSocketUpdateUiScenario = WebSocketUpdateUiScenario($, + loginWithBasicAuthScenario: loginWithBasicAuthScenario, + subject: 'web socket subject', + content: 'web socket content', + ); + + await webSocketUpdateUiScenario.execute(); + } + ); +} \ No newline at end of file diff --git a/integration_test/utils/scenario_utils_mixin.dart b/integration_test/utils/scenario_utils_mixin.dart index 76e5b35d9..6c6ac4bfe 100644 --- a/integration_test/utils/scenario_utils_mixin.dart +++ b/integration_test/utils/scenario_utils_mixin.dart @@ -1,16 +1,15 @@ import 'dart:async'; -import 'dart:io'; +import 'dart:io' hide HttpClient; import 'package:collection/collection.dart'; import 'package:get/get.dart'; +import 'package:jmap_dart_client/http/http_client.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/jmap/jmap_request.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; -import 'package:model/email/attachment.dart'; -import 'package:model/email/email_action_type.dart'; -import 'package:model/extensions/session_extension.dart'; -import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:model/extensions/presentation_mailbox_extension.dart'; -import 'package:model/upload/file_info.dart'; +import 'package:jmap_dart_client/jmap/mail/email/set/set_email_method.dart'; +import 'package:model/model.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_send_email_interactor.dart'; @@ -22,11 +21,15 @@ import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_ident import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart'; +import 'package:tmail_ui_user/main/error/capability_validator.dart'; import '../models/provisioning_email.dart'; mixin ScenarioUtilsMixin { - Future provisionEmail(List provisioningEmails) async { + Future provisionEmail( + List provisioningEmails, { + bool refreshEmailView = true, + }) async { ComposerBindings().dependencies(); final mailboxDashBoardController = Get.find(); @@ -62,11 +65,62 @@ mixin ScenarioUtilsMixin { })); // Refresh view after provisioning emails - threadController.refreshAllEmail(); + if (refreshEmailView) { + threadController.refreshAllEmail(); + } ComposerBindings().dispose(); } + Future simulateUpdateFlagsOfEmailsWithSubjectsFromOutsideCurrentClient({ + required List subjects, + bool? isRead, + bool? isStar, + }) async { + final mailboxDashBoardController = Get.find(); + final emails = mailboxDashBoardController + .emailsInCurrentMailbox + .where((presentationEmail) => subjects.contains( + presentationEmail.subject + )) + .toList(); + final session = mailboxDashBoardController.sessionCurrent; + final accountId = mailboxDashBoardController.accountId.value; + if (session == null || accountId == null) return; + + final requestBuilder = JmapRequestBuilder( + Get.find(), + ProcessingInvocation(), + ); + final capabilities = {}; + + // Mark as read/unread + if (isRead != null) { + final markEmailAsReadMethod = SetEmailMethod(accountId) + ..addUpdates(emails.listEmailIds.generateMapUpdateObjectMarkAsRead( + isRead ? ReadActions.markAsRead : ReadActions.markAsUnread + )); + requestBuilder.invocation(markEmailAsReadMethod); + capabilities.addAll(markEmailAsReadMethod + .requiredCapabilities + .toCapabilitiesSupportTeamMailboxes(session, accountId)); + } + + // Mark as star/unstar + if (isStar != null) { + final markEmailAsStarMethod = SetEmailMethod(accountId) + ..addUpdates(emails.listEmailIds.generateMapUpdateObjectMarkAsStar( + isStar ? MarkStarAction.markStar : MarkStarAction.unMarkStar + )); + requestBuilder.invocation(markEmailAsStarMethod); + capabilities.addAll(markEmailAsStarMethod + .requiredCapabilities + .toCapabilitiesSupportTeamMailboxes(session, accountId)); + } + + await (requestBuilder..usings(capabilities)).build().execute(); + } + Future preparingTxtFile(String content) async { final directory = await getTemporaryDirectory(); final file = File('${directory.path}/test.txt'); diff --git a/scripts/patrol-integration-test-with-docker.sh b/scripts/patrol-integration-test-with-docker.sh index 3fb401127..bc0c85075 100755 --- a/scripts/patrol-integration-test-with-docker.sh +++ b/scripts/patrol-integration-test-with-docker.sh @@ -28,7 +28,9 @@ openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -out jwt_privatekey openssl rsa -in jwt_privatekey -pubout -out jwt_publickey # Replace content of jmap.properties with url.prefix=$BASIC_AUTH_URL +# and websocket.url.prefix=ws${BASIC_AUTH_URL:4} sed -i "s|url.prefix=.*|url.prefix=$BASIC_AUTH_URL|" jmap.properties +sed -i '' "s|websocket.url.prefix=.*|websocket.url.prefix=ws${BASIC_AUTH_URL:4}|" jmap.properties echo "Starting services and adding users..." docker compose up -d diff --git a/scripts/patrol-local-integration-test-with-docker.sh b/scripts/patrol-local-integration-test-with-docker.sh index 50ddd8241..f35e8f2af 100755 --- a/scripts/patrol-local-integration-test-with-docker.sh +++ b/scripts/patrol-local-integration-test-with-docker.sh @@ -28,7 +28,9 @@ openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -out jwt_privatekey openssl rsa -in jwt_privatekey -pubout -out jwt_publickey # Replace content of jmap.properties with url.prefix=$BASIC_AUTH_URL +# and websocket.url.prefix=ws${BASIC_AUTH_URL:4} sed -i '' "s|url.prefix=.*|url.prefix=$BASIC_AUTH_URL|" jmap.properties +sed -i '' "s|websocket.url.prefix=.*|websocket.url.prefix=ws${BASIC_AUTH_URL:4}|" jmap.properties echo "Starting services and adding users..." docker compose up -d