Skip to content

Commit

Permalink
feat: browserstack (#140)
Browse files Browse the repository at this point in the history
Co-authored-by: u221711 <[email protected]>
  • Loading branch information
Grodien and Grodien authored Jul 29, 2024
1 parent 87c1bd7 commit 8396452
Show file tree
Hide file tree
Showing 29 changed files with 742 additions and 257 deletions.
38 changes: 31 additions & 7 deletions .github/workflows/flutter_browserstack_android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
push:
paths:
- 'das_client/**'
branches: [ "feature/das-client-durchstich" ]
branches: [ "main" ]
pull_request:
paths:
- 'das_client/**'
Expand All @@ -34,16 +34,40 @@ jobs:
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.2'
- run: flutter pub get
- run: flutter pub run build_runner build --delete-conflicting-outputs
- name: Prepare Flutter Build
env:
ANDROID_KEYSTORE_STRING: ${{ secrets.ANDROID_KEYSTORE }}
ANDROID_KEYSTORE: ${{ github.workspace }}/das_client/android/das.keystore
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
echo $ANDROID_KEYSTORE_STRING | base64 -d > $ANDROID_KEYSTORE
echo "ANDROID_KEYSTORE=$ANDROID_KEYSTORE" >> "$GITHUB_ENV"
echo "ANDROID_KEYSTORE_PASSWORD=$ANDROID_KEYSTORE_PASSWORD" >> "$GITHUB_ENV"
echo "ANDROID_KEY_ALIAS=$ANDROID_KEY_ALIAS" >> "$GITHUB_ENV"
echo "ANDROID_KEY_PASSWORD=$ANDROID_KEY_PASSWORD" >> "$GITHUB_ENV"
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
- shell: bash
env:
MQTT_USERNAME: ${{ secrets.MQTT_USERNAME }}
MQTT_PASSWORD: ${{ secrets.MQTT_PASSWORD }}
run: |
flutter build apk --flavor dev -t integration_test/app_test.dart --no-tree-shake-icons --debug --dart-define=MQTT_USERNAME=$MQTT_USERNAME --dart-define=MQTT_PASSWORD=$MQTT_PASSWORD
- name: browserstack
uses: Grodien/[email protected]
- name: Build Test Package
run: |
cd android
./gradlew app:assembleAndroidTest
cd ..
- name: Upload and Run on Browserstack
uses: Grodien/[email protected]
with:
browserstackUsername: "asdf"
browserstackAccessKey: "asdf"
browserstackUsername: ${{ secrets.BROWSERSTACK_USERNAME }}
browserstackAccessKey: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
project: das_client
customId: das_client_android
buildTag: das_client_android
appFilePath: ${{ github.workspace }}/das_client/build/app/outputs/flutter-apk/app-dev-debug.apk
testFilePath: ${{ github.workspace }}/das_client/build/app/outputs/apk/androidTest/dev/debug/app-dev-debug-androidTest.apk
devices: Samsung Galaxy Tab S9-13.0,Samsung Galaxy Tab S8-12.0
8 changes: 4 additions & 4 deletions .github/workflows/flutter_browserstack_ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
push:
paths:
- 'das_client/**'
branches: [ "feature/das-client-durchstich" ]
branches: [ "main" ]
pull_request:
paths:
- 'das_client/**'
Expand All @@ -41,15 +41,15 @@ jobs:
MQTT_USERNAME: ${{ secrets.MQTT_USERNAME }}
MQTT_PASSWORD: ${{ secrets.MQTT_PASSWORD }}
run: |
flutter build ios --release --no-codesign -t integration_test/app_test.dart --no-tree-shake-icons --debug --dart-define=MQTT_USERNAME=$MQTT_USERNAME --dart-define=MQTT_PASSWORD=$MQTT_PASSWORD
flutter build ios --release --no-codesign -t integration_test/app_test.dart --no-tree-shake-icons --dart-define=MQTT_USERNAME=$MQTT_USERNAME --dart-define=MQTT_PASSWORD=$MQTT_PASSWORD
pushd ios
xcodebuild -workspace Runner.xcworkspace -scheme Runner -config Flutter/Release.xcconfig -derivedDataPath ../build/ios_integration -sdk iphoneos build-for-testing CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
popd
pushd build/ios_integration/Build/Products
zip -r "app-integrationtest-release.zip" "Release-iphoneos" "Runner_iphoneos17.5-arm64.xctestrun"
zip -r app-integrationtest-release.zip Release-iphoneos Runner_iphoneos*-arm64.xctestrun
popd
- name: Upload and Run on Browserstack
uses: Grodien/browserstack-flutter-action@v1.2
uses: Grodien/browserstack-flutter-action@v1.4
with:
browserstackUsername: ${{ secrets.BROWSERSTACK_USERNAME }}
browserstackAccessKey: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
Expand Down
20 changes: 17 additions & 3 deletions .github/workflows/flutter_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,23 @@ jobs:
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.2'
- run: flutter pub get
- run: flutter pub run build_runner build --delete-conflicting-outputs
- name: Prepare Flutter Build
env:
ANDROID_KEYSTORE_STRING: ${{ secrets.ANDROID_KEYSTORE }}
ANDROID_KEYSTORE: ${{ github.workspace }}/das_client/android/das.keystore
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
echo $ANDROID_KEYSTORE_STRING | base64 -d > $ANDROID_KEYSTORE
echo "ANDROID_KEYSTORE=$ANDROID_KEYSTORE" >> "$GITHUB_ENV"
echo "ANDROID_KEYSTORE_PASSWORD=$ANDROID_KEYSTORE_PASSWORD" >> "$GITHUB_ENV"
echo "ANDROID_KEY_ALIAS=$ANDROID_KEY_ALIAS" >> "$GITHUB_ENV"
echo "ANDROID_KEY_PASSWORD=$ANDROID_KEY_PASSWORD" >> "$GITHUB_ENV"
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
- run: flutter test
- run: flutter build apk --flavor dev -t lib/main_dev.dart
- run: flutter build appbundle --flavor dev -t lib/main_dev.dart
- run: flutter build appbundle --flavor inte -t lib/main_inte.dart
- run: flutter build appbundle --flavor prod -t lib/main_prod.dart
- run: flutter build ios --flavor dev -t lib/main_dev.dart --release --no-codesign
33 changes: 29 additions & 4 deletions das_client/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ android {
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

flavorDimensions = ['env']
Expand All @@ -61,13 +62,33 @@ android {
'appAuthRedirectScheme': 'ch.sbb.das'
]
}
inte {
dimension 'env'
manifestPlaceholders += [
'appAuthRedirectScheme': 'ch.sbb.das'
]
}
prod {
dimension 'env'
manifestPlaceholders += [
'appAuthRedirectScheme': 'ch.sbb.das'
]
}
}

