diff --git a/app/integration_test/drugs_test.dart b/app/integration_test/drugs_test.dart index 37f7bf7d..5d5b1ae9 100644 --- a/app/integration_test/drugs_test.dart +++ b/app/integration_test/drugs_test.dart @@ -12,40 +12,135 @@ import 'package:provider/provider.dart'; class MockDrugsCubit extends MockCubit implements DrugCubit {} +Future _expectDrugContent( + WidgetTester tester, + MockDrugsCubit mockDrugsCubit, { + required Drug drug, + bool isLoading = false, + bool expectNoGuidelines = false, + bool expectDrugToBeActive = false, +}) async { + when(() => mockDrugsCubit.state) + .thenReturn(isLoading ? DrugState.loading() : DrugState.loaded()); + await tester.pumpWidget( + ChangeNotifierProvider( + create: (context) => ActiveDrugs(), + child: MaterialApp( + home: Scaffold( + body: Builder( + builder: (context) { + return DrugPage(drug, cubit: mockDrugsCubit); + }, + ), + ), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [Locale('en', '')], + ), + ), + ); + // Title + final drugName = isInhibitor(drug.name) ? '${drug.name}*' : drug.name; + expect( + find.text(drugName.capitalize()), + findsOneWidget, + ); + // Activity selection + final activitySelection = tester.firstWidget( + find.byType(Switch) + ) as Switch; + expect(activitySelection.onChanged, isLoading ? isNull : isNotNull); + expect(activitySelection.value, expectDrugToBeActive ? isTrue : isFalse); + // Drug details + expect( + find.textContaining( + drug.annotations.drugclass + ), + findsOneWidget, + ); + expect(find.text(drug.annotations.indication), findsOneWidget); + // Guideline details + final card = tester.firstWidget( + find.byKey( + ValueKey('annotationCard'), + ), + ) as RoundedCard; + expect( + card.color, + expectNoGuidelines + ? WarningLevel.green.color + : drug.guidelines.first.annotations.warningLevel.color, + ); + expect(find.byType(Disclaimer), findsOneWidget); + String tooltipText; + List guidelineTexts; + final context = tester.element(find.byType(Scaffold).first); + if (expectNoGuidelines) { + tooltipText = context.l10n.drugs_page_tooltip_guideline_missing; + guidelineTexts = [ + context.l10n.drugs_page_guidelines_empty(drug.name), + context.l10n.drugs_page_no_guidelines_text, + ]; + } else { + tooltipText = context.l10n.drugs_page_tooltip_guideline_present( + drug.guidelines.first.externalData.first.source, + ); + guidelineTexts = [ + drug.guidelines.first.annotations.implication, + drug.guidelines.first.annotations.recommendation, + ...drug.guidelineGenotypes + ]; + } + for (final guidelineText in guidelineTexts) { + expect( + find.textContaining(guidelineText), + findsOneWidget, + ); + } + expect( + find.byTooltip(tooltipText), + findsOneWidget, + ); +} + void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); final mockDrugsCubit = MockDrugsCubit(); binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps; - final testDrug = Drug( - id: '1', + final drugWithProperGuideline = Drug( + id: '6407768b92a4868065b6c466', version: 1, - name: 'Ibuprofen', - rxNorm: 'rxnorm', + name: 'ibuprofen', + rxNorm: 'RxNorm:5640', annotations: DrugAnnotations( - drugclass: 'NSAID', - indication: 'indication', - brandNames: ['brand name', 'another brand name']), + drugclass: 'Non-steroidal anti-inflammatory drug (NSAID)', + indication: 'Ibuprofen is used to treat pain, fever, and inflammation.', + brandNames: ['Advil', 'Motrin']), guidelines: [ Guideline( - id: '1', + id: '64552859a1b68082babc8c31', version: 1, lookupkey: { - 'CYP2C9': ['2'] + 'CYP2C9': ['2.0'] }, externalData: [ GuidelineExtData( source: 'CPIC', - guidelineName: 'cpic name', - guidelineUrl: 'cpic url', - implications: {'CYP2C9': 'Normal Metabolizer'}, - recommendation: 'default dose', - comments: 'comments') + guidelineName: 'CYP2C9 and NSAIDs', + guidelineUrl: 'https://cpicpgx.org/guidelines/cpic-guideline-for-nsaids-based-on-cyp2c9-genotype/', + implications: {'CYP2C9': 'Normal metabolism'}, + recommendation: 'Initiate therapy with recommended starting dose. In accordance with the prescribing information, use the lowest effective dosage for shortest duration consistent with individual patient treatment goals.', + comments: 'n/a') ], annotations: GuidelineAnnotations( - recommendation: 'default dose', - implication: 'nothing', + implication: 'You break down ibuprofen as expected.', + recommendation: 'You can use ibuprofen at standard dose. Consult your pharmacist or doctor for more information.', warningLevel: WarningLevel.green)) ]); UserData.instance.labData = [ @@ -53,7 +148,7 @@ void main() { gene: 'CYP2C9', phenotype: 'Normal Metabolizer', variant: '*1/*1', - allelesTested: '1', + allelesTested: '"*2.*3.*5.*11"', ), ]; UserData.instance.genotypeResults = { @@ -62,154 +157,60 @@ void main() { phenotype: UserData.instance.labData![0].phenotype, variant: UserData.instance.labData![0].variant, allelesTested: UserData.instance.labData![0].variant, - lookupkey: '2', + lookupkey: '2.0', ), }; - final testDrugWithoutGuidelines = Drug( - id: '2', - version: 1, - name: 'Codeine', - rxNorm: 'rxnorm', + final drugWithoutGuidelines = Drug( + id: '64c187431006f51bc6e24959', + version: 2, + name: 'mirabegron', + rxNorm: 'RxNorm:1300786', annotations: DrugAnnotations( - drugclass: 'Pain killer', - indication: 'indication', - brandNames: ['brand name', 'another brand name']), + drugclass: 'Urology drug', + indication: 'Mirabegron is used to treat overactive bladder.', + brandNames: ['Myrbetriq']), guidelines: [], ); - UserData.instance.activeDrugNames = ['Ibuprofen']; + setUp(() => UserData.instance.activeDrugNames = []); group('integration test for the drugs page', () { - testWidgets('test loading', (tester) async { - when(() => mockDrugsCubit.state).thenReturn(DrugState.loading()); - await tester.pumpWidget( - ChangeNotifierProvider( - create: (context) => ActiveDrugs(), - child: MaterialApp( - home: DrugPage(testDrug, cubit: mockDrugsCubit), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], - ), - ), + testWidgets( + 'test that activity selection is disabled when loading', + (tester) async { + await _expectDrugContent( + tester, + mockDrugsCubit, + drug: drugWithProperGuideline, + isLoading: true, + ); + }, + ); + + testWidgets('test drug content with proper guideline', (tester) async { + await _expectDrugContent( + tester, + mockDrugsCubit, + drug: drugWithProperGuideline, ); - final activitySelection = tester.firstWidget( - find.byType(Switch) - ) as Switch; - expect(activitySelection.onChanged, isNull); }); - testWidgets('test loaded page', (tester) async { - when(() => mockDrugsCubit.state) - .thenReturn(DrugState.loaded()); - await tester.pumpWidget( - ChangeNotifierProvider( - create: (context) => ActiveDrugs(), - child: MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return DrugPage(testDrug, cubit: mockDrugsCubit); - }, - ), - ), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], - ), - ), - ); - expect(find.text(testDrug.name.capitalize()), findsOneWidget); - expect( - find.textContaining( - testDrug.annotations.drugclass - ), - findsOneWidget, - ); - expect(find.text(testDrug.annotations.indication), findsOneWidget); - expect( - find.textContaining( - testDrug.guidelines.first.annotations.recommendation, - ), - findsOneWidget, - ); - expect( - find.textContaining(testDrug.guidelines.first.annotations.implication), - findsOneWidget, - ); - expect( - find.textContaining( - testDrug.guidelines.first.lookupkey.keys.first, - ), - findsOneWidget, - ); - expect(find.byType(Disclaimer), findsOneWidget); - final card = tester.firstWidget( - find.byKey( - ValueKey('annotationCard'), - ), - ) as RoundedCard; - expect( - card.color, - testDrug.guidelines.first.annotations.warningLevel.color, - ); - final activitySelection = tester.firstWidget( - find.byType(Switch) - ) as Switch; - expect(activitySelection.onChanged, isNotNull); - final context = tester.element(find.byType(Tooltip).first); - expect( - find.byTooltip(context.l10n.drugs_page_tooltip_guidelines_present( - testDrug.guidelines.first.externalData.first.source - )), - findsOneWidget, + testWidgets('test active drug content', (tester) async { + UserData.instance.activeDrugNames = ['ibuprofen']; + await _expectDrugContent( + tester, + mockDrugsCubit, + drug: drugWithProperGuideline, + expectDrugToBeActive: true, ); }); - testWidgets('test loaded page without guidelines', (tester) async { - when(() => mockDrugsCubit.state).thenReturn( - DrugState.loaded()); - await tester.pumpWidget( - ChangeNotifierProvider( - create: (context) => ActiveDrugs(), - child: MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return DrugPage( - testDrugWithoutGuidelines, - cubit: mockDrugsCubit, - ); - }, - ), - ), - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [Locale('en', '')], - ), - ), - ); - expect(find.text(testDrugWithoutGuidelines.name), findsOneWidget); - final context = tester.element(find.byType(Scaffold).first); - expect( - find.text(context.l10n.drugs_page_no_guidelines_text), - findsOneWidget, - ); - expect( - find.byTooltip(context.l10n.drugs_page_tooltip_guidelines_missing), - findsOneWidget, + testWidgets('test drug content without guidelines', (tester) async { + await _expectDrugContent( + tester, + mockDrugsCubit, + drug: drugWithoutGuidelines, + expectNoGuidelines: true, ); }); }); diff --git a/pharme.code-workspace b/pharme.code-workspace index 1a20dd3e..9354f15e 100644 --- a/pharme.code-workspace +++ b/pharme.code-workspace @@ -68,6 +68,7 @@ "Metabolizer", "mirabegron", "mocktail", + "Myrbetriq", "noto", "NSAID", "omeprazole",