Skip to content

Commit

Permalink
Merge branch 'master' into 331-backup-for-non-privacyidea-token
Browse files Browse the repository at this point in the history
  • Loading branch information
frankmer committed Jun 25, 2024
2 parents 60026ff + cb848d2 commit 0296f0d
Show file tree
Hide file tree
Showing 49 changed files with 1,025 additions and 834 deletions.
47 changes: 22 additions & 25 deletions integration_test/add_tokens_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import 'package:privacyidea_authenticator/model/enums/introduction.dart';
import 'package:privacyidea_authenticator/model/enums/token_types.dart';
import 'package:privacyidea_authenticator/model/states/introduction_state.dart';
import 'package:privacyidea_authenticator/model/states/settings_state.dart';
import 'package:privacyidea_authenticator/model/tokens/token.dart';
import 'package:privacyidea_authenticator/model/version.dart';
import 'package:privacyidea_authenticator/state_notifiers/completed_introduction_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart';
Expand Down Expand Up @@ -37,18 +39,29 @@ void main() {
late final MockIntroductionRepository mockIntroductionRepository;
setUp(() {
mockSettingsRepository = MockSettingsRepository();
when(mockSettingsRepository.loadSettings()).thenAnswer((_) async => SettingsState(useSystemLocale: false, localePreference: const Locale('en')));
when(mockSettingsRepository.loadSettings())
.thenAnswer((_) async => SettingsState(useSystemLocale: false, localePreference: const Locale('en'), latestVersion: Version.parse('999.999.999')));
when(mockSettingsRepository.saveSettings(any)).thenAnswer((_) async => true);
mockTokenRepository = MockTokenRepository();
when(mockTokenRepository.loadTokens()).thenAnswer((_) async => []);
when(mockTokenRepository.saveOrReplaceTokens(any)).thenAnswer((_) async => []);
when(mockTokenRepository.deleteTokens(any)).thenAnswer((_) async => []);
var tokens = <Token>[];
when(mockTokenRepository.loadTokens()).thenAnswer((_) async => tokens);
when(mockTokenRepository.saveOrReplaceToken(any)).thenAnswer((invocation) async {
final arguments = invocation.positionalArguments;
tokens.removeWhere((element) => element.id == (arguments[0] as Token).id);
tokens.add(arguments[0] as Token);
return true;
});
when(mockTokenRepository.deleteToken(any)).thenAnswer((invocation) async {
final arguments = invocation.positionalArguments;
tokens.removeWhere((element) => element.id == (arguments[0] as Token).id);
return true;
});
mockTokenFolderRepository = MockTokenFolderRepository();
when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []);
when(mockTokenFolderRepository.saveReplaceList(any)).thenAnswer((_) async => true);
mockIntroductionRepository = MockIntroductionRepository();
final introductions = {...Introduction.values}..remove(Introduction.introductionScreen);
when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions));
when(mockIntroductionRepository.loadCompletedIntroductions())
.thenAnswer((_) async => const IntroductionState(completedIntroductions: {...Introduction.values}));
});
testWidgets(
'Add Tokens Test',
Expand All @@ -62,9 +75,7 @@ void main() {
],
child: PrivacyIDEAAuthenticator(ApplicationCustomization.defaultCustomization),
));

await _introToMainView(tester);
expectMainViewIsEmptyAndCorrect();
await expectMainViewIsEmptyAndCorrect(tester);
await _addHotpToken(tester);
expect(find.byType(HOTPTokenWidget), findsOneWidget);
await _addTotpToken(tester);
Expand All @@ -89,21 +100,6 @@ void main() {
);
}

Future<void> _introToMainView(WidgetTester tester) async {
var finder = find.byType(FloatingActionButton);
await pumpUntilFindNWidgets(tester, finder, 1, const Duration(seconds: 20));
await tester.tap(finder);
await tester.pump(const Duration(milliseconds: 2000));
await tester.tap(finder);
await tester.pump(const Duration(milliseconds: 2000));
await tester.tap(finder);
await tester.pump(const Duration(milliseconds: 2000));
finder = find.text(AppLocalizationsEn().ok);
await pumpUntilFindNWidgets(tester, finder, 1, const Duration(seconds: 10));
await tester.tap(finder);
await tester.pump(const Duration(milliseconds: 1000));
}

