@ -0,0 +1,563 @@ |
|||||||
|
import 'dart:io'; |
||||||
|
|
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter/services.dart'; |
||||||
|
import 'package:flutter_test/flutter_test.dart'; |
||||||
|
import 'package:integration_test/integration_test.dart'; |
||||||
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; |
||||||
|
import 'package:neon/l10n/localizations.dart'; |
||||||
|
import 'package:neon/src/apps/files/app.dart'; |
||||||
|
import 'package:neon/src/blocs/accounts.dart'; |
||||||
|
import 'package:neon/src/blocs/capabilities.dart'; |
||||||
|
import 'package:neon/src/blocs/push_notifications.dart'; |
||||||
|
import 'package:neon/src/models/account.dart'; |
||||||
|
import 'package:neon/src/neon.dart'; |
||||||
|
import 'package:nextcloud/nextcloud.dart'; |
||||||
|
import 'package:package_info_plus/package_info_plus.dart'; |
||||||
|
import 'package:provider/provider.dart'; |
||||||
|
import 'package:rxdart/rxdart.dart'; |
||||||
|
import 'package:shared_preferences/shared_preferences.dart'; |
||||||
|
|
||||||
|
class MemorySharedPreferences implements SharedPreferences { |
||||||
|
final _data = <String, dynamic>{}; |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> clear() async { |
||||||
|
_data.clear(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> commit() async => true; |
||||||
|
|
||||||
|
@override |
||||||
|
Future reload() async {} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> remove(String key) async { |
||||||
|
_data.remove(key); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Set<String> getKeys() => _data.keys.toSet(); |
||||||
|
|
||||||
|
@override |
||||||
|
bool containsKey(String key) => _data.keys.contains(key); |
||||||
|
|
||||||
|
@override |
||||||
|
Object? get(String key) => _data[key]; |
||||||
|
|
||||||
|
@override |
||||||
|
bool? getBool(String key) => _data[key] as bool?; |
||||||
|
|
||||||
|
@override |
||||||
|
double? getDouble(String key) => _data[key] as double?; |
||||||
|
|
||||||
|
@override |
||||||
|
int? getInt(String key) => _data[key] as int?; |
||||||
|
|
||||||
|
@override |
||||||
|
String? getString(String key) => _data[key] as String?; |
||||||
|
|
||||||
|
@override |
||||||
|
List<String>? getStringList(String key) => (_data[key] as List).cast<String>(); |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> setBool(String key, bool value) async { |
||||||
|
_data[key] = value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> setDouble(String key, double value) async { |
||||||
|
_data[key] = value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> setInt(String key, int value) async { |
||||||
|
_data[key] = value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> setString(String key, String value) async { |
||||||
|
_data[key] = value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> setStringList(String key, List<String> value) async { |
||||||
|
_data[key] = value; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Future pumpAppPage( |
||||||
|
final WidgetTester tester, |
||||||
|
final IntegrationTestWidgetsFlutterBinding binding, { |
||||||
|
required final Widget Function(BuildContext, Function(NextcloudTheme)) builder, |
||||||
|
final Account? account, |
||||||
|
}) async { |
||||||
|
final sharedPreferences = MemorySharedPreferences(); |
||||||
|
|
||||||
|
final platform = await getNeonPlatform(); |
||||||
|
final requestManager = RequestManager(); |
||||||
|
final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform); |
||||||
|
|
||||||
|
final packageInfo = await PackageInfo.fromPlatform(); |
||||||
|
|
||||||
|
final globalOptions = GlobalOptions( |
||||||
|
Storage('global', sharedPreferences), |
||||||
|
packageInfo, |
||||||
|
); |
||||||
|
await globalOptions.pushNotificationsEnabled.set(false); |
||||||
|
|
||||||
|
final accountsBloc = AccountsBloc( |
||||||
|
requestManager, |
||||||
|
Storage('accounts', sharedPreferences), |
||||||
|
sharedPreferences, |
||||||
|
globalOptions, |
||||||
|
packageInfo, |
||||||
|
); |
||||||
|
if (account != null) { |
||||||
|
accountsBloc.addAccount(account..setupClient(packageInfo)); |
||||||
|
} |
||||||
|
|
||||||
|
final pushNotificationsBloc = PushNotificationsBloc( |
||||||
|
accountsBloc, |
||||||
|
sharedPreferences, |
||||||
|
globalOptions, |
||||||
|
null, |
||||||
|
platform, |
||||||
|
); |
||||||
|
|
||||||
|
// ignore: close_sinks |
||||||
|
final userThemeStream = BehaviorSubject<NextcloudTheme>(); |
||||||
|
|
||||||
|
await tester.pumpWidget( |
||||||
|
MultiProvider( |
||||||
|
providers: [ |
||||||
|
Provider<SharedPreferences>( |
||||||
|
create: (final _) => sharedPreferences, |
||||||
|
), |
||||||
|
Provider<Env?>( |
||||||
|
create: (final _) => null, |
||||||
|
), |
||||||
|
Provider<NeonPlatform>( |
||||||
|
create: (final _) => platform, |
||||||
|
), |
||||||
|
Provider<GlobalOptions>( |
||||||
|
create: (final _) => globalOptions, |
||||||
|
), |
||||||
|
Provider<RequestManager>( |
||||||
|
create: (final _) => requestManager, |
||||||
|
), |
||||||
|
Provider<AccountsBloc>( |
||||||
|
create: (final _) => accountsBloc, |
||||||
|
), |
||||||
|
Provider<PushNotificationsBloc>( |
||||||
|
create: (final _) => pushNotificationsBloc, |
||||||
|
), |
||||||
|
Provider<List<AppImplementation>>( |
||||||
|
create: (final _) => allAppImplementations, |
||||||
|
), |
||||||
|
Provider<PackageInfo>( |
||||||
|
create: (final _) => packageInfo, |
||||||
|
), |
||||||
|
], |
||||||
|
child: StreamBuilder<NextcloudTheme>( |
||||||
|
stream: userThemeStream, |
||||||
|
builder: (final context, final themeSnapshot) => StreamBuilder<ThemeMode>( |
||||||
|
stream: globalOptions.themeMode.stream, |
||||||
|
builder: (final context, final themeModeSnapshot) => StreamBuilder<bool>( |
||||||
|
stream: globalOptions.themeOLEDAsDark.stream, |
||||||
|
builder: (final context, final themeOLEDAsDarkSnapshot) => MaterialApp( |
||||||
|
debugShowCheckedModeBanner: false, |
||||||
|
localizationsDelegates: AppLocalizations.localizationsDelegates, |
||||||
|
supportedLocales: AppLocalizations.supportedLocales, |
||||||
|
theme: getThemeFromNextcloudTheme( |
||||||
|
themeSnapshot.data, |
||||||
|
themeModeSnapshot.data ?? ThemeMode.system, |
||||||
|
Brightness.light, |
||||||
|
oledAsDark: themeOLEDAsDarkSnapshot.data ?? false, |
||||||
|
), |
||||||
|
home: builder(context, userThemeStream.add), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
} |
||||||
|
|
||||||
|
Future openDrawer(final WidgetTester tester) async { |
||||||
|
await tester.tap(find.byTooltip('Open navigation menu')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
} |
||||||
|
|
||||||
|
Future switchPage(final WidgetTester tester, final String name) async { |
||||||
|
await openDrawer(tester); |
||||||
|
await tester.tap(find.byKey(Key(name))); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
} |
||||||
|
|
||||||
|
Future prepareScreenshot(final WidgetTester tester, final IntegrationTestWidgetsFlutterBinding binding) async { |
||||||
|
await binding.convertFlutterSurfaceToImage(); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
} |
||||||
|
|
||||||
|
Future main() async { |
||||||
|
// The screenshots are pretty annoying on Android. See https://github.com/flutter/flutter/issues/92381 |
||||||
|
|
||||||
|
assert(Platform.isAndroid, 'Screenshots need to be taken on Android'); |
||||||
|
|
||||||
|
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); |
||||||
|
|
||||||
|
final account = Account( |
||||||
|
serverURL: 'http://10.0.2.2', |
||||||
|
username: 'test', |
||||||
|
password: 'supersafepasswordtocircumventpasswordpolicies', |
||||||
|
); |
||||||
|
|
||||||
|
setUpAll(() async { |
||||||
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); |
||||||
|
}); |
||||||
|
|
||||||
|
testWidgets('login', (final tester) async { |
||||||
|
await pumpAppPage( |
||||||
|
tester, |
||||||
|
binding, |
||||||
|
builder: (final context, final _) => const LoginPage(), |
||||||
|
); |
||||||
|
await prepareScreenshot(tester, binding); |
||||||
|
await binding.takeScreenshot('login_server_selection'); |
||||||
|
|
||||||
|
await tester.enterText(find.byType(TextFormField), account.serverURL); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.testTextInput.receiveAction(TextInputAction.done); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(const Duration(seconds: 3)); // Make sure the login webview is loaded |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await binding.takeScreenshot('login_form'); |
||||||
|
}); |
||||||
|
|
||||||
|
testWidgets('home', (final tester) async { |
||||||
|
await pumpAppPage( |
||||||
|
tester, |
||||||
|
binding, |
||||||
|
account: account, |
||||||
|
builder: (final context, final onThemeChanged) => HomePage( |
||||||
|
account: account, |
||||||
|
onThemeChanged: onThemeChanged, |
||||||
|
), |
||||||
|
); |
||||||
|
await openDrawer(tester); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(); |
||||||
|
await prepareScreenshot(tester, binding); |
||||||
|
await binding.takeScreenshot('home_drawer'); |
||||||
|
}); |
||||||
|
|
||||||
|
testWidgets('files', (final tester) async { |
||||||
|
await pumpAppPage( |
||||||
|
tester, |
||||||
|
binding, |
||||||
|
account: account, |
||||||
|
builder: (final context, final onThemeChanged) => HomePage( |
||||||
|
account: account, |
||||||
|
onThemeChanged: onThemeChanged, |
||||||
|
), |
||||||
|
); |
||||||
|
await prepareScreenshot(tester, binding); |
||||||
|
await binding.takeScreenshot('files_root'); |
||||||
|
|
||||||
|
// Show Photos folder |
||||||
|
await tester.tap(find.text('Photos')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('files_photos'); |
||||||
|
|
||||||
|
// Show file actions |
||||||
|
await tester.tap(find.text('Photos')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.byType(PopupMenuButton<FilesFileAction>).first); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('files_actions'); |
||||||
|
|
||||||
|
// Show details page |
||||||
|
await tester.tap(find.text('Details')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('files_details'); |
||||||
|
|
||||||
|
// Show create dialog |
||||||
|
await tester.tap(find.byIcon(Icons.arrow_back)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.byType(FloatingActionButton)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('files_create'); |
||||||
|
}); |
||||||
|
|
||||||
|
testWidgets('news', (final tester) async { |
||||||
|
const wikipediaFeedURL = 'https://en.wikipedia.org/w/api.php?action=featuredfeed&feed=featured&feedformat=atom'; |
||||||
|
const nasaFeedURL = 'https://www.nasa.gov/rss/dyn/breaking_news.rss'; |
||||||
|
|
||||||
|
final folder = await account.client.news.createFolder( |
||||||
|
NewsCreateFolder( |
||||||
|
name: 'test', |
||||||
|
), |
||||||
|
); |
||||||
|
await account.client.news.addFeed( |
||||||
|
NewsAddFeed( |
||||||
|
url: nasaFeedURL, |
||||||
|
folderId: folder!.folders.single.id, |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
await pumpAppPage( |
||||||
|
tester, |
||||||
|
binding, |
||||||
|
account: account, |
||||||
|
builder: (final context, final onThemeChanged) => HomePage( |
||||||
|
account: account, |
||||||
|
onThemeChanged: onThemeChanged, |
||||||
|
), |
||||||
|
); |
||||||
|
await prepareScreenshot(tester, binding); |
||||||
|
await switchPage(tester, 'app-news'); |
||||||
|
|
||||||
|
// Show folders |
||||||
|
await tester.tap(find.byIcon(Icons.folder)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('news_folders_list'); |
||||||
|
|
||||||
|
// Add Wikipedia feed |
||||||
|
await tester.tap(find.byIcon(Icons.rss_feed)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(); |
||||||
|
await tester.tap(find.byType(FloatingActionButton)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('news_feed_add'); |
||||||
|
|
||||||
|
// Finish adding Wikipedia feed |
||||||
|
await tester.enterText(find.byType(TextFormField), wikipediaFeedURL); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.byType(ElevatedButton)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(const Duration(seconds: 3)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('news_feeds_list'); |
||||||
|
|
||||||
|
// Open feed |
||||||
|
await tester.tap(find.text('NASA Breaking News')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('news_feed_articles_list'); |
||||||
|
|
||||||
|
// Show unread articles |
||||||
|
await tester.tap(find.byIcon(Icons.arrow_back)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.byIcon(Icons.newspaper)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
// Star two articles |
||||||
|
await tester.tap(find.byIcon(Icons.star_outline).at(0)); |
||||||
|
await tester.tap(find.byIcon(Icons.star_outline).at(1)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(const Duration(seconds: 3)); |
||||||
|
|
||||||
|
await binding.takeScreenshot('news_articles_unread_list'); |
||||||
|
|
||||||
|
// Show starred articles |
||||||
|
await tester.tap(find.text('Unread')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.text('Starred').last); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(const Duration(seconds: 3)); |
||||||
|
|
||||||
|
await binding.takeScreenshot('news_articles_starred_list'); |
||||||
|
}); |
||||||
|
|
||||||
|
testWidgets('notes', (final tester) async { |
||||||
|
await account.client.notes.createNote( |
||||||
|
NotesNote( |
||||||
|
title: 'Wishlist', |
||||||
|
category: 'Financial', |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
await pumpAppPage( |
||||||
|
tester, |
||||||
|
binding, |
||||||
|
account: account, |
||||||
|
builder: (final context, final onThemeChanged) => HomePage( |
||||||
|
account: account, |
||||||
|
onThemeChanged: onThemeChanged, |
||||||
|
), |
||||||
|
); |
||||||
|
await prepareScreenshot(tester, binding); |
||||||
|
await switchPage(tester, 'app-notes'); |
||||||
|
|
||||||
|
// Create note |
||||||
|
await tester.tap(find.byType(FloatingActionButton)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.enterText(find.byType(TextFormField).first, 'Grocery'); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.byType(TextFormField).last); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('notes_note_create'); |
||||||
|
|
||||||
|
// Finish creating note |
||||||
|
await tester.enterText(find.byType(TextFormField).last, 'Financial'); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.testTextInput.receiveAction(TextInputAction.done); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(const Duration(seconds: 3)); |
||||||
|
|
||||||
|
// Star note |
||||||
|
await tester.tap(find.byIcon(Icons.star_outline).first); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(const Duration(seconds: 3)); |
||||||
|
|
||||||
|
await binding.takeScreenshot('notes_notes_list'); |
||||||
|
|
||||||
|
// Edit note |
||||||
|
await tester.tap(find.text('Grocery')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.enterText(find.byType(TextField).first, '- Bread\n- Water\n- Apples'); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(); // Needed for the text to actually show up |
||||||
|
|
||||||
|
await binding.takeScreenshot('notes_note_edit'); |
||||||
|
|
||||||
|
// Show note preview |
||||||
|
await tester.tap(find.byIcon(Icons.visibility)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('notes_note_preview'); |
||||||
|
|
||||||
|
// Show categories |
||||||
|
await tester.tap(find.byIcon(Icons.arrow_back)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.byIcon(MdiIcons.tag).last); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('notes_categories_list'); |
||||||
|
}); |
||||||
|
|
||||||
|
testWidgets('notifications', (final tester) async { |
||||||
|
await account.client.notifications.sendAdminNotification( |
||||||
|
account.username, |
||||||
|
NotificationsAdminNotification( |
||||||
|
shortMessage: 'Notifications demo', |
||||||
|
longMessage: 'This is a notifications demo of the Neon app', |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
await pumpAppPage( |
||||||
|
tester, |
||||||
|
binding, |
||||||
|
account: account, |
||||||
|
builder: (final context, final onThemeChanged) => HomePage( |
||||||
|
account: account, |
||||||
|
onThemeChanged: onThemeChanged, |
||||||
|
), |
||||||
|
); |
||||||
|
await prepareScreenshot(tester, binding); |
||||||
|
await switchPage(tester, 'app-notifications'); |
||||||
|
|
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('notifications_list'); |
||||||
|
}); |
||||||
|
|
||||||
|
testWidgets('settings', (final tester) async { |
||||||
|
await pumpAppPage( |
||||||
|
tester, |
||||||
|
binding, |
||||||
|
account: account, |
||||||
|
builder: (final context, final onThemeChanged) => HomePage( |
||||||
|
account: account, |
||||||
|
onThemeChanged: onThemeChanged, |
||||||
|
), |
||||||
|
); |
||||||
|
await prepareScreenshot(tester, binding); |
||||||
|
await switchPage(tester, 'settings'); |
||||||
|
|
||||||
|
// Open Files settings |
||||||
|
await tester.tap(find.text('Files')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_app_files'); |
||||||
|
|
||||||
|
// Open News settings |
||||||
|
await tester.tap(find.byIcon(Icons.arrow_back)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.text('News')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_app_news'); |
||||||
|
|
||||||
|
// Open Notes settings |
||||||
|
await tester.tap(find.byIcon(Icons.arrow_back)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.text('Notes')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_app_notes'); |
||||||
|
|
||||||
|
// Go back to main page |
||||||
|
await tester.tap(find.byIcon(Icons.arrow_back)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_light'); |
||||||
|
|
||||||
|
// Change to dark theme |
||||||
|
await tester.tap(find.text('Automatic')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.text('Dark').last); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_dark'); |
||||||
|
|
||||||
|
// Enable OLED theme |
||||||
|
await tester.tap(find.byType(CheckboxListTile).first); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_oled'); |
||||||
|
|
||||||
|
// Change to back to light theme |
||||||
|
await tester.tap(find.text('Dark')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.text('Light').last); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
// Scroll down to accounts |
||||||
|
await tester.drag(find.byType(ListView), const Offset(0, -10000)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_accounts'); |
||||||
|
|
||||||
|
// Go to account settings |
||||||
|
await tester.tap(find.byType(PopupMenuButton<SettingsAccountAction>)); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.tap(find.text('Settings').last); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
await tester.pump(); // Needed for the drop down button to actually show up |
||||||
|
await tester.tap(find.text('Automatic')); |
||||||
|
await tester.pumpAndSettle(); |
||||||
|
|
||||||
|
await binding.takeScreenshot('settings_account'); |
||||||
|
}); |
||||||
|
} |
After Width: | Height: | Size: 344 KiB |
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 274 KiB |
Before Width: | Height: | Size: 723 KiB After Width: | Height: | Size: 753 KiB |
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 324 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 918 KiB |
After Width: | Height: | Size: 971 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 288 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 271 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 294 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 135 KiB |
@ -0,0 +1,23 @@ |
|||||||
|
import 'dart:io'; |
||||||
|
|
||||||
|
import 'package:integration_test/integration_test_driver_extended.dart'; |
||||||
|
|
||||||
|
Future<void> main() async { |
||||||
|
final screenshotsDir = Directory('screenshots'); |
||||||
|
if (screenshotsDir.existsSync()) { |
||||||
|
screenshotsDir.deleteSync(recursive: true); |
||||||
|
} |
||||||
|
screenshotsDir.createSync(); |
||||||
|
|
||||||
|
try { |
||||||
|
await integrationDriver( |
||||||
|
onScreenshot: (final screenshotName, final screenshotBytes) async { |
||||||
|
File('screenshots/$screenshotName.png').writeAsBytesSync(screenshotBytes); |
||||||
|
return true; |
||||||
|
}, |
||||||
|
); |
||||||
|
} catch (e) { |
||||||
|
// ignore: avoid_print |
||||||
|
print('Error occurred: $e'); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
#!/bin/bash |
||||||
|
set -euxo pipefail |
||||||
|
cd "$(dirname "$0")/.." |
||||||
|
|
||||||
|
docker build -t nextcloud-neon-dev --build-arg "username=$username" --build-arg "password=$password" -f - ./packages/nextcloud/test < tool/Dockerfile.dev |
@ -0,0 +1,5 @@ |
|||||||
|
#!/bin/bash |
||||||
|
set -euxo pipefail |
||||||
|
|
||||||
|
export username="test" |
||||||
|
export password="supersafepasswordtocircumventpasswordpolicies" |
@ -0,0 +1,20 @@ |
|||||||
|
#!/bin/bash |
||||||
|
set -euxo pipefail |
||||||
|
cd "$(dirname "$0")/.." |
||||||
|
|
||||||
|
source tool/common.sh |
||||||
|
|
||||||
|
./tool/build-dev-container-image.sh |
||||||
|
container_id="$(docker run --rm -d -p "80:80" nextcloud-neon-dev)" |
||||||
|
function cleanup() { |
||||||
|
docker kill "$container_id" |
||||||
|
} |
||||||
|
trap cleanup EXIT |
||||||
|
|
||||||
|
( |
||||||
|
cd packages/neon |
||||||
|
fvm flutter drive \ |
||||||
|
--driver=test_driver/integration_test.dart \ |
||||||
|
--target=integration_test/screenshot_test.dart \ |
||||||
|
--android-emulator |
||||||
|
) |