From 9f3176cdb190cae73538b7f40ebb48c660df8c99 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Tue, 5 Mar 2024 19:14:16 +0100 Subject: [PATCH] feat(#687): add script to generate screencasts --- app/CONTRIBUTING.md | 11 ++- app/generate_screenshots/app_test.dart | 14 ++- .../generate_screencast.sh | 96 +++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 app/generate_screenshots/generate_screencast.sh diff --git a/app/CONTRIBUTING.md b/app/CONTRIBUTING.md index 336b2475..92a65ec2 100644 --- a/app/CONTRIBUTING.md +++ b/app/CONTRIBUTING.md @@ -100,12 +100,19 @@ flutter pub run flutter_launcher_icons:main This will generate icons for both iOS as well as Android. -## Updating screenshots +## Updating screencast and screenshots 🙅 _Not working yet due to login redirect, but keeping script for Sinai_ _version (login without redirect)._ -To update the screenshots in `../docs/screenshots` +The `generate_screenshots/generate_screencast.sh` script will create screenshots +and screencasts. It uses Xcode to record the screencast and +[`ffmpeg`](https://ffmpeg.org/) +to cut the `full.mov` to relevant subsets. + +Run the script with `bash generate_screenshots/generate_screencast.sh`. + +To only update the screenshots in `../docs/screenshots` (used in [📑 App screens](../docs/App-screens.md), [📑 User instructions](../docs/User-instructions.html), and the [README](./README.md)), run the following command after adding username and diff --git a/app/generate_screenshots/app_test.dart b/app/generate_screenshots/app_test.dart index 82ee8fb6..93f37ccf 100644 --- a/app/generate_screenshots/app_test.dart +++ b/app/generate_screenshots/app_test.dart @@ -5,11 +5,11 @@ import 'dart:io'; import 'package:app/app.dart'; import 'package:app/common/module.dart'; +import 'package:dartx/dartx_io.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, @@ -22,6 +22,12 @@ Future takeScreenshot( await binding.takeScreenshot(fileName); } +void logTimeStamp(String description) { + final timestamp = DateTime.now().millisecondsSinceEpoch; + // ignore: avoid_print + print('TIMESTAMP: $timestamp $description'); +} + void main() { group('click through the app and create screenshots', () { final binding = IntegrationTestWidgetsFlutterBinding(); @@ -45,12 +51,16 @@ void main() { ); await tester.pumpAndSettle(); + logTimeStamp('test_start'); + // Click though the app and create screenshots // login - await Future.delayed(Duration(seconds: 1)); // wait for logo + await Future.delayed(Duration(seconds: 5)); // wait for logo & screencast await takeScreenshot(tester, binding, 'login'); + logTimeStamp('login'); + // login-redirect (not working; only taking screenshot of loading screen) // could try to use cubit function to directly sign in which will only // open the webview and close it again diff --git a/app/generate_screenshots/generate_screencast.sh b/app/generate_screenshots/generate_screencast.sh new file mode 100644 index 00000000..d8784947 --- /dev/null +++ b/app/generate_screenshots/generate_screencast.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +output_directory="../docs/screencasts/" +cuts_log_path="${output_directory}cuts.log" +test_log_path="${output_directory}test.log" +full_video_path="${output_directory}full.mov" + +read -p "Enter username: " username +read -p "Enter password: " password + +log_timestamp() { + echo "$(date +%s000) $1" >> "$cuts_log_path" +} + +# Doing the recording inspired by +# https://betterprogramming.pub/how-to-record-flutter-integration-tests-with-github-actions-1ca670eff94a + +# Start recording + + +rm "$cuts_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}" +echo "Running app" +flutter drive \ +--driver=generate_screenshots/test_driver.dart \ +--target=generate_screenshots/app_test.dart \ +--dart-define=TEST_USER="$username" \ +--dart-define=TEST_PASSWORD="$password" > "$test_log_path" + +# Write cut timestamps from test.log to cut.log + +echo "Finishing recording" +timestamp_prefix="flutter: TIMESTAMP: " +extracted_timestamps=$(\ +awk -v s="$timestamp_prefix" 'index($0, s) == 1' "$test_log_path" | \ +sed -e "s/^$timestamp_prefix//"\ +) +echo "$extracted_timestamps" >> "$cuts_log_path" +rm "$test_log_path" + +# End recording + +log_timestamp "recording_end" +sleep 5 +kill -SIGINT $RECORDING_PID +sleep 10 +echo "" + +# Cut smaller screencasts + +echo "Cutting smaller screencasts" +timestamps=() +descriptions=() +while read cut_info; do + cut_info_array=($cut_info) + timestamp=${cut_info_array[0]} + description=${cut_info_array[1]} + timestamps+=("$timestamp") + descriptions+=("$description") +done < "$cuts_log_path" + +get_seconds_since_start() { + echo $((($1-${timestamps[0]})/1000)) +} + +# Format seconds as hh:mm:ss, see https://stackoverflow.com/a/13425821 +format_seconds_as_time() { + (( + sec=$1%60, + $1/=60, + min=$1%60, + hrs=$1/60 + )) + echo $(printf "%02d:%02d:%02d" $hrs $min $sec) +} + +for ((i=2; i<${#timestamps[@]}-1; i++)) +do + description=${descriptions[i]} + echo "Cutting $description" + start_seconds=$(get_seconds_since_start ${timestamps[i-1]}) + end_seconds=$(get_seconds_since_start ${timestamps[i]}) + start_time=$(format_seconds_as_time start_seconds) + end_time=$(format_seconds_as_time end_seconds) + ## Use ffmpeg to cut, see https://stackoverflow.com/a/42827058 + video_path="${output_directory}${description}.mov" + ffmpeg -y -loglevel error \ + -ss "$start_time" \ + -to "$end_time" \ + -i "$full_video_path" \ + -c copy "$video_path" +done