diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 0bd19e71..ec741343 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -126,50 +126,8 @@ Another option is to use, e.g., . _version (login without redirect) – can adopt once different login types are_ _supported._ -Scripts were created to click through the app using tests and record the -screen. - -A simulator with the app in its initial state (or not installed) needs to be -running. - -Optional: set the time and status bar items as described in -[screenshots for publishing](#screenshots-for-publishing). - -The `generate_screendocs/run.sh` script will create screencasts -and screenshots. - -Run the script with `bash generate_screendocs/run.sh`. - -### Screencasts - -The script uses uses Xcode to record the screencast and -[`ffmpeg`](https://ffmpeg.org/) to cut the `full.mov` to relevant subsets -(needs to be installed). - -To generate GIFs used in the Tutorial, -[ImageMagick](https://imagemagick.org/index.php) is used -(which also needs to be installed). - -### Screenshots - -The script automatically updates the screenshots in `../docs/screenshots` -(used in [πŸ“‘ App screens](../docs/App-screens.md), -[πŸ“‘ User instructions](../docs/User-instructions.html), and the -[README](./README.md)). - -If the error `The following MissingPluginException was thrown running a test: -MissingPluginException(No implementation found for method captureScreenshot on -channel plugins.flutter.io/integration_test)` occurs, the registration in the -file -`ios/.symlinks/plugins/integration_test/ios/Classes/IntegrationTestPlugin.m` -needs to be adapted (see -[issue](https://github.com/flutter/flutter/issues/91668)): - -```m -+ (void)registerWithRegistrar:(NSObject *)registrar { - [[IntegrationTestPlugin instance] setupChannels:registrar.messenger]; -} -``` +_Check `e60efb4f2fc3ba2efa7735ffb06ec5fdb64d7af6` for a rudimentary script_ +_version, removed afterwards due to too many merge conflicts._ ## Adapting test data diff --git a/app/generate_screendocs/run.sh b/app/generate_screendocs/run.sh deleted file mode 100644 index fb1ce0eb..00000000 --- a/app/generate_screendocs/run.sh +++ /dev/null @@ -1,229 +0,0 @@ -#!/bin/bash - -output_directory="../docs/screencasts/" -assets_directory="assets/images/tutorial/" -cuts_log_path="${output_directory}cuts.log" -flutter_log_path="${output_directory}flutter.log" -full_video_path="${output_directory}full.mov" - -timestamp_prefix="TIMESTAMP: " - -ffmpeg_log_level="warning" - -# The following flags are for testing single steps and should be set to true -redo_recording=true -redo_cutting=true -redo_gif_creation=true -redo_gif_copying=true - -log_timestamp() { - echo "$(date +%s000) $1" >> "$cuts_log_path" -} - -if $redo_recording; then - - read -p "Enter username: " username - read -p "Enter password: " password - - # Doing the recording was inspired by - # https://betterprogramming.pub/how-to-record-flutter-integration-tests-with-github-actions-1ca670eff94a - - # Start recording - - rm -f "$cuts_log_path" - rm -f "$flutter_log_path" - xcrun simctl io booted recordVideo "$full_video_path" -f & - log_timestamp "recording_start" - sleep 5 - export RECORDING_PID=${!} - echo "Recording process up with pid: ${RECORDING_PID}" - - # Run sequence in app - - echo "Running app" - flutter drive \ - --driver=generate_screendocs/test_driver.dart \ - --target=generate_screendocs/screendocs_sequence.dart \ - --dart-define=TIMESTAMP_PREFIX="$timestamp_prefix" \ - --dart-define=TEST_USER="$username" \ - --dart-define=TEST_PASSWORD="$password" | tee "$flutter_log_path" - - # Write cut timestamps from test.log to cut.log - - echo "Finishing recording" - - logged_timestamp_prefix="flutter: $timestamp_prefix" - extracted_timestamps=$(\ - awk -v s="$logged_timestamp_prefix" 'index($0, s) == 1' "$flutter_log_path" | \ - sed -e "s/^$logged_timestamp_prefix//"\ - ) - echo "$extracted_timestamps" >> "$cuts_log_path" - - # End recording - - log_timestamp "recording_end" - sleep 5 - kill -SIGINT $RECORDING_PID - sleep 10 - echo "" - -fi - -if $redo_cutting; then - - # Cut smaller screencasts - - echo "Cutting smaller screencasts at keyframes closest before logged times" - - timestamps=() - descriptions=() - while read cut_info; do - if [ -z "$cut_info" ]; then - continue - fi - cut_info_array=($cut_info) - timestamp=${cut_info_array[0]} - description=${cut_info_array[1]} - timestamps+=("$timestamp") - descriptions+=("$description") - done < "$cuts_log_path" - - get_frame_starts() { - local video_path=$1 - echo $( - ffprobe -loglevel "$ffmpeg_log_level" \ - -select_streams v \ - -show_entries frame=pts_time \ - -of csv=print_section=0 \ - "$video_path" \ - | awk -F',' '{print $1}' - ) - } - - get_frame_durations() { - local video_path=$1 - echo $( - ffprobe -loglevel "$ffmpeg_log_level" \ - -select_streams v \ - -show_entries frame=duration_time \ - -of csv=print_section=0 \ - "$video_path" \ - | awk -F',' '{print $1}' - ) - } - - frame_starts=($(get_frame_starts $full_video_path)) - frame_durations=($(get_frame_durations $full_video_path)) - - get_difference() { - local first_float=$1 - local second_float=$2 - local difference=$(echo "$first_float - $second_float" | bc) - echo ${difference#-} - } - - get_closest_previous_frame_start() { - local target=$1 - local best_fit_index=0 - local smallest_difference=$(get_difference $target ${frame_starts[0]}) - for ((j=1; j<${#frame_starts[@]}; j++)); do - local current_value=${frame_starts[j]} - local current_difference=$(get_difference $target $current_value) - if (( $(echo "$current_value > $target" | bc) )); then - break - fi - if (( $(echo "$current_difference < $smallest_difference" | bc) )); then - smallest_difference=$current_difference - best_fit_index=$j - fi - done - echo "${frame_starts[best_fit_index]}" - } - - get_frame_end() { - local index=$1 - local frame_start=${frame_starts[index]} - local frame_duration=${frame_durations[index]} - echo $(echo "$frame_start + $frame_duration" | bc) - } - - get_closest_previous_frame_end() { - local target=$1 - local best_fit_index=0 - local smallest_difference=$(get_difference $target $(get_frame_end 0)) - for ((j=1; j<${#frame_starts[@]}; j++)); do - local current_value=$(get_frame_end $j) - local current_difference=$(get_difference $target $current_value) - if (( $(echo "$current_value > $target" | bc) )); then - break - fi - if (( $(echo "$current_difference < $smallest_difference" | bc) )); then - smallest_difference=$current_difference - best_fit_index=$j - fi - done - echo "$(get_frame_end $best_fit_index)" - } - - get_seconds_since_start() { - echo $(printf %.3f "$(($1-${timestamps[0]}))e-3") - } - - cut_video() { - local description=$1 - local start_seconds=$(get_closest_previous_frame_start $2) - local end_seconds=$(get_closest_previous_frame_end $3) - - echo "Cutting $description $start_seconds – $end_seconds (originally $2 – $3)" - - local video_path="${output_directory}${description}.mp4" - - ffmpeg -y -loglevel "$ffmpeg_log_level" -an \ - -i "$full_video_path" \ - -ss "$start_seconds" \ - -to "$end_seconds" \ - -pix_fmt yuv420p \ - "$video_path" - } - - cut_video "full_clean" \ - $(get_seconds_since_start ${timestamps[1]}) \ - $(get_seconds_since_start ${timestamps[${#timestamps[@]}-2]}) - - # Skip first and last entries (recording_start and recording_end) - for ((i=2; i<${#timestamps[@]}-1; i+=2)); do - description=${descriptions[i]} - start_seconds=$(get_seconds_since_start ${timestamps[i-1]}) - end_seconds=$(get_seconds_since_start ${timestamps[i]}) - cut_video $description $start_seconds $end_seconds - done - -fi - -if $redo_gif_creation; then - - for video_path in `find $output_directory -name "*.mp4" -type f`; do - video_name=$(basename $video_path) - if [[ "$video_name" == full*.mp4 ]]; then continue; fi - gif_name="${video_name%.mp4}.gif" - gif_path="${output_directory}$gif_name" - echo "Creating $gif_path" - # From https://superuser.com/a/556031 - ffmpeg -y -loglevel "$ffmpeg_log_level" \ - -i "$video_path" \ - -vf "fps=10,scale=320:-1:flags=lanczos" -c:v pam -f image2pipe - | \ - magick -delay 10 - -loop 0 -layers optimize "$gif_path" - done - -fi - -if $redo_gif_copying; then - - for gif_path in `find $output_directory -name "*_loopable.gif" -type f`; do - gif_name="$(basename $gif_path)" - assets_path="${assets_directory}$gif_name" - echo "Copying $gif_name to $assets_directory" - cp $gif_path $assets_path - done - -fi diff --git a/app/generate_screendocs/screendocs_sequence.dart b/app/generate_screendocs/screendocs_sequence.dart deleted file mode 100644 index 2552b144..00000000 --- a/app/generate_screendocs/screendocs_sequence.dart +++ /dev/null @@ -1,524 +0,0 @@ -// Clicks though most parts of the app and creates screenshots (based on -// https://dev.to/mjablecnik/take-screenshot-during-flutter-integration-tests-435k) -// and screencasts; also outputs timestamp logs for cutting smaller screencasts - -import 'dart:io'; - -import 'package:app/app.dart'; -import 'package:app/common/module.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:provider/provider.dart'; - -Future takeScreenshot( - WidgetTester tester, - IntegrationTestWidgetsFlutterBinding binding, - String fileName -) async { - if (Platform.isAndroid) { - await binding.convertFlutterSurfaceToImage(); - await tester.pumpAndSettle(); - } - await binding.takeScreenshot(fileName); -} - -Future logTimeStamp( - WidgetTester tester, - String prefix, - String description, -) async { - await tester.pumpAndSettle(); - final timestamp = DateTime.now().millisecondsSinceEpoch; - // ignore: avoid_print - print('$prefix$timestamp $description'); - await _waitAndSettle(tester, 1); -} - -Future beginPart( - WidgetTester tester, - String timestampPrefix, - String description, -) async{ - await logTimeStamp(tester, timestampPrefix, description); -} - -Future endPart( - WidgetTester tester, - String timestampPrefix, - String description, -) async { - await logTimeStamp(tester, timestampPrefix, description); -} - -void main() { - group('click through the app and create screencasts', () { - final binding = IntegrationTestWidgetsFlutterBinding(); - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('take screencast', (tester) async { - const username = String.fromEnvironment('TEST_USER'); - const password = String.fromEnvironment('TEST_PASSWORD'); - const timestampPrefix = String.fromEnvironment('TIMESTAMP_PREFIX'); - - await _loadApp(tester); - - const acceptLoginDescription = '01_accept_and_login'; - const onboardingDescription = '02_onboarding'; - const drugSelectionDescription = '03_drug_selection'; - const tutorialDescription = '04_tutorial'; - const bottomNavigationDescription = '05_bottom_navigation_loopable'; - const drugSearchFilterDescription = '06_drug_search_and_filter_loopable'; - const ibuprofenDescription = '07_ibuprofen_loopable'; - const reportCyp2c9Description = '08_report_and_cyp2c9_loopable'; - const faqMoreDescription = '09_faq_and_more_loopable'; - const deleteDataDescription = '10_delete_data'; - - await beginPart( - tester, - timestampPrefix, - acceptLoginDescription, - ); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'accept-terms'); - await _tapButton(tester, 'Continue'); - await _waitAndSettle(tester, 2); - await _enterUsername(tester, username); - await _waitAndSettle(tester, 1); - await _enterPassword(tester, password); - await tester.pumpAndSettle(); - await takeScreenshot(tester, binding, 'login'); - await _tapFullWidthButton(tester); - await _waitAndSettle(tester, 1); - await endPart(tester, timestampPrefix, acceptLoginDescription); - - await beginPart( - tester, - timestampPrefix, - onboardingDescription, - ); - const onboardingWaitingTime = 1; - await _waitAndSettle(tester, onboardingWaitingTime); - await takeScreenshot(tester, binding, 'onboarding-1'); - await _tapButton(tester, 'Next'); - await _waitAndSettle(tester, onboardingWaitingTime); - await takeScreenshot(tester, binding, 'onboarding-2'); - await _tapButton(tester, 'Next'); - await _waitAndSettle(tester, onboardingWaitingTime); - await takeScreenshot(tester, binding, 'onboarding-3'); - await _tapButton(tester, 'Next'); - await _waitAndSettle(tester, onboardingWaitingTime); - await takeScreenshot(tester, binding, 'onboarding-4'); - await _tapButton(tester, 'Next'); - await _waitAndSettle(tester, onboardingWaitingTime); - await takeScreenshot(tester, binding, 'onboarding-5'); - await _tapButton(tester, 'Get started'); - await _waitAndSettle(tester, 1); - await endPart(tester, timestampPrefix, onboardingDescription); - - await beginPart(tester, timestampPrefix, drugSelectionDescription); - await _waitAndSettle(tester, 2); - await takeScreenshot(tester, binding, 'drug-selection-intro'); - await _tapButton(tester, 'Continue'); - await _waitAndSettle(tester, 1); - await _searchDrug(tester, 'Ibu'); - await _waitAndSettle(tester, 1); - await _interactWithDrugInSelection( - tester, - 'Ibuprofen', - scroll: false, - tap: true, - ); - await _waitAndSettle(tester, 1); - await _clearDrugSearch(tester); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'drug-selection'); - await _tapFullWidthButton(tester); - await _waitAndSettle(tester, 1); - await endPart(tester, timestampPrefix, drugSelectionDescription); - - await beginPart(tester, timestampPrefix, tutorialDescription); - const tutorialWaitingTime = 1; - await Future.delayed(Duration(seconds: tutorialWaitingTime)); - await takeScreenshot(tester, binding, 'tutorial-1'); - await _tapButton(tester, 'Continue'); - await _waitAndSettle(tester, tutorialWaitingTime); - await takeScreenshot(tester, binding, 'tutorial-2'); - await _tapButton(tester, 'Continue'); - await _waitAndSettle(tester, tutorialWaitingTime); - await takeScreenshot(tester, binding, 'tutorial-3'); - await _tapButton(tester, 'Continue'); - await _waitAndSettle(tester, tutorialWaitingTime); - await takeScreenshot(tester, binding, 'tutorial-4'); - await _tapButton(tester, 'Continue'); - await _waitAndSettle(tester, tutorialWaitingTime); - await takeScreenshot(tester, binding, 'tutorial-5'); - await _tapButton(tester, 'Finish'); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'setup-complete'); - await _selectDialogAction(tester); - await _waitAndSettle(tester, 1); - await endPart(tester, timestampPrefix, tutorialDescription); - - await beginPart(tester, timestampPrefix, bottomNavigationDescription); - await _waitAndSettle(tester, 1); - await _useBottomNavigation(tester, 'Genes'); - await _waitAndSettle(tester, 1); - await _useBottomNavigation(tester, 'FAQ'); - await _waitAndSettle(tester, 1); - await _useBottomNavigation(tester, 'More'); - await _waitAndSettle(tester, 1); - await _useBottomNavigation(tester, 'Medications'); - await _waitAndSettle(tester, 0); - await endPart(tester, timestampPrefix, bottomNavigationDescription); - - const toggledWarningLevelIndex = 3; // missing data - await beginPart(tester, timestampPrefix, drugSearchFilterDescription); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'drug-search'); - await _openDrugFilters(tester); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'drug-search-filter'); - await _toggleNthWarningLevel(tester, toggledWarningLevelIndex); - await _waitAndSettle(tester, 2); - await _toggleNthWarningLevel(tester, toggledWarningLevelIndex); - await _waitAndSettle(tester, 1); - await _closeDrugFilters(tester); - await _waitAndSettle(tester, 0); - await endPart(tester, timestampPrefix, drugSearchFilterDescription); - - await beginPart(tester, timestampPrefix, ibuprofenDescription); - await _waitAndSettle(tester, 1); - await _tapText(tester, 'Ibuprofen'); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'ibuprofen'); - await _changeDrugStatus(tester, 'Ibuprofen', 'inactive'); - await _waitAndSettle(tester, 2); - try { - await _changeDrugStatus(tester, 'Ibuprofen', 'active'); - await _waitAndSettle(tester, 2); - } catch (e) { - // ignore: avoid_print - print( - '🚨 Could not activate Ibuprofen again; video will not be' - 'loopable' - ); - } - await tester.pageBack(); - await _waitAndSettle(tester, 1); - await endPart(tester, timestampPrefix, ibuprofenDescription); - - await _waitAndSettle(tester, 1); - await _useBottomNavigation(tester, 'Genes'); - await _waitAndSettle(tester, 2); - - await beginPart(tester, timestampPrefix, reportCyp2c9Description); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'gene-report-current'); - await _toggleNonCurrentList(tester); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'gene-report-all'); - await _toggleNonCurrentList(tester, expectExpanded: true); - await _waitAndSettle(tester, 1); - await _tapGeneCard(tester, 'CYP2C9'); - await _waitAndSettle(tester, 2); - await takeScreenshot(tester, binding, 'cyp2c9'); - await _toggleNonCurrentList(tester); - await _waitAndSettle(tester, 2); - await takeScreenshot(tester, binding, 'cyp2c9-expanded'); - await _toggleNonCurrentList(tester, expectExpanded: true); - await _waitAndSettle(tester, 0); - await tester.pageBack(); - await _waitAndSettle(tester, 1); - await endPart(tester, timestampPrefix, reportCyp2c9Description); - - await _waitAndSettle(tester, 1); - await _useBottomNavigation(tester, 'FAQ'); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'faq'); - - await beginPart(tester, timestampPrefix, faqMoreDescription); - await _waitAndSettle(tester, 1); - await _tapFirstFaqItem(tester); - await _waitAndSettle(tester, 1); - await takeScreenshot(tester, binding, 'faq-first-item'); - await _tapFirstFaqItem(tester); - await _waitAndSettle(tester, 1); - await _useBottomNavigation(tester, 'More'); - await _waitAndSettle(tester, 3); - await takeScreenshot(tester, binding, 'more'); - await _tapText(tester, 'Contact us', selectLast: true); - await _waitAndSettle(tester, 2); - await _selectDialogAction(tester, selectFirst: true); - await _waitAndSettle(tester, 0); - await _useBottomNavigation(tester, 'FAQ'); - await _waitAndSettle(tester, 1); - await endPart(tester, timestampPrefix, faqMoreDescription); - - - await beginPart(tester, timestampPrefix, deleteDataDescription); - await _useBottomNavigation(tester, 'More'); - await tester.pumpAndSettle(); - await _tapText(tester, 'Delete app data'); - await tester.pumpAndSettle(); - await takeScreenshot(tester, binding, 'delete-app-data'); - await _selectDialogAction(tester, selectFirst: true); - await tester.pumpAndSettle(); - await endPart(tester, timestampPrefix, deleteDataDescription); - - await _cleanupApp(); - }); - }); -} - -Future _loadApp(WidgetTester tester) async { - // Part before runApp in lib/main.dart - await initServices(); - // Load the app - await tester.pumpWidget( - MultiProvider( - providers: [ - ChangeNotifierProvider(create: (context) => ActiveDrugs()), - ChangeNotifierProvider(create: (context) => InactivityTimer()), - ], - child: PharMeApp(), - ), - ); - await tester.pumpAndSettle(); -} - -Future _cleanupApp() async { - // Part after runApp in lib/main.dart - await cleanupServices(); -} - -Future _waitAndSettle(WidgetTester tester, int seconds) async { - await tester.pumpAndSettle(); - await Future.delayed(Duration(seconds: seconds)); - await tester.pumpAndSettle(); -} - -Future _enterText(WidgetTester tester, Finder finder, String text) async { - // Please note: apparently, this does not actually open the keyboard in the - // simulator (which is fine I guess) - await tester.showKeyboard(finder); - await tester.pump(); - var buildingSearchTerm = ''; - for (final character in text.characters) { - // ignore: use_string_buffers - buildingSearchTerm = '$buildingSearchTerm$character'; - await tester.enterText(finder, buildingSearchTerm); - await tester.pump(); - } - await _finishTextInput(tester); -} - -Future _enterUsername(WidgetTester tester, String username) async { - await _enterText(tester, find.byType(TextField).first, username); -} - -Future _enterPassword(WidgetTester tester, String password) async { - await _enterText(tester, find.byType(TextField).last, password); -} - -Future _searchDrug(WidgetTester tester, String drug) async { - await _enterText(tester, find.byType(CupertinoSearchTextField).last, drug); -} - -Future _clearDrugSearch(WidgetTester tester) async { - await tester.tap(find.descendant( - of: find.byType(CupertinoSearchTextField).first, - matching: find.byType(Icon), - ).last); -} - -Future _finishTextInput(WidgetTester tester) async { - await tester.testTextInput.receiveAction(TextInputAction.done); -} - -Future _tapButton(WidgetTester tester, String label) async { - await tester.tap(find.bySemanticsLabel(label).first); -} - -Future _toggleNonCurrentList( - WidgetTester tester, - { bool expectExpanded = false } -) async { - final expectedIcon = expectExpanded - ? Icons.arrow_drop_up - : Icons.arrow_drop_down; - await tester.tap( - find.ancestor( - of: find.byIcon(expectedIcon, skipOffstage: false), - matching: find.byType(ResizedIconButton, skipOffstage: false), - ).last, - ); -} - -Future _tapFullWidthButton(WidgetTester tester) async { - await tester.tap(find.byType(FullWidthButton).first); -} - -Finder _findText(String text, {bool selectLast = false}) { - final exactMatch = find.text(text, skipOffstage: false); - if (exactMatch.hasFound) { - return selectLast - ? exactMatch.last - : exactMatch.first; - } - final fuzzyMatch = find.textContaining(text, skipOffstage: false); - return selectLast ? fuzzyMatch.last : fuzzyMatch.first; -} - -Future _tapText( - WidgetTester tester, - String text, - {bool selectLast = false} -) async { - await tester.tap(_findText(text, selectLast: selectLast)); -} - -Future _tapGeneCard( - WidgetTester tester, - String gene, - { String keyPostfix = 'current-medications' } -) async { - final geneCard = find.byKey( - Key('gene-card-$gene-$keyPostfix'), - skipOffstage: false, - ); - await tester.tap(geneCard); -} - -// This is a bit hacky, as only some list items can be found currently -// (not sure why); therefore, the script needs to select one of those. -// If the interaction could not be executed, a list of available items is -// printed. -Future _interactWithDrugListItem( - WidgetTester tester, - { - required String itemKey, - required String listKey, - required Type itemType, - required bool scroll, - required bool tap, - required bool raiseException, - } -) async { - final listFinder = find.byKey(Key(listKey), skipOffstage: false); - final itemFinder = find.descendant( - of: listFinder, - matching: find.byKey(Key(itemKey), skipOffstage: false), - skipOffstage: false, - ); - final item = itemFinder.first; - try { - if (scroll) { - final list = listFinder.first; - await tester.dragUntilVisible(item, list, Offset(0, -0.1)); - } - if (tap) { - await tester.tap(item); - } - } catch (e) { - var errorMessage = '🚨 Could not drag drug list'; - if (!raiseException) errorMessage += '\n Error: ${e.toString()}\n'; - // ignore: avoid_print - print(errorMessage); - var contextDetails = 'Context details:\n' - ' With item finder: ${itemFinder.toString()}'; - if (scroll) { - contextDetails += '\n With list finder: ${listFinder.toString()}\n'; - } - // ignore: avoid_print - print(contextDetails); - // ignore: avoid_print - print('Available drug items:'); - final availableItems = find.descendant( - of: listFinder, - matching: find.byType(itemType, skipOffstage: false), - skipOffstage: false, - ); - availableItems.evaluate(); - // ignore: avoid_function_literals_in_foreach_calls - availableItems.found.forEach( - // ignore: avoid_print - (element) => print(' ${element.toString()}') - ); - if (raiseException) rethrow; - } -} - -Future _interactWithDrugInSelection( - WidgetTester tester, - String drug, - { - required bool scroll, - required bool tap, - bool raiseException = true, - } -) async { - await _interactWithDrugListItem( - tester, - itemKey: 'other-drug-selection-tile-${drug.toLowerCase()}', - listKey: 'drug-selection', - itemType: SwitchListTile, - scroll: scroll, - tap: tap, - raiseException: raiseException, - ); -} - -Future _useBottomNavigation( - WidgetTester tester, - String destination, -) async { - await tester.tap(find.descendant( - of: find.byType(BottomNavigationBar), - matching: find.text(destination), - ).first); -} - -Future _changeDrugStatus( - WidgetTester tester, - String drug, - String activity, -) async { - await tester.tap( - find.byType(Switch).first, - ); -} - -Future _tapFirstFaqItem(WidgetTester tester) async { - await tester.tap( - find.descendant( - of: find.byType(ExpansionTile).first, - matching: find.byType(Icon), - ), - ); -} - -Future _toggleNthWarningLevel(WidgetTester tester, int n) async { - // We are basically testing :shrug: - // ignore: invalid_use_of_visible_for_testing_member - await tester.tap(find.byType(WarningLevelFilterChip).at(n)); -} - -Future _selectDialogAction( - WidgetTester tester, - {bool selectFirst = false} -) async { - final actions = find.byType(DialogAction, skipOffstage: false); - final action = selectFirst ? actions.first : actions.last; - await tester.tap(action); -} - -Future _openDrugFilters(WidgetTester tester) async { - await tester.tap(find.ancestor( - of: find.byIcon(Icons.filter_list), - matching: find.byType(IconButton), - )); -} - -Future _closeDrugFilters(WidgetTester tester) async { - await tester.tapAt(Offset(0, 0)); -} diff --git a/app/generate_screendocs/test_driver.dart b/app/generate_screendocs/test_driver.dart deleted file mode 100644 index d7bd66f7..00000000 --- a/app/generate_screendocs/test_driver.dart +++ /dev/null @@ -1,23 +0,0 @@ -// Create screenshots from integration tests; from -// https://dev.to/mjablecnik/take-screenshot-during-flutter-integration-tests-435k - -import 'dart:io'; -import 'package:integration_test/integration_test_driver_extended.dart'; - -Future main() async { - try { - await integrationDriver( - onScreenshot: (screenshotName, screenshotBytes, [_]) async { - final image = - await File( - '../docs/screenshots/$screenshotName.png' - ).create(recursive: true); - image.writeAsBytesSync(screenshotBytes); - return true; - }, - ); - } catch (e) { - // ignore: avoid_print - print('Error occured: $e'); - } -} \ No newline at end of file diff --git a/docs/App-screens.md b/docs/App-screens.md index 711b17f7..e2d56e28 100644 --- a/docs/App-screens.md +++ b/docs/App-screens.md @@ -8,6 +8,10 @@ Actions are usually tapping (πŸ‘†) or scrolling down (⏬). If no screen number is given, the action refers to the screen in the previous table row. +_Please note: as screenshot generation is currently not possible, the screens_ +_you see here are the study version screens. Most screens are the same, but_ +_in the study version some additional content is implemented._ + | # | Action | Screen | Description | | - | ------ | ------ | ----------- | | 1 | App opened the first time | accept-terms | Notice that by continuing users agree to terms (see screen 23) |