mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-12-26 08:02:34 +01:00
Merge branch 'braid/integration-tests' into 'main'
chore: add integration tests See merge request famedly/fluffychat!1062
This commit is contained in:
commit
20e26b3747
@ -1,7 +1,9 @@
|
|||||||
variables:
|
variables:
|
||||||
FLUTTER_VERSION: 3.3.9
|
FLUTTER_VERSION: 3.3.9
|
||||||
|
|
||||||
image: cirrusci/flutter:${FLUTTER_VERSION}
|
image:
|
||||||
|
name: cirrusci/flutter:${FLUTTER_VERSION}
|
||||||
|
pull_policy: if-not-present
|
||||||
|
|
||||||
.shared_windows_runners:
|
.shared_windows_runners:
|
||||||
tags:
|
tags:
|
||||||
@ -16,7 +18,7 @@ stages:
|
|||||||
|
|
||||||
code_analyze:
|
code_analyze:
|
||||||
stage: test
|
stage: test
|
||||||
script: [./scripts/code_analyze.sh]
|
script: [ ./scripts/code_analyze.sh ]
|
||||||
artifacts:
|
artifacts:
|
||||||
reports:
|
reports:
|
||||||
codequality: code-quality-report.json
|
codequality: code-quality-report.json
|
||||||
@ -26,13 +28,13 @@ code_analyze:
|
|||||||
|
|
||||||
widget_test:
|
widget_test:
|
||||||
stage: test
|
stage: test
|
||||||
script: [flutter test]
|
script: [ flutter test ]
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- famedly
|
- famedly
|
||||||
|
|
||||||
# the basic integration test configuration testing FLOSS builds on Synapse
|
# the basic integration test configuration testing FLOSS builds on Synapse
|
||||||
.integration_test:
|
integration_test:
|
||||||
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/integration/stable:${FLUTTER_VERSION}
|
image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/integration/stable:${FLUTTER_VERSION}
|
||||||
stage: test
|
stage: test
|
||||||
services:
|
services:
|
||||||
@ -49,15 +51,13 @@ widget_test:
|
|||||||
FF_NETWORK_PER_BUILD: "true"
|
FF_NETWORK_PER_BUILD: "true"
|
||||||
# Tell docker CLI how to talk to Docker daemon.
|
# Tell docker CLI how to talk to Docker daemon.
|
||||||
DOCKER_HOST: tcp://docker:2375/
|
DOCKER_HOST: tcp://docker:2375/
|
||||||
# Use the overlayfs driver for improved performance.
|
# Use the btrfs driver for improved performance.
|
||||||
DOCKER_DRIVER: overlay2
|
DOCKER_DRIVER: btrfs
|
||||||
# Disable TLS since we're running inside local network.
|
# Disable TLS since we're running inside local network.
|
||||||
DOCKER_TLS_CERTDIR: ""
|
DOCKER_TLS_CERTDIR: ""
|
||||||
HOMESERVER: "docker"
|
HOMESERVER: docker
|
||||||
before_script:
|
before_script:
|
||||||
# start AVD and keep running in background
|
- scripts/integration-prepare-host.sh
|
||||||
- scripts/integration-start-avd.sh &
|
|
||||||
- scripts/integration-prepare-alpine.sh
|
|
||||||
# create test user environment variables
|
# create test user environment variables
|
||||||
- source scripts/integration-create-environment-variables.sh
|
- source scripts/integration-create-environment-variables.sh
|
||||||
# create Synapse instance
|
# create Synapse instance
|
||||||
@ -65,31 +65,49 @@ widget_test:
|
|||||||
# properly set the homeserver IP and create test users
|
# properly set the homeserver IP and create test users
|
||||||
- scripts/integration-prepare-homeserver.sh
|
- scripts/integration-prepare-homeserver.sh
|
||||||
script:
|
script:
|
||||||
|
# start AVD and keep running in background
|
||||||
|
- scripts/integration-start-avd.sh &
|
||||||
- flutter pub get
|
- flutter pub get
|
||||||
- flutter test integration_test
|
- scrcpy --no-display --record video.mkv &
|
||||||
timeout: 20m
|
- flutter test integration_test --dart-define=HOMESERVER=$HOMESERVER --dart-define=USER1_NAME=$USER1_NAME --dart-define=USER2_NAME=$USER2_NAME --dart-define=USER1_PW=$USER1_PW --dart-define=USER2_PW=$USER2_PW || ( sleep 10 && exit 1 )
|
||||||
|
after_script:
|
||||||
|
- ffmpeg -i video.mkv -vf scale=iw/2:-2 -crf 40 -b:v 2000k -preset fast video.mp4 || true
|
||||||
|
timeout: 30m
|
||||||
|
retry: 2
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- video.mp4
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- famedly
|
- famedly
|
||||||
|
|
||||||
|
|
||||||
# integration tests for Linux builds
|
# integration tests for Linux builds
|
||||||
|
### disabled because of Linux headless issues
|
||||||
.integration_test_linux:
|
.integration_test_linux:
|
||||||
extends: .integration_test
|
image: cirrusci/flutter:${FLUTTER_VERSION}
|
||||||
|
extends: integration_test
|
||||||
script:
|
script:
|
||||||
- apk add cmake ninja gtk+3.0-dev clang pkgconf xz-dev libsecret-dev jsoncpp-dev
|
- apt-get update
|
||||||
|
- apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libsecret-1-dev libjsoncpp-dev
|
||||||
- flutter pub get
|
- flutter pub get
|
||||||
- flutter test integration_test -d linux
|
- flutter test integration_test -d linux --dart-define=HOMESERVER=$HOMESERVER --dart-define=USER1_NAME=$USER1_NAME --dart-define=USER2_NAME=$USER2_NAME --dart-define=USER1_PW=$USER1_PW --dart-define=USER2_PW=$USER2_PW || ( sleep 10 && exit 1 )
|
||||||
|
after_script: [ ]
|
||||||
|
artifacts:
|
||||||
|
|
||||||
# extending the default tests to test the Google-flavored builds
|
# extending the default tests to test the Google-flavored builds
|
||||||
.integration_test_proprietary:
|
integration_test_proprietary:
|
||||||
extends: .integration_test
|
extends: integration_test
|
||||||
script:
|
script:
|
||||||
|
# start AVD and keep running in background
|
||||||
|
- scripts/integration-start-avd.sh &
|
||||||
- git apply ./scripts/enable-android-google-services.patch
|
- git apply ./scripts/enable-android-google-services.patch
|
||||||
- flutter pub get
|
- flutter pub get
|
||||||
- flutter test integration_test
|
- scrcpy --no-display --record video.mkv &
|
||||||
|
- flutter test integration_test --dart-define=HOMESERVER=$HOMESERVER --dart-define=USER1_NAME=$USER1_NAME --dart-define=USER2_NAME=$USER2_NAME --dart-define=USER1_PW=$USER1_PW --dart-define=USER2_PW=$USER2_PW || ( sleep 10 && exit 1 )
|
||||||
|
|
||||||
.release_mode_launches:
|
release_mode_launches:
|
||||||
parallel:
|
parallel:
|
||||||
matrix:
|
matrix:
|
||||||
- FLAVOR:
|
- FLAVOR:
|
||||||
@ -99,9 +117,9 @@ widget_test:
|
|||||||
stage: test
|
stage: test
|
||||||
before_script:
|
before_script:
|
||||||
- |
|
- |
|
||||||
if [ "$FLAVOR" == "proprietary" ]; then
|
if [ "$FLAVOR" == "proprietary" ]; then
|
||||||
git apply ./scripts/enable-android-google-services.patch
|
git apply ./scripts/enable-android-google-services.patch
|
||||||
fi
|
fi
|
||||||
script:
|
script:
|
||||||
# start AVD and keep running in background
|
# start AVD and keep running in background
|
||||||
- scripts/integration-start-avd.sh &
|
- scripts/integration-start-avd.sh &
|
||||||
@ -115,8 +133,8 @@ widget_test:
|
|||||||
build_web:
|
build_web:
|
||||||
stage: build
|
stage: build
|
||||||
before_script:
|
before_script:
|
||||||
[sudo apt update && sudo apt install curl -y, ./scripts/prepare-web.sh]
|
[ sudo apt update && sudo apt install curl -y, ./scripts/prepare-web.sh ]
|
||||||
script: [./scripts/build-web.sh]
|
script: [ ./scripts/build-web.sh ]
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/web/
|
- build/web/
|
||||||
@ -166,7 +184,7 @@ build_windows:
|
|||||||
|
|
||||||
build_android_debug:
|
build_android_debug:
|
||||||
stage: build
|
stage: build
|
||||||
script: [./scripts/build-android-debug.sh]
|
script: [ ./scripts/build-android-debug.sh ]
|
||||||
artifacts:
|
artifacts:
|
||||||
when: on_success
|
when: on_success
|
||||||
paths:
|
paths:
|
||||||
@ -183,7 +201,7 @@ build_android_apk:
|
|||||||
before_script:
|
before_script:
|
||||||
- git apply ./scripts/enable-android-google-services.patch
|
- git apply ./scripts/enable-android-google-services.patch
|
||||||
- ./scripts/prepare-android-release.sh
|
- ./scripts/prepare-android-release.sh
|
||||||
script: [./scripts/build-android-apk.sh]
|
script: [ ./scripts/build-android-apk.sh ]
|
||||||
artifacts:
|
artifacts:
|
||||||
when: on_success
|
when: on_success
|
||||||
paths:
|
paths:
|
||||||
@ -200,7 +218,7 @@ deploy_playstore_internal:
|
|||||||
before_script:
|
before_script:
|
||||||
- git apply ./scripts/enable-android-google-services.patch
|
- git apply ./scripts/enable-android-google-services.patch
|
||||||
- ./scripts/prepare-android-release.sh
|
- ./scripts/prepare-android-release.sh
|
||||||
script: [./scripts/release-playstore-beta.sh]
|
script: [ ./scripts/release-playstore-beta.sh ]
|
||||||
artifacts:
|
artifacts:
|
||||||
when: on_success
|
when: on_success
|
||||||
paths:
|
paths:
|
||||||
@ -267,7 +285,7 @@ build_linux_x86:
|
|||||||
[
|
[
|
||||||
sudo apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install keyboard-configuration -y && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 -y,
|
sudo apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install keyboard-configuration -y && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 -y,
|
||||||
]
|
]
|
||||||
script: [./scripts/build-linux.sh]
|
script: [ ./scripts/build-linux.sh ]
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
- famedly
|
- famedly
|
||||||
@ -278,9 +296,9 @@ build_linux_x86:
|
|||||||
|
|
||||||
build_linux_arm64:
|
build_linux_arm64:
|
||||||
stage: build
|
stage: build
|
||||||
before_script: [flutter upgrade]
|
before_script: [ flutter upgrade ]
|
||||||
script: [./scripts/build-linux.sh]
|
script: [ ./scripts/build-linux.sh ]
|
||||||
tags: [docker_arm64]
|
tags: [ docker_arm64 ]
|
||||||
only:
|
only:
|
||||||
- main
|
- main
|
||||||
- tags
|
- tags
|
||||||
@ -292,7 +310,7 @@ build_linux_arm64:
|
|||||||
|
|
||||||
update_dependencies:
|
update_dependencies:
|
||||||
stage: build
|
stage: build
|
||||||
needs: []
|
needs: [ ]
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
only:
|
only:
|
||||||
@ -374,7 +392,7 @@ deploy_playstore:
|
|||||||
before_script:
|
before_script:
|
||||||
- git apply ./scripts/enable-android-google-services.patch
|
- git apply ./scripts/enable-android-google-services.patch
|
||||||
- ./scripts/prepare-android-release.sh
|
- ./scripts/prepare-android-release.sh
|
||||||
script: [./scripts/release-playstore.sh]
|
script: [ ./scripts/release-playstore.sh ]
|
||||||
resource_group: playstore_release
|
resource_group: playstore_release
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
|
@ -1,49 +1,117 @@
|
|||||||
import 'dart:developer';
|
import 'package:fluffychat/config/setting_keys.dart';
|
||||||
|
import 'package:fluffychat/pages/chat/chat_view.dart';
|
||||||
|
import 'package:fluffychat/pages/chat_list/chat_list_body.dart';
|
||||||
|
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/main.dart' as app;
|
import 'package:fluffychat/main.dart' as app;
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import 'extensions/default_flows.dart';
|
||||||
|
import 'extensions/wait_for.dart';
|
||||||
import 'users.dart';
|
import 'users.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
group('Integration Test', () {
|
group(
|
||||||
testWidgets('Test if the app starts', (WidgetTester tester) async {
|
'Integration Test',
|
||||||
app.main();
|
() {
|
||||||
await tester.pumpAndSettle();
|
setUpAll(
|
||||||
|
() async {
|
||||||
|
// this random dialog popping up is super hard to cover in tests
|
||||||
|
SharedPreferences.setMockInitialValues({
|
||||||
|
SettingKeys.showNoGoogle: false,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
Hive.deleteFromDisk();
|
||||||
|
Hive.initFlutter();
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
testWidgets(
|
||||||
|
'Start app, login and logout',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.ensureAppStartedHomescreen();
|
||||||
|
await tester.ensureLoggedOut();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
testWidgets(
|
||||||
|
'Login again',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.ensureAppStartedHomescreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
expect(find.text('Connect'), findsOneWidget);
|
testWidgets(
|
||||||
|
'Start chat and send message',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
app.main();
|
||||||
|
await tester.ensureAppStartedHomescreen();
|
||||||
|
await tester.waitFor(find.byType(TextField));
|
||||||
|
await tester.enterText(find.byType(TextField), Users.user2.name);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
final input = find.byType(TextField);
|
await tester.scrollUntilVisible(
|
||||||
|
find.text('Chats'),
|
||||||
|
500,
|
||||||
|
scrollable: find.descendant(
|
||||||
|
of: find.byType(ChatListViewBody),
|
||||||
|
matching: find.byType(Scrollable),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Chats'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.waitFor(find.byType(SearchTitle));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(input, findsOneWidget);
|
await tester.scrollUntilVisible(
|
||||||
|
find.text(Users.user2.name).first,
|
||||||
|
500,
|
||||||
|
scrollable: find.descendant(
|
||||||
|
of: find.byType(ChatListViewBody),
|
||||||
|
matching: find.byType(Scrollable),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text(Users.user2.name).first);
|
||||||
|
|
||||||
await tester.enterText(input, homeserver);
|
try {
|
||||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
await tester.waitFor(
|
||||||
await tester.pumpAndSettle();
|
find.byType(ChatView),
|
||||||
|
timeout: const Duration(seconds: 5),
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// in case the homeserver sends the username as search result
|
||||||
|
if (find.byIcon(Icons.send_outlined).evaluate().isNotEmpty) {
|
||||||
|
await tester.tap(find.byIcon(Icons.send_outlined));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// in case registration is allowed
|
await tester.waitFor(find.byType(ChatView));
|
||||||
try {
|
await tester.enterText(find.byType(TextField).last, 'Test');
|
||||||
await tester.tap(find.text('Login'));
|
await tester.pumpAndSettle();
|
||||||
await tester.pumpAndSettle();
|
try {
|
||||||
} catch (e) {
|
await tester.waitFor(find.byIcon(Icons.send_outlined));
|
||||||
log('Registration is not allowed. Proceeding with login...');
|
await tester.tap(find.byIcon(Icons.send_outlined));
|
||||||
}
|
} catch (_) {
|
||||||
await tester.pumpAndSettle();
|
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||||
|
}
|
||||||
final inputs = find.byType(TextField);
|
await tester.pumpAndSettle();
|
||||||
|
await tester.waitFor(find.text('Test'));
|
||||||
await tester.enterText(inputs.first, Users.user1.name);
|
await tester.pumpAndSettle();
|
||||||
await tester.enterText(inputs.last, Users.user1.password);
|
},
|
||||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
);
|
||||||
});
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
182
integration_test/extensions/default_flows.dart
Normal file
182
integration_test/extensions/default_flows.dart
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:fluffychat/pages/chat_list/chat_list_body.dart';
|
||||||
|
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker.dart';
|
||||||
|
import 'package:fluffychat/pages/settings_account/settings_account_view.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../users.dart';
|
||||||
|
import 'wait_for.dart';
|
||||||
|
|
||||||
|
extension DefaultFlowExtensions on WidgetTester {
|
||||||
|
Future<void> login() async {
|
||||||
|
final tester = this;
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.waitFor(find.text('Let\'s start'));
|
||||||
|
|
||||||
|
expect(find.text('Let\'s start'), findsOneWidget);
|
||||||
|
|
||||||
|
final input = find.byType(TextField);
|
||||||
|
|
||||||
|
expect(input, findsOneWidget);
|
||||||
|
|
||||||
|
// getting the placeholder in place
|
||||||
|
await tester.tap(find.byIcon(Icons.search));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.enterText(input, homeserver);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// in case registration is allowed
|
||||||
|
// try {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
|
||||||
|
await tester.scrollUntilVisible(
|
||||||
|
find.text('Login'),
|
||||||
|
500,
|
||||||
|
scrollable: find.descendant(
|
||||||
|
of: find.byKey(const Key('ConnectPageListView')),
|
||||||
|
matching: find.byType(Scrollable).first,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Login'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
/*} catch (e) {
|
||||||
|
log('Registration is not allowed. Proceeding with login...');
|
||||||
|
}*/
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
|
||||||
|
final inputs = find.byType(TextField);
|
||||||
|
|
||||||
|
await tester.enterText(inputs.first, Users.user1.name);
|
||||||
|
await tester.enterText(inputs.last, Users.user1.password);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// pumpAndSettle does not work in here as setState is called
|
||||||
|
// asynchronously
|
||||||
|
await tester.waitFor(
|
||||||
|
find.byType(LinearProgressIndicator),
|
||||||
|
timeout: const Duration(milliseconds: 1500),
|
||||||
|
skipPumpAndSettle: true,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// in case the input action does not work on the desired platform
|
||||||
|
if (find.text('Login').evaluate().isNotEmpty) {
|
||||||
|
await tester.tap(find.text('Login'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
} catch (_) {
|
||||||
|
// may fail because of ongoing animation below dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.waitFor(
|
||||||
|
find.byType(ChatListViewBody),
|
||||||
|
skipPumpAndSettle: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ensure PushProvider check passes
|
||||||
|
Future<void> acceptPushWarning() async {
|
||||||
|
final tester = this;
|
||||||
|
|
||||||
|
final matcher = find.maybeUppercaseText('Do not show again');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await tester.waitFor(matcher, timeout: const Duration(seconds: 5));
|
||||||
|
|
||||||
|
// the FCM push error dialog to be handled...
|
||||||
|
await tester.tap(matcher);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> ensureLoggedOut() async {
|
||||||
|
final tester = this;
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
if (find.byType(ChatListViewBody).evaluate().isNotEmpty) {
|
||||||
|
await tester.tap(find.byTooltip('Show menu'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Settings'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.scrollUntilVisible(
|
||||||
|
find.text('Account'),
|
||||||
|
500,
|
||||||
|
scrollable: find.descendant(
|
||||||
|
of: find.byKey(const Key('SettingsListViewContent')),
|
||||||
|
matching: find.byType(Scrollable),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Account'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.scrollUntilVisible(
|
||||||
|
find.text('Logout'),
|
||||||
|
500,
|
||||||
|
scrollable: find.descendant(
|
||||||
|
of: find.byType(SettingsAccountView),
|
||||||
|
matching: find.byType(Scrollable),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Logout'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.maybeUppercaseText('Yes'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> ensureAppStartedHomescreen({
|
||||||
|
Duration timeout = const Duration(seconds: 20),
|
||||||
|
}) async {
|
||||||
|
final tester = this;
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
final homeserverPickerFinder = find.byType(HomeserverPicker);
|
||||||
|
final chatListFinder = find.byType(ChatListViewBody);
|
||||||
|
|
||||||
|
final end = DateTime.now().add(timeout);
|
||||||
|
|
||||||
|
log(
|
||||||
|
'Waiting for HomeserverPicker or ChatListViewBody...',
|
||||||
|
name: 'Test Runner',
|
||||||
|
);
|
||||||
|
do {
|
||||||
|
if (DateTime.now().isAfter(end)) {
|
||||||
|
throw Exception(
|
||||||
|
'Timed out waiting for HomeserverPicker or ChatListViewBody');
|
||||||
|
}
|
||||||
|
|
||||||
|
await pumpAndSettle();
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
} while (homeserverPickerFinder.evaluate().isEmpty &&
|
||||||
|
chatListFinder.evaluate().isEmpty);
|
||||||
|
|
||||||
|
if (homeserverPickerFinder.evaluate().isNotEmpty) {
|
||||||
|
log(
|
||||||
|
'Found HomeserverPicker, performing login.',
|
||||||
|
name: 'Test Runner',
|
||||||
|
);
|
||||||
|
await tester.login();
|
||||||
|
} else {
|
||||||
|
log(
|
||||||
|
'Found ChatListViewBody, skipping login.',
|
||||||
|
name: 'Test Runner',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.acceptPushWarning();
|
||||||
|
}
|
||||||
|
}
|
49
integration_test/extensions/wait_for.dart
Normal file
49
integration_test/extensions/wait_for.dart
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
/// Workaround for https://github.com/flutter/flutter/issues/88765
|
||||||
|
extension WaitForExtension on WidgetTester {
|
||||||
|
Future<void> waitFor(
|
||||||
|
Finder finder, {
|
||||||
|
Duration timeout = const Duration(seconds: 20),
|
||||||
|
bool skipPumpAndSettle = false,
|
||||||
|
}) async {
|
||||||
|
final end = DateTime.now().add(timeout);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (DateTime.now().isAfter(end)) {
|
||||||
|
throw Exception('Timed out waiting for $finder');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipPumpAndSettle) {
|
||||||
|
await pumpAndSettle();
|
||||||
|
}
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
} while (finder.evaluate().isEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MaybeUppercaseFinder on CommonFinders {
|
||||||
|
/// On Android some button labels are in uppercase while on iOS they
|
||||||
|
/// are not. This method tries both.
|
||||||
|
Finder maybeUppercaseText(
|
||||||
|
String text, {
|
||||||
|
bool findRichText = false,
|
||||||
|
bool skipOffstage = true,
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
final finder = find.text(
|
||||||
|
text.toUpperCase(),
|
||||||
|
findRichText: findRichText,
|
||||||
|
skipOffstage: skipOffstage,
|
||||||
|
);
|
||||||
|
expect(finder, findsOneWidget);
|
||||||
|
return finder;
|
||||||
|
} catch (_) {
|
||||||
|
return find.text(
|
||||||
|
text,
|
||||||
|
findRichText: findRichText,
|
||||||
|
skipOffstage: skipOffstage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,25 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
abstract class Users {
|
abstract class Users {
|
||||||
const Users._();
|
const Users._();
|
||||||
|
|
||||||
static final user1 = User(
|
static const user1 = User(
|
||||||
Platform.environment['USER1_NAME'] ?? 'alice',
|
String.fromEnvironment(
|
||||||
Platform.environment['USER1_PW'] ?? 'AliceInWonderland',
|
'USER1_NAME',
|
||||||
|
defaultValue: 'alice',
|
||||||
|
),
|
||||||
|
String.fromEnvironment(
|
||||||
|
'USER1_PW',
|
||||||
|
defaultValue: 'AliceInWonderland',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
static final user2 = User(
|
static const user2 = User(
|
||||||
Platform.environment['USER2_NAME'] ?? 'bob',
|
String.fromEnvironment(
|
||||||
Platform.environment['USER2_PW'] ?? 'JoWirSchaffenDas',
|
'USER2_NAME',
|
||||||
|
defaultValue: 'bob',
|
||||||
|
),
|
||||||
|
String.fromEnvironment(
|
||||||
|
'USER2_PW',
|
||||||
|
defaultValue: 'JoWirSchaffenDas',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,5 +30,7 @@ class User {
|
|||||||
const User(this.name, this.password);
|
const User(this.name, this.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
final homeserver =
|
const homeserver = 'http://${const String.fromEnvironment(
|
||||||
'http://${Platform.environment['HOMESERVER'] ?? 'localhost'}';
|
'HOMESERVER',
|
||||||
|
defaultValue: 'localhost',
|
||||||
|
)}';
|
||||||
|
@ -104,8 +104,13 @@ class ClientChooserButton extends StatelessWidget {
|
|||||||
.map(
|
.map(
|
||||||
(client) => PopupMenuItem(
|
(client) => PopupMenuItem(
|
||||||
value: client,
|
value: client,
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile?>(
|
||||||
future: client!.fetchOwnProfile(),
|
// analyzer does not understand this type cast for error
|
||||||
|
// handling
|
||||||
|
//
|
||||||
|
// ignore: unnecessary_cast
|
||||||
|
future: (client!.fetchOwnProfile() as Future<Profile?>)
|
||||||
|
.onError((e, s) => null),
|
||||||
builder: (context, snapshot) => Row(
|
builder: (context, snapshot) => Row(
|
||||||
children: [
|
children: [
|
||||||
Avatar(
|
Avatar(
|
||||||
|
@ -28,6 +28,7 @@ class ConnectPageView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
|
key: const Key('ConnectPageListView'),
|
||||||
children: [
|
children: [
|
||||||
if (Matrix.of(context).loginRegistrationSupported ?? false) ...[
|
if (Matrix.of(context).loginRegistrationSupported ?? false) ...[
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
|
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
import '../../utils/platform_infos.dart';
|
import '../../utils/platform_infos.dart';
|
||||||
import 'login_view.dart';
|
import 'login_view.dart';
|
||||||
@ -30,7 +31,7 @@ class LoginController extends State<Login> {
|
|||||||
void toggleShowPassword() =>
|
void toggleShowPassword() =>
|
||||||
setState(() => showPassword = !loading && !showPassword);
|
setState(() => showPassword = !loading && !showPassword);
|
||||||
|
|
||||||
void login([_]) async {
|
void login() async {
|
||||||
final matrix = Matrix.of(context);
|
final matrix = Matrix.of(context);
|
||||||
if (usernameController.text.isEmpty) {
|
if (usernameController.text.isEmpty) {
|
||||||
setState(() => usernameError = L10n.of(context)!.pleaseEnterYourUsername);
|
setState(() => usernameError = L10n.of(context)!.pleaseEnterYourUsername);
|
||||||
@ -48,6 +49,9 @@ class LoginController extends State<Login> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setState(() => loading = true);
|
setState(() => loading = true);
|
||||||
|
|
||||||
|
_coolDown?.cancel();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final username = usernameController.text;
|
final username = usernameController.text;
|
||||||
AuthenticationIdentifier identifier;
|
AuthenticationIdentifier identifier;
|
||||||
@ -97,8 +101,8 @@ class LoginController extends State<Login> {
|
|||||||
void _checkWellKnown(String userId) async {
|
void _checkWellKnown(String userId) async {
|
||||||
if (mounted) setState(() => usernameError = null);
|
if (mounted) setState(() => usernameError = null);
|
||||||
if (!userId.isValidMatrixId) return;
|
if (!userId.isValidMatrixId) return;
|
||||||
|
final oldHomeserver = Matrix.of(context).getLoginClient().homeserver;
|
||||||
try {
|
try {
|
||||||
final oldHomeserver = Matrix.of(context).getLoginClient().homeserver;
|
|
||||||
var newDomain = Uri.https(userId.domain!, '');
|
var newDomain = Uri.https(userId.domain!, '');
|
||||||
Matrix.of(context).getLoginClient().homeserver = newDomain;
|
Matrix.of(context).getLoginClient().homeserver = newDomain;
|
||||||
DiscoveryInformation? wellKnownInformation;
|
DiscoveryInformation? wellKnownInformation;
|
||||||
@ -112,14 +116,11 @@ class LoginController extends State<Login> {
|
|||||||
// do nothing, newDomain is already set to a reasonable fallback
|
// do nothing, newDomain is already set to a reasonable fallback
|
||||||
}
|
}
|
||||||
if (newDomain != oldHomeserver) {
|
if (newDomain != oldHomeserver) {
|
||||||
await showFutureLoadingDialog(
|
Matrix.of(context)
|
||||||
context: context,
|
.getLoginClient()
|
||||||
// do nothing if we error, we'll handle it below
|
.checkHomeserver(newDomain)
|
||||||
future: () => Matrix.of(context)
|
.catchError((e) {});
|
||||||
.getLoginClient()
|
|
||||||
.checkHomeserver(newDomain)
|
|
||||||
.catchError((e) {}),
|
|
||||||
);
|
|
||||||
if (Matrix.of(context).getLoginClient().homeserver == null) {
|
if (Matrix.of(context).getLoginClient().homeserver == null) {
|
||||||
Matrix.of(context).getLoginClient().homeserver = oldHomeserver;
|
Matrix.of(context).getLoginClient().homeserver = oldHomeserver;
|
||||||
// okay, the server we checked does not appear to be a matrix server
|
// okay, the server we checked does not appear to be a matrix server
|
||||||
@ -140,15 +141,18 @@ class LoginController extends State<Login> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mounted) setState(() => usernameError = null);
|
usernameError = null;
|
||||||
|
if (mounted) setState(() {});
|
||||||
} else {
|
} else {
|
||||||
|
Matrix.of(context).getLoginClient().homeserver = oldHomeserver;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() =>
|
setState(() {});
|
||||||
Matrix.of(context).getLoginClient().homeserver = oldHomeserver);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) setState(() => usernameError = e.toString());
|
Matrix.of(context).getLoginClient().homeserver = oldHomeserver;
|
||||||
|
usernameError = e.toLocalizedString(context);
|
||||||
|
if (mounted) setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class LoginView extends StatelessWidget {
|
|||||||
controller: controller.passwordController,
|
controller: controller.passwordController,
|
||||||
textInputAction: TextInputAction.go,
|
textInputAction: TextInputAction.go,
|
||||||
obscureText: !controller.showPassword,
|
obscureText: !controller.showPassword,
|
||||||
onSubmitted: controller.login,
|
onSubmitted: (_) => controller.login(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.lock_outlined),
|
prefixIcon: const Icon(Icons.lock_outlined),
|
||||||
errorText: controller.passwordError,
|
errorText: controller.passwordError,
|
||||||
@ -87,9 +87,7 @@ class LoginView extends StatelessWidget {
|
|||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
),
|
),
|
||||||
onPressed: controller.loading
|
onPressed: controller.loading ? null : controller.login,
|
||||||
? null
|
|
||||||
: () => controller.login(context),
|
|
||||||
icon: const Icon(Icons.login_outlined),
|
icon: const Icon(Icons.login_outlined),
|
||||||
label: controller.loading
|
label: controller.loading
|
||||||
? const LinearProgressIndicator()
|
? const LinearProgressIndicator()
|
||||||
|
@ -38,6 +38,7 @@ class SettingsView extends StatelessWidget {
|
|||||||
body: ListTileTheme(
|
body: ListTileTheme(
|
||||||
iconColor: Theme.of(context).colorScheme.onBackground,
|
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
|
key: const Key('SettingsListViewContent'),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
height: controller.showChatBackupBanner ? 54 : 0,
|
height: controller.showChatBackupBanner ? 54 : 0,
|
||||||
|
@ -11,6 +11,7 @@ class ContentBanner extends StatelessWidget {
|
|||||||
final void Function()? onEdit;
|
final void Function()? onEdit;
|
||||||
final Client? client;
|
final Client? client;
|
||||||
final double opacity;
|
final double opacity;
|
||||||
|
final WidgetBuilder? placeholder;
|
||||||
|
|
||||||
const ContentBanner(
|
const ContentBanner(
|
||||||
{this.mxContent,
|
{this.mxContent,
|
||||||
@ -19,6 +20,7 @@ class ContentBanner extends StatelessWidget {
|
|||||||
this.onEdit,
|
this.onEdit,
|
||||||
this.client,
|
this.client,
|
||||||
this.opacity = 0.75,
|
this.opacity = 0.75,
|
||||||
|
this.placeholder,
|
||||||
Key? key})
|
Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@ -54,6 +56,7 @@ class ContentBanner extends StatelessWidget {
|
|||||||
uri: mxContent,
|
uri: mxContent,
|
||||||
animated: true,
|
animated: true,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
placeholder: placeholder,
|
||||||
height: 400,
|
height: 400,
|
||||||
width: 800,
|
width: 800,
|
||||||
),
|
),
|
||||||
|
@ -327,15 +327,15 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (state != LoginState.loggedIn) {
|
if (state != LoginState.loggedIn) {
|
||||||
widget.router!.currentState!.to(
|
widget.router?.currentState?.to(
|
||||||
'/rooms',
|
'/rooms',
|
||||||
queryParameters: widget.router!.currentState!.queryParameters,
|
queryParameters: widget.router?.currentState?.queryParameters ?? {},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
widget.router!.currentState!.to(
|
widget.router?.currentState?.to(
|
||||||
state == LoginState.loggedIn ? '/rooms' : '/home',
|
state == LoginState.loggedIn ? '/rooms' : '/home',
|
||||||
queryParameters: widget.router!.currentState!.queryParameters,
|
queryParameters: widget.router?.currentState?.queryParameters ?? {},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,7 @@ import '../utils/localized_exception_extension.dart';
|
|||||||
class ProfileBottomSheet extends StatelessWidget {
|
class ProfileBottomSheet extends StatelessWidget {
|
||||||
final String userId;
|
final String userId;
|
||||||
final BuildContext outerContext;
|
final BuildContext outerContext;
|
||||||
|
|
||||||
const ProfileBottomSheet({
|
const ProfileBottomSheet({
|
||||||
required this.userId,
|
required this.userId,
|
||||||
required this.outerContext,
|
required this.outerContext,
|
||||||
@ -78,6 +79,13 @@ class ProfileBottomSheet extends StatelessWidget {
|
|||||||
mxContent: profile.avatarUrl,
|
mxContent: profile.avatarUrl,
|
||||||
defaultIcon: Icons.account_circle_outlined,
|
defaultIcon: Icons.account_circle_outlined,
|
||||||
client: Matrix.of(context).client,
|
client: Matrix.of(context).client,
|
||||||
|
placeholder: (context) => Center(
|
||||||
|
child: Text(
|
||||||
|
userId.localpart ?? userId,
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.headline3,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
@ -6,6 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'package:system_theme/system_theme.dart';
|
import 'package:system_theme/system_theme.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
|
import 'package:fluffychat/utils/platform_infos.dart';
|
||||||
|
|
||||||
class ThemeBuilder extends StatefulWidget {
|
class ThemeBuilder extends StatefulWidget {
|
||||||
final Widget Function(
|
final Widget Function(
|
||||||
@ -34,6 +35,7 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
Color? _primaryColor;
|
Color? _primaryColor;
|
||||||
|
|
||||||
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
|
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
|
||||||
|
|
||||||
Color? get primaryColor => _primaryColor;
|
Color? get primaryColor => _primaryColor;
|
||||||
|
|
||||||
static ThemeController of(BuildContext context) =>
|
static ThemeController of(BuildContext context) =>
|
||||||
@ -87,10 +89,18 @@ class ThemeController extends State<ThemeBuilder> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Color? get systemAccentColor {
|
Color get systemAccentColor {
|
||||||
final color = SystemTheme.accentColor.accent;
|
if (PlatformInfos.isLinux) return AppConfig.chatColor;
|
||||||
if (color == kDefaultSystemAccentColor) return AppConfig.chatColor;
|
try {
|
||||||
return color;
|
// a bad plugin implementation
|
||||||
|
// https://github.com/bdlukaa/system_theme/issues/10
|
||||||
|
final accentColor = SystemTheme.accentColor;
|
||||||
|
final color = accentColor.accent;
|
||||||
|
if (color == kDefaultSystemAccentColor) return AppConfig.chatColor;
|
||||||
|
return color;
|
||||||
|
} catch (_) {
|
||||||
|
return AppConfig.chatColor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
apk update && apk add docker drill grep
|
|
@ -33,3 +33,28 @@ echo "Homeserver is up."
|
|||||||
|
|
||||||
curl -fS --retry 3 -XPOST -d "{\"username\":\"$USER1_NAME\", \"password\":\"$USER1_PW\", \"inhibit_login\":true, \"auth\": {\"type\":\"m.login.dummy\"}}" "http://$HOMESERVER/_matrix/client/r0/register"
|
curl -fS --retry 3 -XPOST -d "{\"username\":\"$USER1_NAME\", \"password\":\"$USER1_PW\", \"inhibit_login\":true, \"auth\": {\"type\":\"m.login.dummy\"}}" "http://$HOMESERVER/_matrix/client/r0/register"
|
||||||
curl -fS --retry 3 -XPOST -d "{\"username\":\"$USER2_NAME\", \"password\":\"$USER2_PW\", \"inhibit_login\":true, \"auth\": {\"type\":\"m.login.dummy\"}}" "http://$HOMESERVER/_matrix/client/r0/register"
|
curl -fS --retry 3 -XPOST -d "{\"username\":\"$USER2_NAME\", \"password\":\"$USER2_PW\", \"inhibit_login\":true, \"auth\": {\"type\":\"m.login.dummy\"}}" "http://$HOMESERVER/_matrix/client/r0/register"
|
||||||
|
|
||||||
|
usertoken1=$(curl -fS --retry 3 "http://$HOMESERVER/_matrix/client/r0/login" -H "Content-Type: application/json" -d "{\"type\": \"m.login.password\", \"identifier\": {\"type\": \"m.id.user\",\"user\": \"$USER1_NAME\"},\"password\":\"$USER1_PW\"}" | jq -r '.access_token')
|
||||||
|
usertoken2=$(curl -fS --retry 3 "http://$HOMESERVER/_matrix/client/r0/login" -H "Content-Type: application/json" -d "{\"type\": \"m.login.password\", \"identifier\": {\"type\": \"m.id.user\",\"user\": \"$USER2_NAME\"},\"password\":\"$USER2_PW\"}" | jq -r '.access_token')
|
||||||
|
|
||||||
|
|
||||||
|
# get usernames' mxids
|
||||||
|
mxid1=$(curl -fS --retry 3 "http://$HOMESERVER/_matrix/client/r0/account/whoami" -H "Authorization: Bearer $usertoken1" | jq -r .user_id)
|
||||||
|
mxid2=$(curl -fS --retry 3 "http://$HOMESERVER/_matrix/client/r0/account/whoami" -H "Authorization: Bearer $usertoken2" | jq -r .user_id)
|
||||||
|
|
||||||
|
# setting the display name to username
|
||||||
|
curl -fS --retry 3 -XPUT -d "{\"displayname\":\"$USER1_NAME\"}" "http://$HOMESERVER/_matrix/client/v3/profile/$mxid1/displayname" -H "Authorization: Bearer $usertoken1"
|
||||||
|
curl -fS --retry 3 -XPUT -d "{\"displayname\":\"$USER2_NAME\"}" "http://$HOMESERVER/_matrix/client/v3/profile/$mxid2/displayname" -H "Authorization: Bearer $usertoken2"
|
||||||
|
|
||||||
|
echo "Set display names"
|
||||||
|
|
||||||
|
# create new room to invite user too
|
||||||
|
roomID=$(curl --retry 3 --silent --fail -XPOST -d "{\"name\":\"$USER2_NAME\", \"is_direct\": true}" "http://$HOMESERVER/_matrix/client/r0/createRoom?access_token=$usertoken2" | jq -r '.room_id')
|
||||||
|
echo "Created room '$roomID'"
|
||||||
|
|
||||||
|
# send message in created room
|
||||||
|
curl --retry 3 --fail --silent -XPOST -d '{"msgtype":"m.text", "body":"joined room successfully"}' "http://$HOMESERVER/_matrix/client/r0/rooms/$roomID/send/m.room.message?access_token=$usertoken2"
|
||||||
|
echo "Sent message"
|
||||||
|
|
||||||
|
curl -fS --retry 3 -XPOST -d "{\"user_id\":\"$mxid1\"}" "http://$HOMESERVER/_matrix/client/r0/rooms/$roomID/invite?access_token=$usertoken2"
|
||||||
|
echo "Invited $USER1_NAME"
|
||||||
|
6
scripts/integration-prepare-host.sh
Executable file
6
scripts/integration-prepare-host.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
if ! command -v apk &>/dev/null; then
|
||||||
|
apt update && apt install -y -qq docker.io ldnsutils grep scrcpy ffmpeg
|
||||||
|
else
|
||||||
|
apk update && apk add docker drill grep scrcpy ffmpeg
|
||||||
|
fi
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
chmod 777 -R /dev/kvm
|
chmod 777 -R /dev/kvm
|
||||||
adb start-server
|
adb start-server
|
||||||
emulator -avd test -no-audio -no-boot-anim -no-window -accel on -gpu swiftshader_indirect
|
emulator -avd test -wipe-data -no-audio -no-boot-anim -no-window -accel on -gpu swiftshader_indirect
|
||||||
|
Loading…
Reference in New Issue
Block a user