Future<void> _addHotpToken(WidgetTester tester) async {
await tester.pump();
await tester.tap(find.byIcon(Icons.add_moderator));
Expand Down Expand Up @@ -218,7 +214,8 @@ Future<void> _openFolder(WidgetTester tester) async {
await tester.pump();
}

void expectMainViewIsEmptyAndCorrect() {
Future<void> expectMainViewIsEmptyAndCorrect(WidgetTester tester) async {
await pumpUntilFindNWidgets(tester, find.byType(FloatingActionButton), 1, const Duration(seconds: 10));
expect(find.byType(FloatingActionButton), findsOneWidget);
expect(find.byType(AppBarItem), findsNWidgets(5)); // 4 at BottomNavigationBar and 1 at AppBar
expect(find.byType(TokenWidgetBase), findsNothing);
Expand Down
6 changes: 4 additions & 2 deletions integration_test/copy_to_clipboard_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:privacyidea_authenticator/model/enums/introduction.dart';
import 'package:privacyidea_authenticator/model/states/introduction_state.dart';
import 'package:privacyidea_authenticator/model/states/settings_state.dart';
import 'package:privacyidea_authenticator/model/tokens/hotp_token.dart';
import 'package:privacyidea_authenticator/state_notifiers/completed_introduction_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart';
Expand Down Expand Up @@ -40,15 +41,16 @@ void main() {
when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []);
when(mockTokenFolderRepository.saveReplaceList(any)).thenAnswer((_) async => true);
mockIntroductionRepository = MockIntroductionRepository();
final introductions = {...Introduction.values}..remove(Introduction.introductionScreen);
when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions));
when(mockIntroductionRepository.loadCompletedIntroductions())
.thenAnswer((_) async => const IntroductionState(completedIntroductions: {...Introduction.values}));
});
testWidgets('Copy to Clipboard Test', (tester) async {
await tester.pumpWidget(TestsAppWrapper(
overrides: [
settingsProvider.overrideWith((ref) => SettingsNotifier(repository: mockSettingsRepository)),
tokenProvider.overrideWith((ref) => TokenNotifier(repository: mockTokenRepository)),
tokenFolderProvider.overrideWith((ref) => TokenFolderNotifier(repository: mockTokenFolderRepository)),
introductionProvider.overrideWith((ref) => IntroductionNotifier(repository: mockIntroductionRepository)),
],
child: PrivacyIDEAAuthenticator(ApplicationCustomization.defaultCustomization),
));
Expand Down
30 changes: 21 additions & 9 deletions integration_test/rename_and_delete_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:privacyidea_authenticator/model/enums/introduction.dart';
import 'package:privacyidea_authenticator/model/states/introduction_state.dart';
import 'package:privacyidea_authenticator/model/states/settings_state.dart';
import 'package:privacyidea_authenticator/model/tokens/hotp_token.dart';
import 'package:privacyidea_authenticator/model/tokens/token.dart';
import 'package:privacyidea_authenticator/state_notifiers/completed_introduction_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart';
Expand All @@ -35,17 +36,28 @@ void main() {
SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'), latestVersion: Version.parse('999.999.999')));
when(mockSettingsRepository.saveSettings(any)).thenAnswer((_) async => true);
mockTokenRepository = MockTokenRepository();
when(mockTokenRepository.loadTokens()).thenAnswer((_) async => [
HOTPToken(label: 'test', issuer: 'test', id: 'id', algorithm: Algorithms.SHA256, digits: 6, secret: 'secret', counter: 0),
]);
when(mockTokenRepository.saveOrReplaceTokens(any)).thenAnswer((_) async => []);
when(mockTokenRepository.deleteTokens(any)).thenAnswer((_) async => []);
var tokens = <Token>[
HOTPToken(label: 'test', issuer: 'test', id: 'id', algorithm: Algorithms.SHA256, digits: 6, secret: 'secret', counter: 0),
];
when(mockTokenRepository.loadTokens()).thenAnswer((_) async {
return tokens;
});
when(mockTokenRepository.saveOrReplaceToken(any)).thenAnswer((invocation) async {
final arguments = invocation.positionalArguments;
tokens.removeWhere((element) => element.id == (arguments[0] as Token).id);
tokens.add(arguments[0] as Token);
return true;
});
when(mockTokenRepository.deleteToken(tokens.first)).thenAnswer((_) async {
tokens.remove(tokens.first);
return true;
});
mockTokenFolderRepository = MockTokenFolderRepository();
when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []);
when(mockTokenFolderRepository.saveReplaceList(any)).thenAnswer((_) async => true);
mockIntroductionRepository = MockIntroductionRepository();
final introductions = {...Introduction.values}..remove(Introduction.introductionScreen);
when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions));
when(mockIntroductionRepository.loadCompletedIntroductions())
.thenAnswer((_) async => const IntroductionState(completedIntroductions: {...Introduction.values}));
});
testWidgets('Rename and Delete Token', (tester) async {
await tester.pumpWidget(TestsAppWrapper(
Expand Down Expand Up @@ -74,7 +86,7 @@ Future<void> _renameToken(WidgetTester tester, String newName) async {
await tester.tap(find.byType(EditHOTPTokenAction));
await tester.pumpAndSettle();
expect(find.text(AppLocalizationsEn().editToken), findsOneWidget);
expect(find.byType(TextFormField), findsNWidgets(3));
expect(find.byType(TextFormField), findsNWidgets(4));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextFormField).first, '');
await tester.pumpAndSettle();
Expand All @@ -97,6 +109,6 @@ Future<void> _deleteToken(WidgetTester tester) async {
expect(find.text(AppLocalizationsEn().confirmDeletion), findsOneWidget);
expect(find.text(AppLocalizationsEn().delete), findsOneWidget);
await tester.tap(find.text(AppLocalizationsEn().delete));
await tester.pumpAndSettle();
await pumpUntilFindNWidgets(tester, find.byType(HOTPTokenWidget), 0, const Duration(seconds: 2));
expect(find.byType(HOTPTokenWidget), findsNothing);
}
6 changes: 4 additions & 2 deletions integration_test/two_step_rollout_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:privacyidea_authenticator/mains/main_netknights.dart';
import 'package:privacyidea_authenticator/model/enums/introduction.dart';
import 'package:privacyidea_authenticator/model/states/introduction_state.dart';
import 'package:privacyidea_authenticator/model/states/settings_state.dart';
import 'package:privacyidea_authenticator/state_notifiers/completed_introduction_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart';
Expand Down Expand Up @@ -41,8 +42,8 @@ void main() {
when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []);
when(mockTokenFolderRepository.saveReplaceList(any)).thenAnswer((_) async => true);
mockIntroductionRepository = MockIntroductionRepository();
final introductions = {...Introduction.values}..remove(Introduction.introductionScreen);
when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions));
when(mockIntroductionRepository.loadCompletedIntroductions())
.thenAnswer((_) async => const IntroductionState(completedIntroductions: {...Introduction.values}));
});
testWidgets(
'2step rollout test',
Expand All @@ -52,6 +53,7 @@ void main() {
settingsProvider.overrideWith((ref) => SettingsNotifier(repository: mockSettingsRepository)),
tokenProvider.overrideWith((ref) => TokenNotifier(repository: mockTokenRepository)),
tokenFolderProvider.overrideWith((ref) => TokenFolderNotifier(repository: mockTokenFolderRepository)),
introductionProvider.overrideWith((ref) => IntroductionNotifier(repository: mockIntroductionRepository)),
],
child: PrivacyIDEAAuthenticator(ApplicationCustomization.defaultCustomization),
));
Expand Down
40 changes: 33 additions & 7 deletions integration_test/views_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ import 'package:privacyidea_authenticator/mains/main_netknights.dart';
import 'package:privacyidea_authenticator/model/enums/introduction.dart';
import 'package:privacyidea_authenticator/model/states/introduction_state.dart';
import 'package:privacyidea_authenticator/model/states/settings_state.dart';
import 'package:privacyidea_authenticator/model/tokens/token.dart';
import 'package:privacyidea_authenticator/state_notifiers/completed_introduction_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/push_request_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart';
import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart';
import 'package:privacyidea_authenticator/utils/customization/application_customization.dart';
import 'package:privacyidea_authenticator/utils/push_provider.dart';
import 'package:privacyidea_authenticator/utils/riverpod_providers.dart';
import 'package:privacyidea_authenticator/utils/rsa_utils.dart';
import 'package:privacyidea_authenticator/model/version.dart';
Expand All @@ -36,9 +40,19 @@ void main() {
SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'), latestVersion: Version.parse('999.999.999')));
when(mockSettingsRepository.saveSettings(any)).thenAnswer((_) async => true);
mockTokenRepository = MockTokenRepository();
when(mockTokenRepository.loadTokens()).thenAnswer((_) async => []);
when(mockTokenRepository.saveOrReplaceTokens(any)).thenAnswer((_) async => []);
when(mockTokenRepository.deleteTokens(any)).thenAnswer((_) async => []);
var tokens = <Token>[];
when(mockTokenRepository.loadTokens()).thenAnswer((_) async => tokens);
when(mockTokenRepository.saveOrReplaceToken(any)).thenAnswer((invocation) async {
final arguments = invocation.positionalArguments;
tokens.removeWhere((element) => element.id == (arguments[0] as Token).id);
tokens.add(arguments[0] as Token);
return true;
});
when(mockTokenRepository.deleteToken(any)).thenAnswer((invocation) async {
final arguments = invocation.positionalArguments;
tokens.removeWhere((element) => element.id == (arguments[0] as Token).id);
return true;
});
mockTokenFolderRepository = MockTokenFolderRepository();
when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []);
when(mockTokenFolderRepository.saveReplaceList(any)).thenAnswer((_) async => true);
Expand All @@ -56,8 +70,8 @@ void main() {
sslVerify: anyNamed('sslVerify'),
)).thenAnswer((_) => Future.value(Response('{"detail": {"public_key": "publicKey"}}', 200)));
mockIntroductionRepository = MockIntroductionRepository();
final introductions = {...Introduction.values}..remove(Introduction.introductionScreen);
when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions));
when(mockIntroductionRepository.loadCompletedIntroductions())
.thenAnswer((_) async => const IntroductionState(completedIntroductions: {...Introduction.values}));
});

