mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-29 12:22:52 +03:00
Merge PR #886.
This commit is contained in:
commit
05ce987038
@ -61,7 +61,7 @@ Future<void> grantCameraPermissions(WidgetTester tester) async {
|
||||
var manualEntryBtn = find.byKey(android_keys.manualEntryButton).hitTestable();
|
||||
|
||||
if (manualEntryBtn.evaluate().isEmpty) {
|
||||
tester.testLog(false, 'Allow camera permission');
|
||||
tester.testLog(true, 'Allow camera permission');
|
||||
manualEntryBtn = await tester.waitForFinder(manualEntryBtn);
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,8 @@ void main() {
|
||||
var binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
||||
|
||||
group('OATH UI tests', () {
|
||||
appTest('Menu items exist', (WidgetTester tester) async {
|
||||
group('UI tests', () {
|
||||
appTest('OATH Menu items exist', (WidgetTester tester) async {
|
||||
await tester.tapDeviceButton();
|
||||
expect(find.byKey(keys.addAccountAction), findsOneWidget);
|
||||
expect(find.byKey(keys.setOrManagePasswordAction), findsOneWidget);
|
||||
@ -35,8 +35,8 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('OATH Account tests', () {
|
||||
appTest('Create account', (WidgetTester tester) async {
|
||||
group('Account tests', () {
|
||||
appTest('Create OATH account', (WidgetTester tester) async {
|
||||
// account with issuer
|
||||
var testAccount = const Account(
|
||||
issuer: 'IssuerForTests',
|
||||
@ -45,7 +45,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.deleteAccount(testAccount);
|
||||
await tester.addAccount(testAccount, quiet: false);
|
||||
await tester.addAccount(testAccount);
|
||||
|
||||
// account without issuer
|
||||
testAccount = const Account(
|
||||
@ -54,24 +54,24 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.deleteAccount(testAccount);
|
||||
await tester.addAccount(testAccount, quiet: false);
|
||||
await tester.addAccount(testAccount);
|
||||
});
|
||||
|
||||
/// deletes accounts created in previous test
|
||||
appTest('Delete account', (WidgetTester tester) async {
|
||||
appTest('Delete OATH account', (WidgetTester tester) async {
|
||||
var testAccount =
|
||||
const Account(issuer: 'IssuerForTests', name: 'NameForTests');
|
||||
|
||||
await tester.deleteAccount(testAccount, quiet: false);
|
||||
await tester.deleteAccount(testAccount);
|
||||
expect(await tester.findAccount(testAccount), isNull);
|
||||
|
||||
testAccount = const Account(issuer: null, name: 'NoIssuerName');
|
||||
await tester.deleteAccount(testAccount, quiet: false);
|
||||
await tester.deleteAccount(testAccount);
|
||||
expect(await tester.findAccount(testAccount), isNull);
|
||||
});
|
||||
|
||||
/// adds an account, renames, verifies
|
||||
appTest('Rename account', (WidgetTester tester) async {
|
||||
appTest('Rename OATH account', (WidgetTester tester) async {
|
||||
var testAccount =
|
||||
const Account(issuer: 'IssuerToRename', name: 'NameToRename');
|
||||
|
||||
@ -85,7 +85,7 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('OATH Password tests', () {
|
||||
group('Password tests', () {
|
||||
/// note that the password groups should be run as whole
|
||||
|
||||
/// TODO implement test for password replacement
|
||||
@ -94,27 +94,27 @@ void main() {
|
||||
/// });
|
||||
|
||||
// cannot restart the app on Android to be able to unlock
|
||||
group('OATH: remove oath password when unlocked', skip: isAndroid, () {
|
||||
group('Desktop password tests', skip: isAndroid, () {
|
||||
var testPassword = 'testPassword';
|
||||
|
||||
appTest('OATH: set oath password', (WidgetTester tester) async {
|
||||
appTest('Set OATH password', (WidgetTester tester) async {
|
||||
await tester.setOathPassword(testPassword);
|
||||
});
|
||||
|
||||
appTest('OATH: remove oath password', (WidgetTester tester) async {
|
||||
appTest('Remove OATH password', (WidgetTester tester) async {
|
||||
await tester.unlockOathSession(testPassword);
|
||||
await tester.removeOathPassword(testPassword);
|
||||
});
|
||||
});
|
||||
|
||||
group('OATH: remove oath password when locked', () {
|
||||
group('All password tests', () {
|
||||
var testPassword = 'testPasswordX';
|
||||
|
||||
appTest('OATH: set oath password', (WidgetTester tester) async {
|
||||
appTest('Set OATH password', (WidgetTester tester) async {
|
||||
await tester.setOathPassword(testPassword);
|
||||
});
|
||||
|
||||
appTest('OATH: remove oath password', (WidgetTester tester) async {
|
||||
appTest('Remove OATH password', (WidgetTester tester) async {
|
||||
await tester.removeOathPassword(testPassword);
|
||||
});
|
||||
});
|
||||
|
@ -111,6 +111,7 @@ extension OathFunctions on WidgetTester {
|
||||
return null;
|
||||
}
|
||||
|
||||
await shortWait();
|
||||
/// find an AccountView with issuer/name in the account list
|
||||
var matchingAccounts = find.descendant(
|
||||
of: findAccountList(),
|
||||
@ -174,6 +175,7 @@ extension OathFunctions on WidgetTester {
|
||||
expect(deleteButton, findsOneWidget);
|
||||
await tap(deleteButton);
|
||||
await longWait();
|
||||
await longWait();
|
||||
|
||||
/// try to find account
|
||||
var deletedAccountView = await findAccount(a);
|
||||
@ -202,6 +204,7 @@ extension OathFunctions on WidgetTester {
|
||||
/// TODO verify this is correct for the FW of the YubiKey
|
||||
if (renameIconButton.evaluate().isEmpty) {
|
||||
/// close the dialog and return
|
||||
testLog(false, 'This YubiKey does not support account renaming');
|
||||
await tapAt(const Offset(10, 10));
|
||||
await shortWait();
|
||||
return;
|
||||
@ -218,7 +221,7 @@ extension OathFunctions on WidgetTester {
|
||||
var nameTextField = find.byKey(keys.nameField).hitTestable();
|
||||
await tap(nameTextField);
|
||||
await enterText(nameTextField, newName);
|
||||
await shortestWait();
|
||||
await shortWait();
|
||||
|
||||
var saveButton = find.byKey(keys.saveButton).hitTestable();
|
||||
expect(saveButton, findsOneWidget);
|
||||
@ -300,7 +303,10 @@ extension OathFunctions on WidgetTester {
|
||||
await shortWait();
|
||||
var unlockButton = find.byKey(keys.unlockButton);
|
||||
await tap(unlockButton);
|
||||
await longWait();
|
||||
|
||||
/// TODO:
|
||||
/// the following pump is because of NEO keys
|
||||
await pump(const Duration(seconds: 1));
|
||||
|
||||
expect(find.byKey(keys.unlockButton).hitTestable(), findsNothing);
|
||||
}
|
||||
@ -315,7 +321,10 @@ extension OathFunctions on WidgetTester {
|
||||
await enterText(currentPasswordEntry, currentPassword);
|
||||
await shortWait();
|
||||
await tap(find.byKey(keys.removePasswordButton));
|
||||
await longWait();
|
||||
|
||||
/// TODO:
|
||||
/// the following pump is because of NEO keys
|
||||
await pump(const Duration(seconds: 1));
|
||||
|
||||
expect(find.byKey(keys.removePasswordButton).hitTestable(), findsNothing);
|
||||
}
|
||||
|
@ -27,10 +27,8 @@ import 'android/util.dart' as android_test_util;
|
||||
import 'approved_yubikeys.dart';
|
||||
import 'desktop/util.dart' as desktop_test_util;
|
||||
|
||||
const shortestWaitMs = 10;
|
||||
const shortWaitMs = 50;
|
||||
const longWaitMs = 200;
|
||||
const veryLongWaitS = 10; // seconds
|
||||
const shortWaitMs = 10;
|
||||
const longWaitMs = 50;
|
||||
|
||||
/// information about YubiKey as seen by the app
|
||||
String? yubiKeyName;
|
||||
@ -39,10 +37,6 @@ String? yubiKeySerialNumber;
|
||||
bool collectedYubiKeyInformation = false;
|
||||
|
||||
extension AppWidgetTester on WidgetTester {
|
||||
Future<void> shortestWait() async {
|
||||
await pump(const Duration(milliseconds: shortestWaitMs));
|
||||
}
|
||||
|
||||
Future<void> shortWait() async {
|
||||
await pump(const Duration(milliseconds: shortWaitMs));
|
||||
}
|
||||
@ -51,10 +45,6 @@ extension AppWidgetTester on WidgetTester {
|
||||
await pump(const Duration(milliseconds: longWaitMs));
|
||||
}
|
||||
|
||||
Future<void> veryLongWait() async {
|
||||
await pump(const Duration(seconds: veryLongWaitS));
|
||||
}
|
||||
|
||||
/// waits up to [timeOutSec] seconds evaluating whether [Finder] f is
|
||||
/// visible
|
||||
Future<Finder> waitForFinder(Finder f, [int timeOutSec = 20]) async {
|
||||
|
@ -57,9 +57,7 @@ Future<Widget> initialize() async {
|
||||
Application.oath,
|
||||
]),
|
||||
prefProvider.overrideWithValue(await SharedPreferences.getInstance()),
|
||||
logLevelProvider.overrideWith(
|
||||
(ref) => ref.watch(androidLogProvider.notifier),
|
||||
),
|
||||
logLevelProvider.overrideWith((ref) => AndroidLogger()),
|
||||
attachedDevicesProvider
|
||||
.overrideWith(
|
||||
() => AndroidAttachedDevicesNotifier(),
|
||||
@ -71,7 +69,7 @@ Future<Widget> initialize() async {
|
||||
credentialListProvider
|
||||
.overrideWithProvider(androidCredentialListProvider),
|
||||
currentAppProvider.overrideWith(
|
||||
(ref) => ref.watch(androidSubPageProvider.notifier),
|
||||
(ref) => AndroidSubPageNotifier(ref.watch(supportedAppsProvider))
|
||||
),
|
||||
managementStateProvider.overrideWithProvider(androidManagementState),
|
||||
currentDeviceProvider.overrideWith(
|
||||
@ -92,9 +90,6 @@ Future<Widget> initialize() async {
|
||||
child: DismissKeyboard(
|
||||
child: YubicoAuthenticatorApp(page: Consumer(
|
||||
builder: (context, ref, child) {
|
||||
// activates the sub page provider
|
||||
ref.read(androidSubPageProvider);
|
||||
|
||||
// activates window state provider
|
||||
ref.read(androidWindowStateProvider);
|
||||
|
||||
|
@ -15,15 +15,11 @@
|
||||
*/
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:yubico_authenticator/app/logging.dart';
|
||||
|
||||
final _log = Logger('android.logger');
|
||||
|
||||
final androidLogProvider =
|
||||
StateNotifierProvider<LogLevelNotifier, Level>((ref) => AndroidLogger());
|
||||
|
||||
class AndroidLogger extends LogLevelNotifier {
|
||||
final MethodChannel _channel = const MethodChannel('android.log.redirect');
|
||||
|
||||
|
@ -74,13 +74,8 @@ final androidSupportedThemesProvider = StateProvider<List<ThemeMode>>((ref) {
|
||||
}
|
||||
});
|
||||
|
||||
final androidSubPageProvider =
|
||||
StateNotifierProvider<CurrentAppNotifier, Application>((ref) {
|
||||
return _AndroidSubPageNotifier(ref.watch(supportedAppsProvider));
|
||||
});
|
||||
|
||||
class _AndroidSubPageNotifier extends CurrentAppNotifier {
|
||||
_AndroidSubPageNotifier(super.supportedApps) {
|
||||
class AndroidSubPageNotifier extends CurrentAppNotifier {
|
||||
AndroidSubPageNotifier(super.supportedApps) {
|
||||
_handleSubPage(state);
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,11 @@ class UsbDeviceNotifier extends StateNotifier<List<UsbYubiKeyNode>> {
|
||||
|
||||
try {
|
||||
var scan = await rpc.command('scan', ['usb']);
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final pids = {
|
||||
for (var e in (scan['pids'] as Map).entries)
|
||||
UsbPid.fromValue(int.parse(e.key)): e.value as int
|
||||
|
@ -125,7 +125,7 @@ class AccountDialog extends ConsumerWidget with AccountMixin {
|
||||
}
|
||||
|
||||
final code = getCode(ref);
|
||||
if (code == null) {
|
||||
if (isValid(ref) && code == null) {
|
||||
if (isDesktop ||
|
||||
(isAndroid &&
|
||||
currentDeviceData.value?.node.transport == Transport.usb)) {
|
||||
|
@ -50,6 +50,11 @@ mixin AccountMixin {
|
||||
@protected
|
||||
OathCode? getCode(WidgetRef ref) => ref.watch(codeProvider(credential));
|
||||
|
||||
@protected
|
||||
bool isValid(WidgetRef ref) =>
|
||||
ref.watch(credentialsProvider)?.any((c) => credential.id == c.id) ??
|
||||
false;
|
||||
|
||||
@protected
|
||||
String formatCode(OathCode? code) {
|
||||
final value = code?.value;
|
||||
|
@ -225,7 +225,7 @@ packages:
|
||||
name: flutter_riverpod
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.3"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -462,7 +462,7 @@ packages:
|
||||
name: riverpod
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.3"
|
||||
screen_retriever:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -45,7 +45,7 @@ dependencies:
|
||||
logging: ^1.1.0
|
||||
collection: ^1.16.0
|
||||
shared_preferences: ^2.0.12
|
||||
flutter_riverpod: ^2.1.1
|
||||
flutter_riverpod: ^2.1.3
|
||||
json_annotation: ^4.7.0
|
||||
freezed_annotation: ^2.2.0
|
||||
window_manager: ^0.2.7
|
||||
|
Loading…
Reference in New Issue
Block a user