Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 34 additions & 102 deletions packages/app/integration_test/screenshot_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,85 +9,11 @@ import 'package:integration_test/integration_test.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:neon/models.dart';
import 'package:neon/neon.dart';
import 'package:neon/nextcloud.dart';
import 'package:neon/settings.dart';
import 'package:neon_files/widgets/actions.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(final String key) async {
_data.remove(key);
return true;
}

@override
Set<String> getKeys() => _data.keys.toSet();

@override
bool containsKey(final String key) => _data.keys.contains(key);

@override
Object? get(final String key) => _data[key];

@override
bool? getBool(final String key) => _data[key] as bool?;

@override
double? getDouble(final String key) => _data[key] as double?;

@override
int? getInt(final String key) => _data[key] as int?;

@override
String? getString(final String key) => _data[key] as String?;

@override
List<String>? getStringList(final String key) => (_data[key] as List).cast<String>();

@override
Future<bool> setBool(final String key, final bool value) async {
_data[key] = value;
return true;
}

@override
Future<bool> setDouble(final String key, final double value) async {
_data[key] = value;
return true;
}

@override
Future<bool> setInt(final String key, final int value) async {
_data[key] = value;
return true;
}

@override
Future<bool> setString(final String key, final String value) async {
_data[key] = value;
return true;
}

@override
Future<bool> setStringList(final String key, final List<String> value) async {
_data[key] = value;
return true;
}
}

