diff --git a/lib/core/global_keys.dart b/lib/core/global_keys.dart index e447d685467c50c75bcc576c8d6ef96023f48219..c2eed85c28254b22d1fe1045ce9780f2afe5d99f 100644 --- a/lib/core/global_keys.dart +++ b/lib/core/global_keys.dart @@ -6,4 +6,5 @@ class GlobalKeys { static GlobalKey<State<BottomNavigationBar>> rootBottomNavigationBarKey = GlobalKey(); static String navigationBarCurrentIndexKey = "navigation_bar_current_index"; + static String currentMergeRequestWebUrlKey = "current_merge_request_web_url"; } diff --git a/lib/core/user_provider.dart b/lib/core/user_provider.dart index 27553e52eedc427d0ef594ed6e2725b0d81cd6c8..01a6d33a9bce8427812ee82d00b851683a631f97 100644 --- a/lib/core/user_provider.dart +++ b/lib/core/user_provider.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:jihu_gitlab_app/core/domain/token.dart'; +import 'package:jihu_gitlab_app/core/global_keys.dart'; import 'package:jihu_gitlab_app/core/id.dart'; import 'package:jihu_gitlab_app/core/local_storage.dart'; import 'package:jihu_gitlab_app/core/log_helper.dart'; @@ -169,6 +170,8 @@ class UserProvider extends ChangeNotifier { static int? get userId => _users.isEmpty ? null : _users[0].id; static bool get isLoggedOutByUser => _isLoggedOutByUser; + + static Future<String> get mergeRequestWebUrl async => await LocalStorage.get(GlobalKeys.currentMergeRequestWebUrlKey, ''); } class UserInfo { diff --git a/lib/modules/mr/merge_request_page.dart b/lib/modules/mr/merge_request_page.dart index a0dcb8442642762688f983d41e744f510126fc6f..4fb04a0a5c0f99a89de6673a2d5fdf4fe3f21c28 100644 --- a/lib/modules/mr/merge_request_page.dart +++ b/lib/modules/mr/merge_request_page.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:jihu_gitlab_app/core/global_keys.dart'; import 'package:jihu_gitlab_app/core/load_state.dart'; +import 'package:jihu_gitlab_app/core/local_storage.dart'; import 'package:jihu_gitlab_app/core/settings/settings_provider.dart'; import 'package:jihu_gitlab_app/core/string_extension.dart'; import 'package:jihu_gitlab_app/core/user_provider.dart'; @@ -38,19 +40,31 @@ class _MergeRequestPageState extends State<MergeRequestPage> { @override Widget build(BuildContext context) { + _saveWebUrl(); return Consumer<UserProvider>( builder: (context, _, child) => _buildScaffold(), ); } + Future<void> _saveWebUrl() async { + if (_model.mr != null && (await UserProvider.mergeRequestWebUrl).isEmpty) { + LocalStorage.save(GlobalKeys.currentMergeRequestWebUrlKey, _model.mr!.webUrl); + } + } + Widget _buildScaffold() { - return Scaffold( - appBar: CommonAppBar( - showLeading: true, - title: Text(_model.pageTitle), - ), - body: SafeArea(child: _buildBody()), - ); + return WillPopScope( + child: Scaffold( + appBar: CommonAppBar( + showLeading: true, + title: Text(_model.pageTitle), + ), + body: SafeArea(child: _buildBody()), + ), + onWillPop: () { + LocalStorage.remove(GlobalKeys.currentMergeRequestWebUrlKey); + return Future(() => true); + }); } Widget _buildBody() { diff --git a/lib/modules/mr/models/graphql_request_body.dart b/lib/modules/mr/models/graphql_request_body.dart index 2f58d847424c794c281e9a8cddb83c69a7707269..d14b3c177923f4006ef8a30d3e3aa79e9c136020 100644 --- a/lib/modules/mr/models/graphql_request_body.dart +++ b/lib/modules/mr/models/graphql_request_body.dart @@ -3,7 +3,7 @@ Map<String, dynamic> getMergeRequestDetailsGraphQLRequestBody(String fullPath, i "operationName": "getMergeRequest", "variables": {"fullPath": fullPath, "mrIid": '$mrIid'}, "query": - "query getMergeRequest(\$fullPath: ID!, \$mrIid: String!) { project(fullPath: \$fullPath) { id name nameWithNamespace path fullPath webUrl allowMergeOnSkippedPipeline onlyAllowMergeIfAllStatusChecksPassed onlyAllowMergeIfAllDiscussionsAreResolved onlyAllowMergeIfPipelineSucceeds removeSourceBranchAfterMerge mergeRequest(iid: \$mrIid) { id iid state title description draft createdAt rebaseInProgress allowCollaboration approved approvalsLeft approvalsRequired approvalState { approvalRulesOverwritten rules { id name type approvalsRequired eligibleApprovers { ...UserCoreFragment } } } approvedBy { nodes { ...UserCoreFragment } } assignees { nodes { id name } } commitCount commits { nodes { id shortId sha title description } } author { id name username avatarUrl } autoMergeEnabled autoMergeStrategy availableAutoMergeStrategies conflicts mergeError mergeOngoing detailedMergeStatus mergeStatusEnum mergeUser { ...UserCoreFragment } mergeWhenPipelineSucceeds mergeable mergeableDiscussionsState mergedAt rebaseCommitSha rebaseInProgress shouldBeRebased shouldRemoveSourceBranch sourceBranch sourceBranchExists sourceBranchProtected targetBranch targetBranchExists squash squashOnMerge defaultMergeCommitMessage defaultSquashCommitMessage headPipeline { id iid active cancelable complete coverage status ref detailedStatus { icon label } codeQualityReportSummary { count } codeQualityReports { nodes { line path description severity } } jobs { nodes { id name active status allowFailure duration startedAt finishedAt stage { id name status jobs { nodes { id name status allowFailure } } } } } warnings warningMessages { content } createdAt finishedAt committedAt startedAt } userPermissions { adminMergeRequest canMerge pushToSourceBranch readMergeRequest removeSourceBranch } } }}fragment UserCoreFragment on UserCore { id username name state avatarUrl webUrl publicEmail commitEmail}" + "query getMergeRequest(\$fullPath: ID!, \$mrIid: String!) { project(fullPath: \$fullPath) { id name nameWithNamespace path fullPath webUrl allowMergeOnSkippedPipeline onlyAllowMergeIfAllStatusChecksPassed onlyAllowMergeIfAllDiscussionsAreResolved onlyAllowMergeIfPipelineSucceeds removeSourceBranchAfterMerge mergeRequest(iid: \$mrIid) { id iid state title description webUrl draft createdAt rebaseInProgress allowCollaboration approved approvalsLeft approvalsRequired approvalState { approvalRulesOverwritten rules { id name type approvalsRequired eligibleApprovers { ...UserCoreFragment } } } approvedBy { nodes { ...UserCoreFragment } } assignees { nodes { id name } } commitCount commits { nodes { id shortId sha title description } } author { id name username avatarUrl } autoMergeEnabled autoMergeStrategy availableAutoMergeStrategies conflicts mergeError mergeOngoing detailedMergeStatus mergeStatusEnum mergeUser { ...UserCoreFragment } mergeWhenPipelineSucceeds mergeable mergeableDiscussionsState mergedAt rebaseCommitSha rebaseInProgress shouldBeRebased shouldRemoveSourceBranch sourceBranch sourceBranchExists sourceBranchProtected targetBranch targetBranchExists squash squashOnMerge defaultMergeCommitMessage defaultSquashCommitMessage headPipeline { id iid active cancelable complete coverage status ref detailedStatus { icon label } codeQualityReportSummary { count } codeQualityReports { nodes { line path description severity } } jobs { nodes { id name active status allowFailure duration startedAt finishedAt stage { id name status jobs { nodes { id name status allowFailure } } } } } warnings warningMessages { content } createdAt finishedAt committedAt startedAt } userPermissions { adminMergeRequest canMerge pushToSourceBranch readMergeRequest removeSourceBranch } } }}fragment UserCoreFragment on UserCore { id username name state avatarUrl webUrl publicEmail commitEmail}" }; } diff --git a/lib/modules/mr/models/merge_request.dart b/lib/modules/mr/models/merge_request.dart index 8e58807fb2e0814488472bd780f97aca065b5d21..ffe9f3d63ad6ddcc5ba01cb467152e4fa106530d 100644 --- a/lib/modules/mr/models/merge_request.dart +++ b/lib/modules/mr/models/merge_request.dart @@ -36,6 +36,7 @@ class MergeRequest { final bool mergeable; final bool shouldBeRebased; final String? mergeError; + final String webUrl; MergeRequest._( this.iid, @@ -62,38 +63,41 @@ class MergeRequest { this._approvalRules, this.mergeable, this.shouldBeRebased, - this.mergeError); + this.mergeError, + this.webUrl); factory MergeRequest.fromJson(Map<String, dynamic> json) { var mergeRequest = json['mergeRequest'] ?? {}; var headPipeline = mergeRequest['headPipeline'] ?? {}; List<dynamic> rules = mergeRequest['approvalState']?['rules'] ?? []; return MergeRequest._( - int.parse(mergeRequest['iid'] ?? ''), - Id.fromGid(json['id'] ?? '/0').id, - json['fullPath'] ?? '', - json['name'] ?? '', - mergeRequest['sourceBranch'] ?? '', - mergeRequest['targetBranch'] ?? '', - MergeRequestState.valueOf(mergeRequest['state'] ?? ''), - mergeRequest['title'] ?? '', - mergeRequest['description'] ?? '', - Assignee.fromGraphQLJson(mergeRequest['author'] ?? {}), - mergeRequest['headPipeline'] == null ? null : Pipeline.fromJson(headPipeline), - Time.init(mergeRequest['createdAt'] ?? ''), - mergeRequest['rebaseInProgress'] ?? false, - DetailMergeStatus.valueOf(mergeRequest['detailedMergeStatus'] ?? DetailMergeStatus.unchecked.name), - mergeRequest['userPermissions']?['canMerge'] ?? false, - mergeRequest['conflicts'] ?? false, - mergeRequest['commitCount'] ?? 0, - mergeRequest['draft'] ?? false, - Commits.fromJson(mergeRequest['commits']['nodes']), - mergeRequest['headPipeline'] == null ? null : CodeQualityReport.fromJson(headPipeline), - Jobs.fromJson(headPipeline['jobs']?['nodes'] ?? []), - rules.map((e) => ApprovalRule.fromGraphQLJson(e)).toList(), - mergeRequest['mergeable'] ?? false, - mergeRequest['shouldBeRebased'] ?? false, - mergeRequest['mergeError']); + int.parse(mergeRequest['iid'] ?? ''), + Id.fromGid(json['id'] ?? '/0').id, + json['fullPath'] ?? '', + json['name'] ?? '', + mergeRequest['sourceBranch'] ?? '', + mergeRequest['targetBranch'] ?? '', + MergeRequestState.valueOf(mergeRequest['state'] ?? ''), + mergeRequest['title'] ?? '', + mergeRequest['description'] ?? '', + Assignee.fromGraphQLJson(mergeRequest['author'] ?? {}), + mergeRequest['headPipeline'] == null ? null : Pipeline.fromJson(headPipeline), + Time.init(mergeRequest['createdAt'] ?? ''), + mergeRequest['rebaseInProgress'] ?? false, + DetailMergeStatus.valueOf(mergeRequest['detailedMergeStatus'] ?? DetailMergeStatus.unchecked.name), + mergeRequest['userPermissions']?['canMerge'] ?? false, + mergeRequest['conflicts'] ?? false, + mergeRequest['commitCount'] ?? 0, + mergeRequest['draft'] ?? false, + Commits.fromJson(mergeRequest['commits']['nodes']), + mergeRequest['headPipeline'] == null ? null : CodeQualityReport.fromJson(headPipeline), + Jobs.fromJson(headPipeline['jobs']?['nodes'] ?? []), + rules.map((e) => ApprovalRule.fromGraphQLJson(e)).toList(), + mergeRequest['mergeable'] ?? false, + mergeRequest['shouldBeRebased'] ?? false, + mergeRequest['mergeError'], + mergeRequest['webUrl'] ?? '', + ); } List<ApprovalRule> get approvalRules => _approvalRules.where((e) => e.ruleType != 'ANY_APPROVER').toList(); diff --git a/lib/modules/mr/models/merge_request_model.dart b/lib/modules/mr/models/merge_request_model.dart index 43152341051bc2ea4b28e80cbb13eb149fc72a7a..80837e5c8392bad326a7dbb31600de9fa7a88698 100644 --- a/lib/modules/mr/models/merge_request_model.dart +++ b/lib/modules/mr/models/merge_request_model.dart @@ -38,8 +38,7 @@ class MergeRequestModel { Future<void> _getBasicMr(void Function()? setState) { return _makeRefresh(() async { var resp = await HttpClient.instance().post('/api/graphql', getMergeRequestDetailsGraphQLRequestBody(projectFullPath, mergeRequestIid)); - var result = MergeRequest.fromJson(resp.body()['data']['project']); - mr = result; + mr = MergeRequest.fromJson(resp.body()['data']['project']); }, setState); } diff --git a/lib/modules/root/root_page.dart b/lib/modules/root/root_page.dart index b9b3a876826d164e9c7b6c6423f4e3454a03e654..3e75e8d902f9a4d3d799e356400510d8a7a0a05c 100644 --- a/lib/modules/root/root_page.dart +++ b/lib/modules/root/root_page.dart @@ -8,6 +8,7 @@ import 'package:jihu_gitlab_app/core/layout/adaptive.dart'; import 'package:jihu_gitlab_app/core/local_storage.dart'; import 'package:jihu_gitlab_app/core/plugins/install_apk_plugin.dart'; import 'package:jihu_gitlab_app/core/user_provider.dart'; +import 'package:jihu_gitlab_app/core/widgets/paste_type_url_navigation.dart'; import 'package:jihu_gitlab_app/l10n/jihu_localizations.dart'; import 'package:jihu_gitlab_app/modules/privacy_policy/privacy_policy_page.dart'; import 'package:provider/provider.dart'; @@ -48,6 +49,17 @@ class _RootPageState extends State<RootPage> { if (!agreedPrivacyPolicy) { Future.delayed(Duration.zero, _showPrivacyPolicyDialog); } + parseMergeRequest(); + } + + Future<void> parseMergeRequest() async { + var url = await UserProvider.mergeRequestWebUrl; + if (url.isEmpty) { + return; + } + Future.delayed(Duration.zero, () { + PasteTypeUrlNavigationModel().parseAndToward(url, context); + }); } @override diff --git a/test/integration_tests/helper/core.dart b/test/integration_tests/helper/core.dart index e67f7a2f201b428a68058b55224c05ce23aa26f4..64d3971878e4333f88448fd3a201edd85d3ae4d4 100644 --- a/test/integration_tests/helper/core.dart +++ b/test/integration_tests/helper/core.dart @@ -1,3 +1,28 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jihu_gitlab_app/core/net/http_client.dart'; +import 'package:jihu_gitlab_app/core/net/response.dart'; +import 'package:jihu_gitlab_app/core/user_provider.dart'; +import 'package:mockito/mockito.dart'; + import '../../core/net/http_request_test.mocks.dart'; +import '../../mocker/tester.dart'; +import '../../test_data/user.dart'; final client = MockHttpClient(); + +void saasLogin({VoidCallback? onMock}) { + expect(UserProvider.authorized, isFalse); + UserProvider().resetToken(Tester.token()); + UserProvider().reset(Tester.user()); + UserProvider().notifyChanged(); + expect(UserProvider.authorized, isTrue); + + when(client.get<Map<String, dynamic>>("/api/v4/user")).thenAnswer((_) => Future(() => Response.of<Map<String, dynamic>>(userInfo))); + when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=pending")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); + when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=done")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); + when(client.get<List<dynamic>>("/api/v4/groups?top_level_only=true&page=1&per_page=50")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); + when(client.get<List<dynamic>>("/api/v4/users/9527/starred_projects?page=1&per_page=50")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); + onMock?.call(); + HttpClient.setInstance(client); +} diff --git a/test/integration_tests/helper/mobile_test_helper/root.dart b/test/integration_tests/helper/mobile_test_helper/root.dart index dbe3b2541257bb277637f4f54c7ea6cd512c7cca..83fead680dae0906d6fd9b7f14e986d85d70aa0d 100644 --- a/test/integration_tests/helper/mobile_test_helper/root.dart +++ b/test/integration_tests/helper/mobile_test_helper/root.dart @@ -1,19 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:jihu_gitlab_app/core/global_keys.dart'; -import 'package:jihu_gitlab_app/core/net/http_client.dart'; -import 'package:jihu_gitlab_app/core/net/response.dart'; - -import 'package:jihu_gitlab_app/core/user_provider.dart'; import 'package:jihu_gitlab_app/core/widgets/unauthorized_view.dart'; import 'package:jihu_gitlab_app/main.dart'; import 'package:jihu_gitlab_app/modules/modules.dart'; import 'package:jihu_gitlab_app/modules/root/mobile_root_page.dart'; -import 'package:mockito/mockito.dart'; -import '../../../mocker/tester.dart'; import '../../../test_binding_setter.dart'; -import '../../../test_data/user.dart'; import '../core.dart'; Future<void> launchMobileAppForTest(WidgetTester tester) async { @@ -37,19 +30,7 @@ Future<void> navigateToTargetMobilePage(WidgetTester tester, int targetPageIndex Future<void> mobileLoginWithSaaSForPage(WidgetTester tester, index, pageType, {VoidCallback? onMock}) async { await navigateToTargetMobilePage(tester, JiHuPageType.todo.index, ToDosPage); expect(find.byType(UnauthorizedView), findsWidgets); - expect(UserProvider.authorized, isFalse); - UserProvider().resetToken(Tester.token()); - UserProvider().reset(Tester.user()); - UserProvider().notifyChanged(); - expect(UserProvider.authorized, isTrue); - - when(client.get<Map<String, dynamic>>("/api/v4/user")).thenAnswer((_) => Future(() => Response.of<Map<String, dynamic>>(userInfo))); - when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=pending")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=done")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - when(client.get<List<dynamic>>("/api/v4/groups?top_level_only=true&page=1&per_page=50")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - when(client.get<List<dynamic>>("/api/v4/users/9527/starred_projects?page=1&per_page=50")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - onMock?.call(); - HttpClient.setInstance(client); + saasLogin(onMock: onMock); await navigateToTargetMobilePage(tester, JiHuPageType.faq.index, FaqPage); await navigateToTargetMobilePage(tester, JiHuPageType.todo.index, ToDosPage); diff --git a/test/integration_tests/helper/tablet_test_helper/root.dart b/test/integration_tests/helper/tablet_test_helper/root.dart index 8f6e506e499a562d3222e69b7ce899f54937264f..896976b0d68662047d4b67d5ed28e6c3b3470155 100644 --- a/test/integration_tests/helper/tablet_test_helper/root.dart +++ b/test/integration_tests/helper/tablet_test_helper/root.dart @@ -1,20 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:jihu_gitlab_app/core/global_keys.dart'; -import 'package:jihu_gitlab_app/core/net/http_client.dart'; -import 'package:jihu_gitlab_app/core/net/response.dart'; - -import 'package:jihu_gitlab_app/core/user_provider.dart'; import 'package:jihu_gitlab_app/core/widgets/unauthorized_view.dart'; import 'package:jihu_gitlab_app/l10n/jihu_localizations.dart'; import 'package:jihu_gitlab_app/main.dart'; import 'package:jihu_gitlab_app/modules/modules.dart'; import 'package:jihu_gitlab_app/modules/root/desktop_root_page.dart'; -import 'package:mockito/mockito.dart'; -import '../../../mocker/tester.dart'; import '../../../test_binding_setter.dart'; -import '../../../test_data/user.dart'; import '../core.dart'; Future<void> launchTabletAppForTest(WidgetTester tester) async { @@ -38,19 +31,7 @@ Future<void> tabletLoginWithSaaSForPage(WidgetTester tester, index, pageType, {V await navigateToTargetTabletPage(tester, JiHuPageType.todo.index, ToDosPage); expect(find.byType(UnauthorizedView), findsWidgets); - expect(UserProvider.authorized, isFalse); - UserProvider().resetToken(Tester.token()); - UserProvider().reset(Tester.user()); - UserProvider().notifyChanged(); - expect(UserProvider.authorized, isTrue); - - when(client.get<Map<String, dynamic>>("/api/v4/user")).thenAnswer((_) => Future(() => Response.of<Map<String, dynamic>>(userInfo))); - when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=pending")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=done")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - when(client.get<List<dynamic>>("/api/v4/groups?top_level_only=true&page=1&per_page=50")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - when(client.get<List<dynamic>>("/api/v4/users/9527/starred_projects?page=1&per_page=50")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>([]))); - onMock?.call(); - HttpClient.setInstance(client); + saasLogin(onMock: onMock); await navigateToTargetTabletPage(tester, JiHuPageType.faq.index, FaqPage); await navigateToTargetTabletPage(tester, JiHuPageType.todo.index, ToDosPage); diff --git a/test/integration_tests/modules/merge_request/reopen_merge_request_details_test.dart b/test/integration_tests/modules/merge_request/reopen_merge_request_details_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..e6d166367614a2e57cfa40c4f3b1434a12abeef7 --- /dev/null +++ b/test/integration_tests/modules/merge_request/reopen_merge_request_details_test.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jihu_gitlab_app/core/dependency_injector.dart'; +import 'package:jihu_gitlab_app/core/global_keys.dart'; +import 'package:jihu_gitlab_app/core/local_storage.dart'; +import 'package:jihu_gitlab_app/core/net/http_client.dart'; +import 'package:jihu_gitlab_app/core/net/response.dart'; +import 'package:jihu_gitlab_app/core/user_provider.dart'; +import 'package:jihu_gitlab_app/core/widgets/photo_picker.dart'; +import 'package:jihu_gitlab_app/modules/modules.dart'; +import 'package:jihu_gitlab_app/modules/mr/merge_request_page.dart'; +import 'package:jihu_gitlab_app/modules/mr/models/graphql_request_body.dart'; +import 'package:mockito/mockito.dart'; + +import '../../../test_data/issue.dart'; +import '../../../test_data/merge_request.dart'; +import '../../../test_data/to_dos.dart'; +import '../../helper/core.dart'; +import '../../helper/mobile_test_helper/root.dart'; +import '../../helper/tablet_test_helper/root.dart'; + +void main() { + setUp(() async { + WidgetsFlutterBinding.ensureInitialized(); + setupLocator(); + + when(client.post<dynamic>("/api/graphql", getIssuesEEGraphQLRequestBody(true, 'ultimate-plan/jihu-gitlab-app/faq', '', 20, null))) + .thenAnswer((_) => Future(() => Response.of(faqIssuesGraphQLResponseData))); + HttpClient.setInstance(client); + }); + + testWidgets('Should launch with mobile and save webUrl of Merge Request after enter in Merge Request Page and clear when exit', (WidgetTester tester) async { + await launchMobileAppForTest(tester); + await mobileLoginWithSaaSForPage(tester, JiHuPageType.todo.index, ToDosPage, onMock: onToDoRequestMock); + await pageContentTest(tester); + }); + + testWidgets('Should app launch with tablet and save webUrl of Merge Request after enter in Merge Request Page and clear when exit', (WidgetTester tester) async { + await launchTabletAppForTest(tester); + await tabletLoginWithSaaSForPage(tester, JiHuPageType.todo.index, ToDosPage, onMock: onToDoRequestMock); + await pageContentTest(tester); + }); + + tearDown(() async { + UserProvider().fullReset(); + + locator.unregister<SubgroupListModel>(); + locator.unregister<ProjectsGroupsModel>(); + locator.unregister<ProjectsStarredModel>(); + locator.unregister<IssueDetailsModel>(); + locator.unregister<PhotoPicker>(); + }); +} + +Future<void> pageContentTest(WidgetTester tester) async { + expect(find.text('pending test name'), findsOneWidget); + expect(find.text('merge request item'), findsOneWidget); + await tester.tap(find.text('merge request item')); + await tester.pumpAndSettle(); + expect(find.byType(ToDosPage), findsNothing); + expect(find.byType(MergeRequestPage), findsOneWidget); + + var webUrl = await LocalStorage.get(GlobalKeys.currentMergeRequestWebUrlKey, ''); + expect(webUrl, isNotEmpty); + expect(webUrl, 'https://jihulab.com/ultimate-plan/jihu-gitlab-app/jihu-gitlab-app/-/merge_requests/84'); + + await tester.tap(find.byType(BackButton)); + await tester.pumpAndSettle(); + webUrl = await LocalStorage.get(GlobalKeys.currentMergeRequestWebUrlKey, ''); + expect(find.byType(ToDosPage), findsOneWidget); + expect(find.byType(MergeRequestPage), findsNothing); + expect(webUrl, isEmpty); +} + +void onToDoRequestMock() { + when(client.get<List<dynamic>>("/api/v4/todos?page=1&per_page=20&state=pending")).thenAnswer((_) => Future(() => Response.of<List<dynamic>>(todoPendingResponseData))); + when(client.get<Map<String, dynamic>>("/api/v4/projects/59893/merge_requests/84/approvals")).thenAnswer((_) => Future(() => Response.of<Map<String, dynamic>>(approvalResponse))); + when(client.get('/api/v4/projects/59893')).thenAnswer((_) => Future(() => Response.of<Map>({"path_with_namespace": "ultimate-plan/jihu-gitlab-app/jihu-gitlab-app"}))); + when(client.post<Map<String, dynamic>>('/api/graphql', getMergeRequestDetailsGraphQLRequestBody('ultimate-plan/jihu-gitlab-app/jihu-gitlab-app', 84))) + .thenAnswer((_) => Future(() => Response.of<Map<String, dynamic>>(mrGraphQLDraftResponse))); + when(client.post('/api/graphql', { + "variables": {"fullPath": 'ultimate-plan/jihu-gitlab-app/jihu-gitlab-app'}, + "query": """ + query (\$fullPath: ID!) { + project(fullPath: \$fullPath) { + id + name + fullPath + } + } + """, + })).thenAnswer((_) => Future(() => Response.of<Map<String, dynamic>>({ + "data": { + "project": {"id": "gid://gitlab/Project/59893", "name": "merge request item", "fullPath": "ultimate-plan/jihu-gitlab-app/jihu-gitlab-app"} + } + }))); +} diff --git a/test/test_data/merge_request.dart b/test/test_data/merge_request.dart index 7ba8aa5cd1ea34189ea9240e21880326f81819d0..65e98b276e5bfaf1f31766362f711e7066cd96ef 100644 --- a/test/test_data/merge_request.dart +++ b/test/test_data/merge_request.dart @@ -382,6 +382,7 @@ Map<String, dynamic> mrGraphQLDraftResponse = { "state": "opened", "title": "Draft: Resolve \"Feature: å¼€å‘人员查看 Merge blocked\"", "description": "Closes #47", + "webUrl": "https://jihulab.com/ultimate-plan/jihu-gitlab-app/jihu-gitlab-app/-/merge_requests/84", "draft": true, "createdAt": "2023-02-14T15:34:08+08:00", "rebaseInProgress": false,