Skip to content

Commit

Permalink
Refactor: Start using feature-first architecture
Browse files Browse the repository at this point in the history
It's a goal to refactor the source to have a clean feature-first
architecture. Start by putting the new sharing feature into a separate
folder features/share/
Increase test coverage for ShareButton to 100%
  • Loading branch information
holybiber committed Jul 24, 2024
1 parent 4f45eac commit 0448122
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,17 @@ import 'dart:async';

import 'package:app4training/data/languages.dart';
import 'package:app4training/design/theme.dart';
import 'package:app4training/features/share/share_service.dart';
import 'package:app4training/l10n/l10n.dart';
import 'package:app4training/routes/view_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:open_filex/open_filex.dart';
import 'package:share_plus/share_plus.dart';

// TODO: Use build_runner with flutter_gen instead
const String openPdfImage = 'assets/file-document-outline.png';
const String sharePdfImage = 'assets/file-document-arrow-right-outline.png';
const String shareLinkImage = 'assets/link.png';

/// A class around all sharing functionality to enable testing
/// (Packages: share_plus, url_launcher, open_filex)
///
/// Other options for testing (dependency injection etc.) are hard to use here
/// because of the usage of context.findAncestorWidgetOfExactType
/// so this is probably the best way to do it
class ShareService {
/// Wraps Share.share() (package share_plus)
Future<void> share(String text) {
return Share.share(text);
}

/// Wraps Share.shareXFiles() (package share_plus)
///
/// Not using the same argument List<XFile> because that would make it
/// harder to verify that the function gets called with correct arguments
/// with mocktail
Future<ShareResult> shareFile(String path) {
return Share.shareXFiles([XFile(path)]);
}

/// Wraps launchUrl (package url_launcher)
Future<bool> launchUrl(Uri url) {
return launchUrl(url);
}

/// Wraps OpenFilex.open (package open_filex)
Future<OpenResult> open(String? filePath) {
return OpenFilex.open(filePath);
}
}

/// A provider for all sharing functionality to enable testing
final shareProvider = Provider<ShareService>((ref) {
return ShareService();
});

/// Share button in the top right corner of the main view.
/// Opens a dropdown with several sharing options.
/// Implemented with MenuAnchor+MenuItemButton
Expand Down
40 changes: 40 additions & 0 deletions lib/features/share/share_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:open_filex/open_filex.dart';
import 'package:share_plus/share_plus.dart';

/// A class around all sharing functionality to enable testing
/// (Packages: share_plus, url_launcher, open_filex)
///
/// Other options for testing (dependency injection etc.) are hard to use here
/// because of the usage of context.findAncestorWidgetOfExactType
/// so this is probably the best way to do it
class ShareService {
/// Wraps Share.share() (package share_plus)
Future<void> share(String text) {
return Share.share(text);
}

/// Wraps Share.shareXFiles() (package share_plus)
///
/// Not using the same argument List<XFile> because that would make it
/// harder to verify that the function gets called with correct arguments
/// with mocktail
Future<ShareResult> shareFile(String path) {
return Share.shareXFiles([XFile(path)]);
}

/// Wraps launchUrl (package url_launcher)
Future<bool> launchUrl(Uri url) {
return launchUrl(url);
}

/// Wraps OpenFilex.open (package open_filex)
Future<OpenResult> open(String? filePath) {
return OpenFilex.open(filePath);
}
}

/// A provider for all sharing functionality to enable testing
final shareProvider = Provider<ShareService>((ref) {
return ShareService();
});
2 changes: 1 addition & 1 deletion lib/routes/view_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:app4training/data/globals.dart';
import 'package:app4training/l10n/l10n.dart';
import 'package:app4training/widgets/error_message.dart';
import 'package:app4training/widgets/html_view.dart';
import 'package:app4training/widgets/share_button.dart';
import 'package:app4training/features/share/share_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:app4training/data/languages.dart';
Expand Down
10 changes: 8 additions & 2 deletions test/share_button_test.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import 'package:app4training/data/app_language.dart';
import 'package:app4training/data/languages.dart';
import 'package:app4training/features/share/share_service.dart';
import 'package:app4training/features/share/share_button.dart';
import 'package:app4training/l10n/l10n.dart';
import 'package:app4training/routes/view_page.dart';
import 'package:app4training/widgets/share_button.dart';
import 'package:flutter/material.dart' hide Page;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -42,7 +43,7 @@ Finder findAssetImageIcon(String assetName, [Color? color]) {
class MockShareService extends Mock implements ShareService {}

void main() {
testWidgets('Smoke test: open the share menu (English locale)',
testWidgets('Smoke test: open and close the share menu (English locale)',
(WidgetTester tester) async {
await tester.pumpWidget(ProviderScope(overrides: [
appLanguageProvider.overrideWith(() => TestAppLanguage('en')),
Expand All @@ -64,6 +65,11 @@ void main() {
expect(findAssetImageIcon(openPdfImage), findsOneWidget);
expect(findAssetImageIcon(sharePdfImage), findsOneWidget);
expect(findAssetImageIcon(shareLinkImage), findsOneWidget);

await tester.tap(find.byType(ShareButton));
await tester.pump();
expect(find.byIcon(Icons.share), findsOneWidget);
expect(find.text('Open PDF'), findsNothing);
});

testWidgets('Test when PDFs are not available', (WidgetTester tester) async {
Expand Down
2 changes: 1 addition & 1 deletion test/view_page_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import 'package:app4training/data/app_language.dart';
import 'package:app4training/data/exceptions.dart';
import 'package:app4training/data/globals.dart';
import 'package:app4training/data/languages.dart';
import 'package:app4training/features/share/share_button.dart';
import 'package:app4training/l10n/l10n.dart';
import 'package:app4training/routes/view_page.dart';
import 'package:app4training/widgets/language_selection_button.dart';
import 'package:app4training/widgets/share_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down

0 comments on commit 0448122

Please sign in to comment.