testWidgets('Views Test', (tester) async {
Expand All @@ -70,7 +84,19 @@ void main() {
firebaseUtils: mockFirebaseUtils,
ioClient: mockIOClient,
)),
pushRequestProvider.overrideWith(
(ref) => PushRequestNotifier(
rsaUtils: mockRsaUtils,
pushProvider: PushProvider(
rsaUtils: mockRsaUtils,
ioClient: mockIOClient,
firebaseUtils: mockFirebaseUtils,
),
ioClient: mockIOClient,
),
),
tokenFolderProvider.overrideWith((ref) => TokenFolderNotifier(repository: mockTokenFolderRepository)),
introductionProvider.overrideWith((ref) => IntroductionNotifier(repository: mockIntroductionRepository)),
],
child: PrivacyIDEAAuthenticator(ApplicationCustomization.defaultCustomization),
));
Expand Down Expand Up @@ -108,10 +134,10 @@ Future<void> _settingsViewTest(WidgetTester tester) async {
expect(find.text(AppLocalizationsEn().theme), findsOneWidget);
expect(find.text(AppLocalizationsEn().language), findsOneWidget);
expect(find.text(AppLocalizationsEn().errorLogTitle), findsOneWidget);
expect(find.byType(SettingsGroup), findsNWidgets(5));
expect(find.byType(SettingsGroup), findsNWidgets(6));
globalRef!.read(tokenProvider.notifier).handleQrCode(
'otpauth://pipush/label?url=http%3A%2F%2Fwww.example.com&ttl=10&issuer=issuer&enrollment_credential=enrollmentCredentials&v=1&serial=serial&serial=serial&sslverify=0');
await pumpUntilFindNWidgets(tester, find.text(AppLocalizationsEn().pushToken), 1, const Duration(minutes: 5));
expect(find.text(AppLocalizationsEn().pushToken), findsOneWidget);
expect(find.byType(SettingsGroup), findsNWidgets(5));
expect(find.byType(SettingsGroup), findsNWidgets(6));
}
14 changes: 12 additions & 2 deletions lib/l10n/app_cs.arb
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,8 @@
"importExistingToken": "{count, plural, zero{Nebyl nalezen žádný token, který by se již v aplikaci nacházel.} one{Byl nalezen token, který již v aplikaci existuje.} other{{count} byly nalezeny tokeny, které se již v aplikaci nacházejí.}}",
"importConflictToken": "{count, plural, zero{Není žádný konflikt s tokeny, které již existují.} one{Je konflikt s tokeny, které již existují.\nProsím, vyberte, který z nich chcete zachovat.} other{Je konflikt s tokeny, které již existují.\nProsím, vyberte, který z nich chcete zachovat.}}",
"importNewToken": "{count, plural, zero{Nenalezen žádný nový token.} one{Nalezen nový token, který bude importován.} other{Nalezen nový token {count}, který bude importován.}}",
"importHintPrivacyIdeaQrScan": "",
"importHintPrivacyIdeaFile": "",
"importHintPrivacyIdeaQrScan": "Chcete-li vytvořit QR kódy žetonů, přejděte do nastavení a klepněte na \"Export\". Poté vyberte \"Jako QR kód\" a klepněte na token, který chcete exportovat. Tato varianta je vhodná pouze pro přímý přenos do jiného zařízení, protože QR kód není šifrovaný.",
"importHintPrivacyIdeaFile": "Chcete-li vytvořit zálohu, přejděte do nastavení a klepněte na položku \"Export\". Vyberte \"Jako soubor\" a vyberte tokeny, které chcete exportovat. Potom klepněte na \"Exportovat\" a nastavte heslo. Úložištěm je složka pro stahování ve vašem zařízení.",
"importHint2FAS": "Vyberte zálohu 2FAS.\nPokud nemáte zálohu, vytvořte ji v aplikaci 2FAS. Doporučujeme použít heslo.",
"importHintAegisBackupFile": "Vyberte svůj export Aegis (.JSON).\nPokud nemáte export, vytvořte si jej prostřednictvím nabídky nastavení v aplikaci Aegis. Doporučujeme použít heslo.",
"importHintAegisQrScan": "Naskenujte QR kód, který obdržíte při přenosu záznamů z aplikace Aegis.",
Expand Down Expand Up @@ -638,6 +638,16 @@
"oneMore": "Ještě jeden",
"done": "Hotovo",
"confirmPassword": "Potvrďte heslo",
"exampleUrl": "Zadejte prosím platnou adresu URL, například: \"https://example.com/\"",
"pushEndpointUrl": "URL koncového bodu push",
"mustNotBeEmpty": "{field} nesmí být prázdné",
"@mustNotBeEmpty": {
"placeholders": {
"field": {
"example": "Name"
}
}
},
"sendPushRequestResponseFailed": "Odpověď se nepodařilo odeslat.",
"@sendPushRequestResponseFailed": {
"description": "Error message when the response to a push request could not be sent."
Expand Down
Loading

0 comments on commit 0296f0d

Please sign in to comment.