Future runTestApp(
final WidgetTester tester,
final IntegrationTestWidgetsFlutterBinding binding, {
Expand All @@ -97,7 +23,6 @@ Future runTestApp(
getAppImplementations: getAppImplementations,
theme: neonTheme,
bindingOverride: binding,
sharedPreferencesOverride: MemorySharedPreferences(),
account: account,
firstLaunchDisabled: true,
nextPushDisabled: true,
Expand All @@ -112,7 +37,7 @@ Future openDrawer(final WidgetTester tester) async {

Future switchPage(final WidgetTester tester, final String name) async {
await openDrawer(tester);
await tester.tap(find.byKey(Key(name)));
await tester.tap(find.text(name).last);
await tester.pumpAndSettle();
}

Expand All @@ -121,37 +46,48 @@ Future prepareScreenshot(final WidgetTester tester, final IntegrationTestWidgets
await tester.pumpAndSettle();
}

Future<Account> getAccount(final String username) async {
const host = 'http://10.0.2.2';
final appPassword = (await NextcloudClient(
host,
loginName: username,
password: username,
).core.appPassword.getAppPassword())
.ocs
.data
.apppassword;
return Account(
serverURL: host,
username: username,
password: appPassword,
);
}

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: 'user1',
password: 'user1',
);

late Account account;

setUpAll(() async {
account = await getAccount('demo');
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
});

setUp(() {
SharedPreferences.setMockInitialValues({});
});

testWidgets('login', (final tester) async {
await runTestApp(
tester,
binding,
);
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 {
Expand Down Expand Up @@ -222,7 +158,7 @@ Future main() async {
account: account,
);
await prepareScreenshot(tester, binding);
await switchPage(tester, 'app-news');
await switchPage(tester, 'News');

// Show folders
await tester.tap(find.byIcon(Icons.folder));
Expand Down Expand Up @@ -292,7 +228,7 @@ Future main() async {
account: account,
);
await prepareScreenshot(tester, binding);
await switchPage(tester, 'app-notes');
await switchPage(tester, 'Notes');

// Create note
await tester.tap(find.byType(FloatingActionButton));
Expand Down Expand Up @@ -343,11 +279,7 @@ Future main() async {
});

testWidgets('notifications', (final tester) async {
await Account(
serverURL: 'http://10.0.2.2',
username: 'admin',
password: 'admin',
).client.notifications.sendAdminNotification(
await (await getAccount('admin')).client.notifications.sendAdminNotification(
userId: account.username,
shortMessage: 'Notifications demo',
longMessage: 'This is a notifications demo of the Neon app',
Expand All @@ -359,7 +291,7 @@ Future main() async {
account: account,
);
await prepareScreenshot(tester, binding);
await tester.tap(find.byKey(const Key('app-notifications')));
await tester.tap(find.byTooltip('Notifications'));
await tester.pumpAndSettle();

await tester.pumpAndSettle();
Expand All @@ -375,7 +307,7 @@ Future main() async {
account: account,
);
await prepareScreenshot(tester, binding);
await switchPage(tester, 'settings');
await switchPage(tester, 'Settings');

// Open Files settings
await tester.tap(find.text('Files'));
Expand Down Expand Up @@ -426,13 +358,13 @@ Future main() async {
await tester.pumpAndSettle();

// Scroll down to accounts
await tester.drag(find.byType(ListView), const Offset(0, -10000));
await tester.drag(find.byType(SettingsList).first, const Offset(0, -10000));
await tester.pumpAndSettle();

await binding.takeScreenshot('settings_accounts');

// Go to account settings
await tester.tap(find.text('user1@10.0.2.2:80'));
await tester.tap(find.text('demo@10.0.2.2:80'));
await tester.pumpAndSettle();
await tester.tap(find.text('Automatic'));
await tester.pumpAndSettle();
Expand Down
Binary file modified packages/app/screenshots/files_actions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/files_create.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/files_details.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/files_photos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/files_root.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/home_drawer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed packages/app/screenshots/login_form.png
Binary file not shown.
Binary file modified packages/app/screenshots/login_server_selection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/news_articles_starred_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/news_articles_unread_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/news_feed_add.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/news_feed_articles_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/news_feeds_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/news_folders_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/notes_categories_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/notes_note_create.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/notes_note_edit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/notes_note_preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/notes_notes_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/notifications_list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/settings_account.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/settings_accounts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/settings_app_files.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/settings_app_news.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/settings_app_notes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/app/screenshots/settings_dark.png
Binary file modified packages/app/screenshots/settings_light.png
Binary file modified packages/app/screenshots/settings_oled.png
4 changes: 2 additions & 2 deletions packages/app/test_driver/integration_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:integration_test/integration_test_driver_extended.dart';

Future<void> main() async {
Expand All @@ -16,6 +15,7 @@ Future<void> main() async {
},
);
} catch (e) {
debugPrint('Error occurred: $e');
// ignore: avoid_print
print('Error occurred: $e');
}
}
22 changes: 7 additions & 15 deletions packages/neon/neon/lib/neon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/app.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/first_launch.dart';
Expand All @@ -14,27 +13,24 @@ import 'package:neon/src/platform/platform.dart';
import 'package:neon/src/theme/neon.dart';
import 'package:neon/src/utils/global_options.dart';
import 'package:neon/src/utils/request_manager.dart';
import 'package:neon/src/utils/user_agent.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

@internal
late final String neonUserAgent;

Future runNeon({
required final Iterable<AppImplementation> Function(SharedPreferences, RequestManager, NeonPlatform)
getAppImplementations,
required final NeonTheme theme,
final WidgetsBinding? bindingOverride,
final SharedPreferences? sharedPreferencesOverride,
final Account? account,
final bool firstLaunchDisabled = false,
final bool nextPushDisabled = false,
@visibleForTesting final WidgetsBinding? bindingOverride,
@visibleForTesting final Account? account,
@visibleForTesting final bool firstLaunchDisabled = false,
@visibleForTesting final bool nextPushDisabled = false,
}) async {
final binding = bindingOverride ?? WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: binding);

final sharedPreferences = sharedPreferencesOverride ?? await SharedPreferences.getInstance();
final sharedPreferences = await SharedPreferences.getInstance();

final platform = await getNeonPlatform();
final cache = Cache(platform);
Expand All @@ -43,11 +39,7 @@ Future runNeon({
final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform);

final packageInfo = await PackageInfo.fromPlatform();
var buildNumber = packageInfo.buildNumber;
if (buildNumber.isEmpty) {
buildNumber = '1';
}
neonUserAgent = 'Neon ${packageInfo.version}+$buildNumber';
buildUserAgent(packageInfo);

final globalOptions = GlobalOptions(
sharedPreferences,
Expand Down
1 change: 1 addition & 0 deletions packages/neon/neon/lib/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export 'package:neon/src/settings/models/options_category.dart';
export 'package:neon/src/settings/models/select_option.dart';
export 'package:neon/src/settings/models/storage.dart';
export 'package:neon/src/settings/models/toggle_option.dart';
export 'package:neon/src/settings/widgets/settings_list.dart';
2 changes: 1 addition & 1 deletion packages/neon/neon/lib/src/blocs/login_check_account.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:neon/neon.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/utils/user_agent.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:neon/neon.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/utils/user_agent.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';

Expand Down
2 changes: 1 addition & 1 deletion packages/neon/neon/lib/src/blocs/login_flow.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:neon/neon.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/utils/user_agent.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/settings/widgets/settings_category.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

@internal
@visibleForTesting
class SettingsList extends StatelessWidget {
const SettingsList({
required this.categories,
Expand Down
15 changes: 15 additions & 0 deletions packages/neon/neon/lib/src/utils/user_agent.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:meta/meta.dart';
import 'package:package_info_plus/package_info_plus.dart';

late String? _userAgent;

void buildUserAgent(final PackageInfo packageInfo) {
var buildNumber = packageInfo.buildNumber;
if (buildNumber.isEmpty) {
buildNumber = '1';
}
_userAgent = 'Neon ${packageInfo.version}+$buildNumber';
}

@internal
String get neonUserAgent => _userAgent!;
2 changes: 2 additions & 0 deletions tool/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ RUN ./occ app:disable password_policy

RUN OC_PASS="user1" ./occ user:add --password-from-env --display-name "User One" user1
RUN OC_PASS="user2" ./occ user:add --password-from-env --display-name "User Two" user2
RUN OC_PASS="demo" ./occ user:add --password-from-env --display-name "Demo" demo

RUN ./occ app:install news --force --allow-unstable # 21.2.0
RUN ./occ app:install notes --force --allow-unstable # 4.8.0
Expand All @@ -18,5 +19,6 @@ RUN ./occ app:install uppush --force --allow-unstable # 1.4.0
RUN ./occ app:enable password_policy
RUN (sh /entrypoint.sh php -S 0.0.0.0:8080 &) && \
until curl -s -o /dev/null http://localhost:8080/status.php; do true; done && \
# Do not setup the demo user here
for user in admin user1 user2; do curl -u "$user:$user" -H "ocs-apirequest: true" -s -o /dev/null http://localhost:8080/ocs/v2.php/cloud/user; done
COPY --chown=www-data:www-data overlay /usr/src/nextcloud/