signingConfigs {
release {
storeFile = file(System.getenv("ANDROID_KEYSTORE"))
storePassword System.getenv("ANDROID_KEYSTORE_PASSWORD")
keyAlias System.getenv("ANDROID_KEY_ALIAS")
keyPassword System.getenv("ANDROID_KEY_PASSWORD")
}
}

buildTypes {
debug
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
signingConfig signingConfigs.release
}
}
}
Expand All @@ -76,4 +97,8 @@ flutter {
source '../..'
}

dependencies {}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ch.sbb.das;
import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.integration_test.FlutterTestRunner;
import org.junit.Rule;
import org.junit.runner.RunWith;
@RunWith(FlutterTestRunner.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class, true, false);
}
50 changes: 50 additions & 0 deletions das_client/integration_test/app_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:das_client/flavor.dart';
import 'package:das_client/main.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'di.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
Fimber.plantTree(DebugTree());

group('home screen test', () {
testWidgets('load fahrbild company=1088, train=9232', (tester) async {

// Load app widget.
await prepareAndStartApp(tester);

await tester.pump(const Duration(seconds: 1));

// Verify we have trainnumber with 9232.
expect(find.text('9232'), findsOneWidget);

// Verify we have company with 1088.
expect(find.text('1088'), findsOneWidget);

// check that the primary button is enabled
var primaryButton = find.byWidgetPredicate((widget) => widget is SBBPrimaryButton).first;
expect(tester.widget<SBBPrimaryButton>(primaryButton).onPressed, isNotNull);

// press load Fahrordnung button
await tester.tap(primaryButton);

// wait for fahrbild to load
await tester.pumpAndSettle(const Duration(seconds: 1));

// check if station is present
expect(find.text('MEER-GRENS'), findsOneWidget);

});
});
}

