mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-26 10:33:15 +03:00
added all tests relating to OATH options, and a first TOTP test (max
accounts)
This commit is contained in:
parent
b29917de8c
commit
19bb535fda
@ -4,7 +4,6 @@ import 'dart:math';
|
|||||||
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:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:yubico_authenticator/app/views/no_device_screen.dart';
|
|
||||||
import 'package:yubico_authenticator/oath/views/account_list.dart';
|
import 'package:yubico_authenticator/oath/views/account_list.dart';
|
||||||
import 'package:yubico_authenticator/oath/views/oath_screen.dart';
|
import 'package:yubico_authenticator/oath/views/oath_screen.dart';
|
||||||
|
|
||||||
@ -24,11 +23,11 @@ String randomPadded() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String generateRandomIssuer() {
|
String generateRandomIssuer() {
|
||||||
return 'i' + randomPadded();
|
return 'i${randomPadded()}';
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateRandomName() {
|
String generateRandomName() {
|
||||||
return 'n' + randomPadded();
|
return 'n${randomPadded()}';
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateRandomSecret() {
|
String generateRandomSecret() {
|
||||||
@ -40,111 +39,141 @@ void main() {
|
|||||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
||||||
|
|
||||||
group('OATH tests', () {
|
group('OATH Options', () {
|
||||||
/// For these tests there are defined Keys in manage_password_dialog.dart
|
/*
|
||||||
testWidgets('set password', (WidgetTester tester) async {
|
These tests verify that all oath options are verified to function correctly by:
|
||||||
|
1. setting firsPassword and verifying it
|
||||||
|
2. logging in and changing to secondPassword and verifying it
|
||||||
|
3. changing to thirdPassword
|
||||||
|
4. removing thirdPassword
|
||||||
|
*/
|
||||||
|
testWidgets('OATH: set firstPassword', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(await getAuthenticatorApp());
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
var firstPassword = 'aaa111';
|
||||||
reason: 'No YubiKey connected');
|
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
|
||||||
|
|
||||||
await tester.tap(find.byType(FloatingActionButton));
|
/// expect(find.byType(OathScreen), findsOneWidget); <<< I am not certain if this is needed.
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
|
||||||
|
await tester.tap(find.byIcon(Icons.tune));
|
||||||
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
await tester.tap(find.text('Set password'));
|
await tester.tap(find.text('Set password'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
var first_password = 'aaa111';
|
await tester.enterText(find.byKey(const Key('new oath password')), firstPassword);
|
||||||
|
await tester.pump();
|
||||||
/// TODO: I don't understand why these Keys don't work as intended
|
await tester.enterText(find.byKey(const Key('confirm oath password')), firstPassword);
|
||||||
await tester.enterText(
|
|
||||||
find.byKey(const Key('new oath password')), first_password);
|
|
||||||
await tester.enterText(
|
|
||||||
find.byKey(const Key('confirm oath password')), first_password);
|
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
await tester.tap(find.text('Save'));
|
await tester.tap(find.text('Save'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
/// TODO: verification of state here: restarting app and entering password
|
await tester.pump(const Duration(milliseconds: 1000));
|
||||||
await tester.pump(const Duration(seconds: 3));
|
|
||||||
});
|
});
|
||||||
testWidgets('change password', (WidgetTester tester) async {
|
testWidgets('OATH: verify firstPassword', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(await getAuthenticatorApp());
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
var firstPassword = 'aaa111';
|
||||||
reason: 'No YubiKey connected');
|
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
|
||||||
|
|
||||||
await tester.tap(find.byType(FloatingActionButton));
|
await tester.enterText(find.byKey(const Key('oath password')), firstPassword);
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump();
|
||||||
|
|
||||||
|
/// TODO: verification of state here: see that list of accounts is shown
|
||||||
|
await tester.pump(const Duration(milliseconds: 1000));
|
||||||
|
});
|
||||||
|
testWidgets('OATH: set secondPassword', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
|
var firstPassword = 'aaa111';
|
||||||
|
var secondPassword = 'bbb222';
|
||||||
|
|
||||||
|
await tester.enterText(find.byKey(const Key('oath password')), firstPassword);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.tap(find.byKey(const Key('oath unlock')));
|
||||||
|
|
||||||
|
await tester.tap(find.byIcon(Icons.tune));
|
||||||
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
await tester.tap(find.text('Manage password'));
|
await tester.tap(find.text('Manage password'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
var current_password = 'aaa111';
|
await tester.enterText(find.byKey(const Key('current oath password')), firstPassword);
|
||||||
var second_password = 'bbb222';
|
await tester.pump();
|
||||||
|
await tester.enterText(find.byKey(const Key('new oath password')), secondPassword);
|
||||||
|
|
||||||
/// TODO: I don't understand why these Keys don't work as intended
|
await tester.pump();
|
||||||
await tester.enterText(
|
await tester.enterText(find.byKey(const Key('confirm oath password')), secondPassword);
|
||||||
find.byKey(const Key('current oath password')), current_password);
|
|
||||||
await tester.enterText(
|
|
||||||
find.byKey(const Key('new oath password')), second_password);
|
|
||||||
await tester.enterText(
|
|
||||||
find.byKey(const Key('confirm oath password')), second_password);
|
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
await tester.tap(find.text('Save'));
|
await tester.tap(find.text('Save'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
/// TODO: verification of state here: restarting app and entering password
|
await tester.pump(const Duration(milliseconds: 1000));
|
||||||
await tester.pump(const Duration(seconds: 3));
|
|
||||||
});
|
});
|
||||||
testWidgets('remove password', (WidgetTester tester) async {
|
testWidgets('OATH: set thirdPassword', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(await getAuthenticatorApp());
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
var secondPassword = 'bbb222';
|
||||||
reason: 'No YubiKey connected');
|
var thirdPassword = 'ccc333';
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
|
||||||
|
|
||||||
await tester.tap(find.byType(FloatingActionButton));
|
await tester.tap(find.byIcon(Icons.tune));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
await tester.tap(find.text('Manage password'));
|
await tester.tap(find.text('Manage password'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
var second_password = 'bbb222';
|
await tester.enterText(find.byKey(const Key('current oath password')), secondPassword);
|
||||||
await tester.enterText(
|
await tester.pump();
|
||||||
find.byKey(const Key('current oath password')), second_password);
|
await tester.enterText(find.byKey(const Key('new oath password')), thirdPassword);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.enterText(find.byKey(const Key('confirm oath password')), thirdPassword);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Save'));
|
||||||
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 1000));
|
||||||
|
|
||||||
|
/// TODO: verification of state here: see that list of accounts is shown
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('OATH: remove thirdPassword', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
|
var thirdPassword = 'ccc333';
|
||||||
|
|
||||||
|
await tester.tap(find.byIcon(Icons.tune));
|
||||||
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
|
await tester.tap(find.text('Manage password'));
|
||||||
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
|
await tester.enterText(find.byKey(const Key('current oath password')), thirdPassword);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
await tester.tap(find.text('Remove password'));
|
await tester.tap(find.text('Remove password'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
|
||||||
|
|
||||||
/// TODO: verification of state here: restarting app and entering password
|
/// TODO: verification of state here: see that list of accounts is shown
|
||||||
await tester.pump(const Duration(seconds: 3));
|
await tester.pump(const Duration(milliseconds: 1000));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('TOTP tests', () {
|
group('TOTP tests', () {
|
||||||
testWidgets('Add 32 TOTP accounts and reset oath',
|
/*
|
||||||
(WidgetTester tester) async {
|
Tests will verify all TOTP functionality, not yet though:
|
||||||
|
1. Add 32 TOTP accounts
|
||||||
|
*/
|
||||||
|
testWidgets('TOTP: Add 32 accounts', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(await getAuthenticatorApp());
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
|
||||||
reason: 'No YubiKey connected');
|
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
|
||||||
|
|
||||||
for (var i = 0; i < 32; i += 1) {
|
for (var i = 0; i < 32; i += 1) {
|
||||||
await tester.tap(find.byType(FloatingActionButton));
|
await tester.tap(find.byKey(const Key('add oath account')));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
await tester.tap(find.text('Add account'));
|
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
|
||||||
|
|
||||||
var issuer = generateRandomIssuer();
|
var issuer = generateRandomIssuer();
|
||||||
var name = generateRandomName();
|
var name = generateRandomName();
|
||||||
@ -153,53 +182,50 @@ void main() {
|
|||||||
/// this random fails: generateRandomSecret();
|
/// this random fails: generateRandomSecret();
|
||||||
|
|
||||||
await tester.enterText(find.byKey(const Key('issuer')), issuer);
|
await tester.enterText(find.byKey(const Key('issuer')), issuer);
|
||||||
await tester.pump(const Duration(milliseconds: 5));
|
await tester.pump(const Duration(milliseconds: 40));
|
||||||
await tester.enterText(find.byKey(const Key('name')), name);
|
await tester.enterText(find.byKey(const Key('name')), name);
|
||||||
await tester.pump(const Duration(milliseconds: 5));
|
await tester.pump(const Duration(milliseconds: 40));
|
||||||
await tester.enterText(find.byKey(const Key('secret')), secret);
|
await tester.enterText(find.byKey(const Key('secret')), secret);
|
||||||
|
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
await tester.tap(find.byKey(const Key('save_btn')));
|
await tester.tap(find.byKey(const Key('save_btn')));
|
||||||
|
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
expect(find.byType(OathScreen), findsOneWidget);
|
||||||
|
|
||||||
await tester.enterText(
|
await tester.enterText(find.byKey(const Key('search_accounts')), issuer);
|
||||||
find.byKey(const Key('search_accounts')), issuer);
|
|
||||||
|
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
|
||||||
expect(
|
expect(find.descendant(of: find.byType(AccountList), matching: find.textContaining(issuer)), findsOneWidget);
|
||||||
find.descendant(
|
|
||||||
of: find.byType(AccountList),
|
|
||||||
matching: find.textContaining(issuer)),
|
|
||||||
findsOneWidget);
|
|
||||||
|
|
||||||
await tester.pump(const Duration(milliseconds: 50));
|
await tester.pump(const Duration(milliseconds: 50));
|
||||||
}
|
}
|
||||||
|
await tester.pump(const Duration(milliseconds: 3000));
|
||||||
|
/*
|
||||||
await tester.tap(find.byType(FloatingActionButton));
|
await tester.tap(find.byType(FloatingActionButton));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
await tester.tap(find.text('Reset OATH'));
|
await tester.tap(find.text('Reset OATH'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
await tester.tap(find.text('Reset'));
|
await tester.tap(find.text('Reset'));
|
||||||
await tester.pump(const Duration(milliseconds: 300));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
await tester.pump(const Duration(seconds: 3));
|
*/
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
/*
|
||||||
group('HOTP tests', () {
|
group('HOTP tests', () {
|
||||||
testWidgets('first HOTP test', (WidgetTester tester) async {
|
testWidgets('first HOTP test', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(await getAuthenticatorApp());
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
|
||||||
reason: 'No YubiKey connected');
|
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
expect(find.byType(OathScreen), findsOneWidget);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import 'dart:math';
|
|||||||
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:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:yubico_authenticator/app/views/no_device_screen.dart';
|
|
||||||
import 'package:yubico_authenticator/oath/views/oath_screen.dart';
|
import 'package:yubico_authenticator/oath/views/oath_screen.dart';
|
||||||
|
|
||||||
import 'test_util.dart';
|
import 'test_util.dart';
|
||||||
@ -23,11 +22,11 @@ String randomPadded() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String generateRandomIssuer() {
|
String generateRandomIssuer() {
|
||||||
return 'i' + randomPadded();
|
return 'i${randomPadded()}';
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateRandomName() {
|
String generateRandomName() {
|
||||||
return 'n' + randomPadded();
|
return 'n${randomPadded()}';
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateRandomSecret() {
|
String generateRandomSecret() {
|
||||||
@ -46,10 +45,6 @@ void main() {
|
|||||||
await tester.pumpWidget(await getAuthenticatorApp());
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
|
||||||
reason: 'No YubiKey connected');
|
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
|
||||||
|
|
||||||
/// QUESTION: I want to click the DrawerItem named 'WebAuthn' | 'Authenticator'
|
/// QUESTION: I want to click the DrawerItem named 'WebAuthn' | 'Authenticator'
|
||||||
/// await tester.tap(find.byType(DrawerItem.titleText == 'WebAuthn'));
|
/// await tester.tap(find.byType(DrawerItem.titleText == 'WebAuthn'));
|
||||||
/// which can be found in main_drawer.dart, how do I make sure I call the right
|
/// which can be found in main_drawer.dart, how do I make sure I call the right
|
||||||
@ -90,10 +85,6 @@ void main() {
|
|||||||
await tester.pumpWidget(await getAuthenticatorApp());
|
await tester.pumpWidget(await getAuthenticatorApp());
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
|
||||||
reason: 'No YubiKey connected');
|
|
||||||
expect(find.byType(OathScreen), findsOneWidget);
|
|
||||||
|
|
||||||
/// QUESTION: I want to click the DrawerItem named 'WebAuthn' | 'Authenticator'
|
/// QUESTION: I want to click the DrawerItem named 'WebAuthn' | 'Authenticator'
|
||||||
/// await tester.tap(find.byType(DrawerItem.titleText == 'WebAuthn'));
|
/// await tester.tap(find.byType(DrawerItem.titleText == 'WebAuthn'));
|
||||||
/// which can be found in main_drawer.dart, how do I make sure I call the right
|
/// which can be found in main_drawer.dart, how do I make sure I call the right
|
||||||
@ -127,8 +118,7 @@ void main() {
|
|||||||
await tester.pump(const Duration(milliseconds: 30000));
|
await tester.pump(const Duration(milliseconds: 30000));
|
||||||
|
|
||||||
/// The following should report the success, if there are no accounts.
|
/// The following should report the success, if there are no accounts.
|
||||||
expect(find.byType(OathScreen), findsNothing,
|
expect(find.byType(OathScreen), findsNothing, reason: 'FIDO successfully reset.');
|
||||||
reason: 'FIDO successfully reset.');
|
|
||||||
|
|
||||||
await tester.pump(const Duration(seconds: 3));
|
await tester.pump(const Duration(seconds: 3));
|
||||||
});
|
});
|
||||||
|
@ -36,9 +36,8 @@ class OathScreen extends ConsumerWidget {
|
|||||||
title: const Text('Authenticator'),
|
title: const Text('Authenticator'),
|
||||||
cause: error,
|
cause: error,
|
||||||
),
|
),
|
||||||
data: (oathState) => oathState.locked
|
data: (oathState) =>
|
||||||
? _LockedView(devicePath, oathState)
|
oathState.locked ? _LockedView(devicePath, oathState) : _UnlockedView(devicePath, oathState),
|
||||||
: _UnlockedView(devicePath, oathState),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,8 +63,7 @@ class _LockedView extends ConsumerWidget {
|
|||||||
action: (context) {
|
action: (context) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) =>
|
builder: (context) => ManagePasswordDialog(devicePath, oathState),
|
||||||
ManagePasswordDialog(devicePath, oathState),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -124,8 +122,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isEmpty = ref.watch(credentialListProvider(widget.devicePath)
|
final isEmpty = ref.watch(credentialListProvider(widget.devicePath).select((value) => value?.isEmpty == true));
|
||||||
.select((value) => value?.isEmpty == true));
|
|
||||||
if (isEmpty) {
|
if (isEmpty) {
|
||||||
return MessagePage(
|
return MessagePage(
|
||||||
title: const Text('Authenticator'),
|
title: const Text('Authenticator'),
|
||||||
@ -137,8 +134,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
|||||||
return Actions(
|
return Actions(
|
||||||
actions: {
|
actions: {
|
||||||
SearchIntent: CallbackAction(onInvoke: (_) {
|
SearchIntent: CallbackAction(onInvoke: (_) {
|
||||||
searchController.selection = TextSelection(
|
searchController.selection = TextSelection(baseOffset: 0, extentOffset: searchController.text.length);
|
||||||
baseOffset: 0, extentOffset: searchController.text.length);
|
|
||||||
searchFocus.requestFocus();
|
searchFocus.requestFocus();
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
@ -191,6 +187,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
|||||||
List<Widget> _buildActions(BuildContext context, bool isEmpty) {
|
List<Widget> _buildActions(BuildContext context, bool isEmpty) {
|
||||||
return [
|
return [
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
|
key: const Key('add oath account'),
|
||||||
style: isEmpty ? AppTheme.primaryOutlinedButtonStyle(context) : null,
|
style: isEmpty ? AppTheme.primaryOutlinedButtonStyle(context) : null,
|
||||||
label: const Text('Add account'),
|
label: const Text('Add account'),
|
||||||
icon: const Icon(Icons.person_add_alt_1),
|
icon: const Icon(Icons.person_add_alt_1),
|
||||||
@ -211,14 +208,12 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
showBottomMenu(context, [
|
showBottomMenu(context, [
|
||||||
MenuAction(
|
MenuAction(
|
||||||
text:
|
text: widget.oathState.hasKey ? 'Manage password' : 'Set password',
|
||||||
widget.oathState.hasKey ? 'Manage password' : 'Set password',
|
|
||||||
icon: const Icon(Icons.password),
|
icon: const Icon(Icons.password),
|
||||||
action: (context) {
|
action: (context) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) =>
|
builder: (context) => ManagePasswordDialog(widget.devicePath, widget.oathState),
|
||||||
ManagePasswordDialog(widget.devicePath, widget.oathState),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -287,6 +282,7 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16.0),
|
const SizedBox(height: 16.0),
|
||||||
TextField(
|
TextField(
|
||||||
|
key: const Key('oath password'),
|
||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
obscureText: _isObscure,
|
obscureText: _isObscure,
|
||||||
@ -339,6 +335,7 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
|
key: const Key('oath unlock'),
|
||||||
onPressed: _passwordController.text.isNotEmpty ? _submit : null,
|
onPressed: _passwordController.text.isNotEmpty ? _submit : null,
|
||||||
child: const Text('Unlock'),
|
child: const Text('Unlock'),
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user