mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-28 20:06:56 +03:00
update OTP integration tests
This commit is contained in:
parent
b49ac0bb4e
commit
4f186bb2fc
@ -2,16 +2,33 @@
|
|||||||
tags:
|
tags:
|
||||||
# Tests which we want to run on desktop
|
# Tests which we want to run on desktop
|
||||||
desktop:
|
desktop:
|
||||||
|
timeout: none
|
||||||
|
|
||||||
# Tests which we want to run on Android
|
# Tests which we want to run on Android
|
||||||
android:
|
android:
|
||||||
|
timeout: none
|
||||||
|
|
||||||
# Tests consuming quiet a lot of time
|
# Tests consuming quiet a lot of time
|
||||||
slow:
|
slow:
|
||||||
|
timeout: none
|
||||||
|
|
||||||
# Minimal tests
|
# Minimal tests
|
||||||
# quick verification that the framework is working
|
# quick verification that the framework is working
|
||||||
minimal:
|
minimal:
|
||||||
|
timeout: none
|
||||||
|
|
||||||
# OATH tests
|
# OATH tests
|
||||||
oath:
|
oath:
|
||||||
|
timeout: none
|
||||||
|
|
||||||
|
# OTP tests
|
||||||
|
otp:
|
||||||
|
timeout: none
|
||||||
|
|
||||||
|
# PIV tests
|
||||||
|
piv:
|
||||||
|
timeout: none
|
||||||
|
|
||||||
|
# Management tests
|
||||||
|
management:
|
||||||
|
timeout: none
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Tags(['desktop', 'management'])
|
||||||
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';
|
||||||
|
@ -14,12 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Tags(['android', 'desktop', 'oath'])
|
@Tags(['desktop', 'otp'])
|
||||||
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/keys.dart';
|
import 'package:yubico_authenticator/app/views/keys.dart';
|
||||||
import 'package:yubico_authenticator/otp/keys.dart';
|
import 'package:yubico_authenticator/otp/keys.dart';
|
||||||
|
import 'package:yubico_authenticator/otp/models.dart';
|
||||||
|
|
||||||
|
import 'utils/otp_test_util.dart';
|
||||||
import 'utils/test_util.dart';
|
import 'utils/test_util.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -27,21 +29,12 @@ void main() {
|
|||||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
||||||
|
|
||||||
group('OTP UI tests', () {
|
group('OTP UI tests', () {
|
||||||
appTest('OTP menu items exist', (WidgetTester tester) async {
|
|
||||||
await tester.tap(find.byKey(otpAppDrawer));
|
|
||||||
await tester.shortWait();
|
|
||||||
|
|
||||||
await tester.tap(find.byKey(configureYubiOtp).hitTestable());
|
|
||||||
await tester.shortWait();
|
|
||||||
});
|
|
||||||
|
|
||||||
appTest('Yubico OTP slot 1', (WidgetTester tester) async {
|
appTest('Yubico OTP slot 1', (WidgetTester tester) async {
|
||||||
await tester.tap(find.byKey(otpAppDrawer).hitTestable());
|
await tester.tap(find.byKey(otpAppDrawer).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
|
|
||||||
//verify "Slot 1 is empty"
|
//verify "Slot 1 is empty"
|
||||||
|
await tester.openSlotMenu(SlotId.one);
|
||||||
// we are missing the right click on top of the correct slot
|
|
||||||
|
|
||||||
await tester.tap(find.byKey(configureYubiOtp).hitTestable());
|
await tester.tap(find.byKey(configureYubiOtp).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
@ -65,7 +58,7 @@ void main() {
|
|||||||
|
|
||||||
// verify "Slot 1 is configured"
|
// verify "Slot 1 is configured"
|
||||||
|
|
||||||
// we are missing the right click on top of the correct slot
|
await tester.openSlotMenu(SlotId.one);
|
||||||
|
|
||||||
await tester.tap(find.byKey(configureChalResp).hitTestable());
|
await tester.tap(find.byKey(configureChalResp).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
@ -85,9 +78,9 @@ void main() {
|
|||||||
|
|
||||||
// verify "Slot 2 is empty"
|
// verify "Slot 2 is empty"
|
||||||
|
|
||||||
// we are missing the right click on top of the correct slot
|
await tester.openSlotMenu(SlotId.two);
|
||||||
|
|
||||||
await tester.tap(find.byKey(configureYubiOtp).hitTestable());
|
await tester.tap(find.byKey(configureStatic).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
|
|
||||||
// this generates and saves static password
|
// this generates and saves static password
|
||||||
@ -105,9 +98,9 @@ void main() {
|
|||||||
|
|
||||||
// verify "Slot 2 is configured"
|
// verify "Slot 2 is configured"
|
||||||
|
|
||||||
// we are missing the right click on top of the correct slot
|
await tester.openSlotMenu(SlotId.two);
|
||||||
|
|
||||||
await tester.tap(find.byKey(configureYubiOtp).hitTestable());
|
await tester.tap(find.byKey(configureHotp).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
|
|
||||||
// this writes and saves oath secret
|
// this writes and saves oath secret
|
||||||
@ -127,17 +120,14 @@ void main() {
|
|||||||
// verify "Slot 2 is configured"
|
// verify "Slot 2 is configured"
|
||||||
|
|
||||||
// taps swap
|
// taps swap
|
||||||
await tester.tap(find.byKey(actionsIconButtonKey).hitTestable());
|
await tester.tapSwapSlotsButton();
|
||||||
await tester.shortWait();
|
await tester.tap(find.byKey(swapButton).hitTestable());
|
||||||
await tester.tap(find.byKey(swapSlots).hitTestable());
|
|
||||||
await tester.shortWait();
|
|
||||||
await tester.tap(find.byKey(swap).hitTestable());
|
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
|
|
||||||
// verify "Slot 1 is configured"
|
// verify "Slot 1 is configured"
|
||||||
// verify "Slot 2 is configured"
|
// verify "Slot 2 is configured"
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
appTest('Delete Credentials', (WidgetTester tester) async {
|
appTest('Delete Credentials', (WidgetTester tester) async {
|
||||||
await tester.tap(find.byKey(otpAppDrawer).hitTestable());
|
await tester.tap(find.byKey(otpAppDrawer).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
@ -145,15 +135,24 @@ void main() {
|
|||||||
// verify "Slot 1 is configured"
|
// verify "Slot 1 is configured"
|
||||||
// verify "Slot 2 is configured"
|
// verify "Slot 2 is configured"
|
||||||
|
|
||||||
// we need to right click on slot 1
|
await tester.openSlotMenu(SlotId.one);
|
||||||
await tester.tap(find.byKey(deleteAction).hitTestable());
|
await tester.tap(find.byKey(deleteAction).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.longWait();
|
||||||
await tester.tap(find.byKey(deleteButton).hitTestable());
|
await tester.tap(find.byKey(deleteButton).hitTestable());
|
||||||
await tester.shortWait();
|
|
||||||
|
// wait for any toasts to be gone
|
||||||
|
await tester.pump(const Duration(seconds: 3));
|
||||||
|
var closeFinder = find.byKey(closeButton);
|
||||||
|
if (closeFinder.evaluate().isNotEmpty) {
|
||||||
|
// close the view
|
||||||
|
await tester.tap(closeFinder);
|
||||||
|
await tester.shortWait();
|
||||||
|
}
|
||||||
|
|
||||||
// we need to right click on slot 2
|
// we need to right click on slot 2
|
||||||
|
await tester.openSlotMenu(SlotId.two);
|
||||||
await tester.tap(find.byKey(deleteAction).hitTestable());
|
await tester.tap(find.byKey(deleteAction).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.longWait();
|
||||||
await tester.tap(find.byKey(deleteButton).hitTestable());
|
await tester.tap(find.byKey(deleteButton).hitTestable());
|
||||||
await tester.shortWait();
|
await tester.shortWait();
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Tags(['desktop', 'piv'])
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.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';
|
||||||
|
49
integration_test/utils/otp_test_util.dart
Normal file
49
integration_test/utils/otp_test_util.dart
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Yubico.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yubico_authenticator/app/views/keys.dart';
|
||||||
|
import 'package:yubico_authenticator/otp/keys.dart';
|
||||||
|
import 'package:yubico_authenticator/otp/models.dart';
|
||||||
|
|
||||||
|
import 'test_util.dart';
|
||||||
|
|
||||||
|
extension OathFunctions on WidgetTester {
|
||||||
|
/// Opens the menu of specific OTP Slot, either by tapping the button or
|
||||||
|
/// by tapping the list item
|
||||||
|
Future<void> openSlotMenu(SlotId slotId) async {
|
||||||
|
final menuButtonFinder = find.byKey(getOpenMenuButtonKey(slotId));
|
||||||
|
if (menuButtonFinder.evaluate().isNotEmpty) {
|
||||||
|
await tap(menuButtonFinder);
|
||||||
|
} else {
|
||||||
|
await tap(find.byKey(getAppListItemKey(slotId)));
|
||||||
|
}
|
||||||
|
await longWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// tap the Swap Slots button - either first open the action menu,
|
||||||
|
/// or try to find the button on visible screen
|
||||||
|
Future<void> tapSwapSlotsButton() async {
|
||||||
|
final actionButtonFinder = find.byKey(actionsIconButtonKey);
|
||||||
|
if (actionButtonFinder.evaluate().isNotEmpty) {
|
||||||
|
await tap(actionButtonFinder);
|
||||||
|
await shortWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
await tap(find.byKey(swapSlots).hitTestable());
|
||||||
|
await longWait();
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'models.dart';
|
||||||
|
|
||||||
const _prefix = 'otp.keys';
|
const _prefix = 'otp.keys';
|
||||||
const _keyAction = '$_prefix.actions';
|
const _keyAction = '$_prefix.actions';
|
||||||
const _slotAction = '$_prefix.slot.actions';
|
const _slotAction = '$_prefix.slot.actions';
|
||||||
@ -34,7 +36,7 @@ const deleteAction = Key('$_slotAction.delete');
|
|||||||
|
|
||||||
const saveButton = Key('$_prefix.save');
|
const saveButton = Key('$_prefix.save');
|
||||||
const deleteButton = Key('$_prefix.delete');
|
const deleteButton = Key('$_prefix.delete');
|
||||||
const swap = Key('$_prefix.swap');
|
const swapButton = Key('$_prefix.swap');
|
||||||
|
|
||||||
const secretField = Key('$_prefix.secret');
|
const secretField = Key('$_prefix.secret');
|
||||||
const publicIdField = Key('$_prefix.public_id');
|
const publicIdField = Key('$_prefix.public_id');
|
||||||
@ -43,3 +45,9 @@ const privateIdField = Key('$_prefix.private_id');
|
|||||||
const useSerial = Key('$_prefix.use_serial');
|
const useSerial = Key('$_prefix.use_serial');
|
||||||
const generatePrivateId = Key('$_prefix.generate_private_id');
|
const generatePrivateId = Key('$_prefix.generate_private_id');
|
||||||
const generateSecretKey = Key('$_prefix.generate_secret_key');
|
const generateSecretKey = Key('$_prefix.generate_secret_key');
|
||||||
|
|
||||||
|
Key getOpenMenuButtonKey(SlotId slotId) =>
|
||||||
|
Key('$_prefix.open_slot_menu_slot_${slotId.name}');
|
||||||
|
|
||||||
|
Key getAppListItemKey(SlotId slotId) =>
|
||||||
|
Key('$_prefix.app_list_item_slot_${slotId.name}');
|
||||||
|
@ -32,6 +32,7 @@ import '../../core/state.dart';
|
|||||||
import '../../management/models.dart';
|
import '../../management/models.dart';
|
||||||
import '../../widgets/list_title.dart';
|
import '../../widgets/list_title.dart';
|
||||||
import '../features.dart' as features;
|
import '../features.dart' as features;
|
||||||
|
import '../keys.dart';
|
||||||
import '../models.dart';
|
import '../models.dart';
|
||||||
import '../state.dart';
|
import '../state.dart';
|
||||||
import 'actions.dart';
|
import 'actions.dart';
|
||||||
@ -197,6 +198,7 @@ class _SlotListItem extends ConsumerWidget {
|
|||||||
final hasFeature = ref.watch(featureProvider);
|
final hasFeature = ref.watch(featureProvider);
|
||||||
|
|
||||||
return AppListItem(
|
return AppListItem(
|
||||||
|
key: getAppListItemKey(slot),
|
||||||
otpSlot,
|
otpSlot,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
@ -209,6 +211,7 @@ class _SlotListItem extends ConsumerWidget {
|
|||||||
trailing: expanded
|
trailing: expanded
|
||||||
? null
|
? null
|
||||||
: OutlinedButton(
|
: OutlinedButton(
|
||||||
|
key: getOpenMenuButtonKey(slot),
|
||||||
onPressed: Actions.handler(context, OpenIntent(otpSlot)),
|
onPressed: Actions.handler(context, OpenIntent(otpSlot)),
|
||||||
child: const Icon(Icons.more_horiz),
|
child: const Icon(Icons.more_horiz),
|
||||||
),
|
),
|
||||||
|
@ -36,7 +36,7 @@ class SwapSlotsDialog extends ConsumerWidget {
|
|||||||
title: Text(l10n.s_swap_slots),
|
title: Text(l10n.s_swap_slots),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
key: swap,
|
key: swapButton,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await ref.read(otpStateProvider(devicePath).notifier).swapSlots();
|
await ref.read(otpStateProvider(devicePath).notifier).swapSlots();
|
||||||
await ref.read(withContextProvider)((context) async {
|
await ref.read(withContextProvider)((context) async {
|
||||||
|
Loading…
Reference in New Issue
Block a user