Future<void> prepareAndStartApp(WidgetTester tester) async {
await IntegrationTestDI.init(Flavor.dev);
runDasApp();
await tester.pumpAndSettle(const Duration(milliseconds: 500));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:das_client/auth/authenticator.dart';
import 'package:flutter/foundation.dart';
import 'package:sbb_oidc/sbb_oidc.dart';

class IntegrationtestAuthenticator implements Authenticator {
@override
Future<void> endSession() async {}

@override
Future<bool> get isAuthenticated async => true;

@override
Future<OidcToken> login({String? tokenId}) async {
return _token();
}

@override
Future<void> logout() async {}

@override
Future<OidcToken> token({String? tokenId}) async {
return _token();
}

@override
Future<String> userId({String? tokenId}) async {
return "[email protected]";
}

OidcToken _token() {
return OidcToken(
tokenType: '',
accessToken: const AccessToken(''),
idToken: JsonWebToken(
header: const <String, dynamic>{}, payload: const <String, dynamic>{}, signature: Uint8List.fromList([])));
}
}
34 changes: 34 additions & 0 deletions das_client/integration_test/auth/mqtt_client_user_connector.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'package:das_client/service/mqtt/mqtt_client_connector.dart';
import 'package:fimber/fimber.dart';
import 'package:mqtt_client/mqtt_client.dart';

class MqttClientUserConnector implements MqttClientConnector {
static const mqttUsername = "MQTT_USERNAME";
static const mqttPassword = "MQTT_PASSWORD";

@override
Future<bool> connect(MqttClient client, String company, String train) async {
Fimber.i("Connecting to mqtt using static login and password");

if (!const bool.hasEnvironment(mqttUsername) || !const bool.hasEnvironment(mqttPassword)) {
Fimber.e("$mqttUsername or $mqttPassword not defined");
return false;
}

try {
var mqttClientConnectionStatus =
await client.connect(const String.fromEnvironment(mqttUsername), const String.fromEnvironment(mqttPassword));
Fimber.i("mqttClientConnectionStatus=$mqttClientConnectionStatus");

if (mqttClientConnectionStatus?.state == MqttConnectionState.connected) {
Fimber.i("Successfully connected to MQTT broker");
return true;
}
} catch (e) {
Fimber.e("Exception during connect", ex: e);
}

Fimber.w("Failed to connect to MQTT broker");
return false;
}
}
33 changes: 33 additions & 0 deletions das_client/integration_test/di.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:das_client/auth/authenticator.dart';
import 'package:das_client/di.dart';
import 'package:das_client/flavor.dart';
import 'package:das_client/service/mqtt/mqtt_client_connector.dart';
import 'package:fimber/fimber.dart';
import 'package:get_it/get_it.dart';

import 'auth/integrationtest_authenticator.dart';
import 'auth/mqtt_client_user_connector.dart';

class IntegrationTestDI {
const IntegrationTestDI._();

static Future<void> init(Flavor flavor) {
Fimber.i('Initialize integration test dependency injection');
GetIt.I.registerFlavor(flavor);
GetIt.I.registerTokenSpecProvider();
GetIt.I.registerOidcClient();
_registerIntegrationTestAuthenticator();
GetIt.I.registerBackendService();
_registerMqttClientConnector();
GetIt.I.registerMqttService();
return GetIt.I.allReady();
}

static void _registerIntegrationTestAuthenticator() {
GetIt.I.registerSingletonAsync<Authenticator>(() async => IntegrationtestAuthenticator());
}

static void _registerMqttClientConnector() {
GetIt.I.registerSingletonAsync<MqttClientConnector>(() async => MqttClientUserConnector());
}
}
4 changes: 4 additions & 0 deletions das_client/ios/IntegrationTests/IntegrationTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@import XCTest;
@import integration_test;

INTEGRATION_TEST_IOS_RUNNER(IntegrationTests)
2 changes: 1 addition & 1 deletion das_client/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ target 'Runner' do
use_modular_headers!

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
target 'IntegrationTests' do
inherit! :search_paths
end
end
Expand Down
10 changes: 8 additions & 2 deletions das_client/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ PODS:
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- integration_test (0.0.1):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
Expand All @@ -22,6 +24,7 @@ DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_appauth (from `.symlinks/plugins/flutter_appauth/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)

SPEC REPOS:
Expand All @@ -37,6 +40,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_appauth/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"

Expand All @@ -46,8 +51,9 @@ SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_appauth: 1ce438877bc111c5d8f42da47729909290624886
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46

PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
PODFILE CHECKSUM: d9dad56c0cd0b4fd8b4fe3034a53fd42a0b990f6

COCOAPODS: 1.13.0
COCOAPODS: 1.15.2
Loading

0 comments on commit 8396452

Please sign in to comment.