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_test/flutter_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/oath_screen.dart';
|
||||
|
||||
@ -24,11 +23,11 @@ String randomPadded() {
|
||||
}
|
||||
|
||||
String generateRandomIssuer() {
|
||||
return 'i' + randomPadded();
|
||||
return 'i${randomPadded()}';
|
||||
}
|
||||
|
||||
String generateRandomName() {
|
||||
return 'n' + randomPadded();
|
||||
return 'n${randomPadded()}';
|
||||
}
|
||||
|
||||
String generateRandomSecret() {
|
||||
@ -40,111 +39,141 @@ void main() {
|
||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
||||
|
||||
group('OATH tests', () {
|
||||
/// For these tests there are defined Keys in manage_password_dialog.dart
|
||||
testWidgets('set password', (WidgetTester tester) async {
|
||||
group('OATH Options', () {
|
||||
/*
|
||||
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.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
||||
reason: 'No YubiKey connected');
|
||||
expect(find.byType(OathScreen), findsOneWidget);
|
||||
var firstPassword = 'aaa111';
|
||||
|
||||
await tester.tap(find.byType(FloatingActionButton));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
/// expect(find.byType(OathScreen), findsOneWidget); <<< I am not certain if this is needed.
|
||||
|
||||
await tester.tap(find.byIcon(Icons.tune));
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
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';
|
||||
|
||||
/// TODO: I don't understand why these Keys don't work as intended
|
||||
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.enterText(find.byKey(const Key('new oath password')), firstPassword);
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byKey(const Key('confirm oath password')), firstPassword);
|
||||
await tester.pump();
|
||||
|
||||
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(seconds: 3));
|
||||
await tester.pump(const Duration(milliseconds: 1000));
|
||||
});
|
||||
testWidgets('change password', (WidgetTester tester) async {
|
||||
testWidgets('OATH: verify firstPassword', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(await getAuthenticatorApp());
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
||||
reason: 'No YubiKey connected');
|
||||
expect(find.byType(OathScreen), findsOneWidget);
|
||||
var firstPassword = 'aaa111';
|
||||
|
||||
await tester.tap(find.byType(FloatingActionButton));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
await tester.enterText(find.byKey(const Key('oath password')), firstPassword);
|
||||
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.pump(const Duration(milliseconds: 300));
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
var current_password = 'aaa111';
|
||||
var second_password = 'bbb222';
|
||||
await tester.enterText(find.byKey(const Key('current oath password')), firstPassword);
|
||||
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.enterText(
|
||||
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.enterText(find.byKey(const Key('confirm oath password')), secondPassword);
|
||||
await tester.pump();
|
||||
|
||||
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(seconds: 3));
|
||||
await tester.pump(const Duration(milliseconds: 1000));
|
||||
});
|
||||
testWidgets('remove password', (WidgetTester tester) async {
|
||||
testWidgets('OATH: set thirdPassword', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(await getAuthenticatorApp());
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
||||
reason: 'No YubiKey connected');
|
||||
expect(find.byType(OathScreen), findsOneWidget);
|
||||
var secondPassword = 'bbb222';
|
||||
var thirdPassword = 'ccc333';
|
||||
|
||||
await tester.tap(find.byType(FloatingActionButton));
|
||||
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('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')), second_password);
|
||||
await tester.enterText(find.byKey(const Key('current oath password')), secondPassword);
|
||||
await tester.pump();
|
||||
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.tap(find.text('Remove password'));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
|
||||
/// TODO: verification of state here: restarting app and entering password
|
||||
await tester.pump(const Duration(seconds: 3));
|
||||
/// TODO: verification of state here: see that list of accounts is shown
|
||||
await tester.pump(const Duration(milliseconds: 1000));
|
||||
});
|
||||
});
|
||||
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.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) {
|
||||
await tester.tap(find.byType(FloatingActionButton));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
|
||||
await tester.tap(find.text('Add account'));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
await tester.tap(find.byKey(const Key('add oath account')));
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
var issuer = generateRandomIssuer();
|
||||
var name = generateRandomName();
|
||||
@ -153,53 +182,50 @@ void main() {
|
||||
/// this random fails: generateRandomSecret();
|
||||
|
||||
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.pump(const Duration(milliseconds: 5));
|
||||
await tester.pump(const Duration(milliseconds: 40));
|
||||
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.pump(const Duration(milliseconds: 300));
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(find.byType(OathScreen), findsOneWidget);
|
||||
|
||||
await tester.enterText(
|
||||
find.byKey(const Key('search_accounts')), issuer);
|
||||
await tester.enterText(find.byKey(const Key('search_accounts')), issuer);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(AccountList),
|
||||
matching: find.textContaining(issuer)),
|
||||
findsOneWidget);
|
||||
expect(find.descendant(of: find.byType(AccountList), matching: find.textContaining(issuer)), findsOneWidget);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
}
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 3000));
|
||||
/*
|
||||
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.pump(const Duration(milliseconds: 300));
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
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', () {
|
||||
testWidgets('first HOTP test', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(await getAuthenticatorApp());
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(find.byType(NoDeviceScreen), findsNothing,
|
||||
reason: 'No YubiKey connected');
|
||||
expect(find.byType(OathScreen), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_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 'test_util.dart';
|
||||
@ -23,11 +22,11 @@ String randomPadded() {
|
||||
}
|
||||
|
||||
String generateRandomIssuer() {
|
||||
return 'i' + randomPadded();
|
||||
return 'i${randomPadded()}';
|
||||
}
|
||||
|
||||
String generateRandomName() {
|
||||
return 'n' + randomPadded();
|
||||
return 'n${randomPadded()}';
|
||||
}
|
||||
|
||||
String generateRandomSecret() {
|
||||
@ -46,10 +45,6 @@ void main() {
|
||||
await tester.pumpWidget(await getAuthenticatorApp());
|
||||
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'
|
||||
/// 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
|
||||
@ -90,10 +85,6 @@ void main() {
|
||||
await tester.pumpWidget(await getAuthenticatorApp());
|
||||
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'
|
||||
/// 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
|
||||
@ -127,8 +118,7 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 30000));
|
||||
|
||||
/// The following should report the success, if there are no accounts.
|
||||
expect(find.byType(OathScreen), findsNothing,
|
||||
reason: 'FIDO successfully reset.');
|
||||
expect(find.byType(OathScreen), findsNothing, reason: 'FIDO successfully reset.');
|
||||
|
||||
await tester.pump(const Duration(seconds: 3));
|
||||
});
|
||||
|
@ -36,9 +36,8 @@ class OathScreen extends ConsumerWidget {
|
||||
title: const Text('Authenticator'),
|
||||
cause: error,
|
||||
),
|
||||
data: (oathState) => oathState.locked
|
||||
? _LockedView(devicePath, oathState)
|
||||
: _UnlockedView(devicePath, oathState),
|
||||
data: (oathState) =>
|
||||
oathState.locked ? _LockedView(devicePath, oathState) : _UnlockedView(devicePath, oathState),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -64,8 +63,7 @@ class _LockedView extends ConsumerWidget {
|
||||
action: (context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ManagePasswordDialog(devicePath, oathState),
|
||||
builder: (context) => ManagePasswordDialog(devicePath, oathState),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -124,8 +122,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isEmpty = ref.watch(credentialListProvider(widget.devicePath)
|
||||
.select((value) => value?.isEmpty == true));
|
||||
final isEmpty = ref.watch(credentialListProvider(widget.devicePath).select((value) => value?.isEmpty == true));
|
||||
if (isEmpty) {
|
||||
return MessagePage(
|
||||
title: const Text('Authenticator'),
|
||||
@ -137,8 +134,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
return Actions(
|
||||
actions: {
|
||||
SearchIntent: CallbackAction(onInvoke: (_) {
|
||||
searchController.selection = TextSelection(
|
||||
baseOffset: 0, extentOffset: searchController.text.length);
|
||||
searchController.selection = TextSelection(baseOffset: 0, extentOffset: searchController.text.length);
|
||||
searchFocus.requestFocus();
|
||||
return null;
|
||||
}),
|
||||
@ -191,6 +187,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
List<Widget> _buildActions(BuildContext context, bool isEmpty) {
|
||||
return [
|
||||
OutlinedButton.icon(
|
||||
key: const Key('add oath account'),
|
||||
style: isEmpty ? AppTheme.primaryOutlinedButtonStyle(context) : null,
|
||||
label: const Text('Add account'),
|
||||
icon: const Icon(Icons.person_add_alt_1),
|
||||
@ -211,14 +208,12 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
onPressed: () {
|
||||
showBottomMenu(context, [
|
||||
MenuAction(
|
||||
text:
|
||||
widget.oathState.hasKey ? 'Manage password' : 'Set password',
|
||||
text: widget.oathState.hasKey ? 'Manage password' : 'Set password',
|
||||
icon: const Icon(Icons.password),
|
||||
action: (context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ManagePasswordDialog(widget.devicePath, widget.oathState),
|
||||
builder: (context) => ManagePasswordDialog(widget.devicePath, widget.oathState),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -287,6 +282,7 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
TextField(
|
||||
key: const Key('oath password'),
|
||||
controller: _passwordController,
|
||||
autofocus: true,
|
||||
obscureText: _isObscure,
|
||||
@ -339,6 +335,7 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: ElevatedButton(
|
||||
key: const Key('oath unlock'),
|
||||
onPressed: _passwordController.text.isNotEmpty ? _submit : null,
|
||||
child: const Text('Unlock'),
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user