fix(app): the phrase is never copied, codes leave the clipboard a minute later
The recovery phrase opens the account's keys; clipboard histories and cloud clipboard sync would keep it beyond the app's reach. It is saved as a file or written down. Recovery codes and an authenticator's key may be copied, and the clipboard is emptied 60 s later if it still holds them; they are no longer selectable, as the system's own copy wouldn't empty it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
7bef70948d
commit
a0b7e2bf18
1 changed files with 163 additions and 17 deletions
|
|
@ -0,0 +1,103 @@
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_app/halkyon_app.dart';
|
||||
import 'package:halkyon_app/testing.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
void main() {
|
||||
/// What the system's clipboard holds.
|
||||
String? clipboard;
|
||||
|
||||
/// Stands in for the system's clipboard for [tester].
|
||||
void mockClipboard(WidgetTester tester) {
|
||||
clipboard = null;
|
||||
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(call) async => switch (call.method) {
|
||||
'Clipboard.setData' => () {
|
||||
clipboard = (call.arguments as Map)['text'] as String?;
|
||||
return null;
|
||||
}(),
|
||||
'Clipboard.getData' => {'text': clipboard},
|
||||
_ => null,
|
||||
},
|
||||
);
|
||||
addTearDown(
|
||||
() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> pumpView(WidgetTester tester, Widget view) async {
|
||||
tester.view.physicalSize = const Size(412, 1400);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await tester.pumpWidget(
|
||||
AppScope(
|
||||
dependencies: TestAppDependencies(gateway: FakeAuthGateway()),
|
||||
child: MaterialApp(
|
||||
localizationsDelegates:
|
||||
HalkyonAppLocalizations.localizationsDelegates,
|
||||
supportedLocales: HalkyonAppLocalizations.supportedLocales,
|
||||
home: Scaffold(body: SingleChildScrollView(child: view)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
group('copySecret', () {
|
||||
testWidgets('clears the clipboard a minute later', (tester) async {
|
||||
mockClipboard(tester);
|
||||
await copySecret('ABCD-EFGH');
|
||||
expect(clipboard, 'ABCD-EFGH');
|
||||
await tester.pump(const Duration(seconds: 59));
|
||||
expect(clipboard, 'ABCD-EFGH');
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
expect(clipboard, '');
|
||||
});
|
||||
|
||||
testWidgets('leaves what was copied since', (tester) async {
|
||||
mockClipboard(tester);
|
||||
await copySecret('ABCD-EFGH');
|
||||
clipboard = 'something else';
|
||||
await tester.pump(secretClipboardLifetime);
|
||||
expect(clipboard, 'something else');
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('the recovery phrase is saved, never copied', (tester) async {
|
||||
mockClipboard(tester);
|
||||
await pumpView(
|
||||
tester,
|
||||
const RecoveryPhraseView(
|
||||
phrase:
|
||||
'abandon abandon abandon abandon abandon abandon abandon abandon '
|
||||
'abandon abandon abandon abandon abandon abandon abandon abandon '
|
||||
'abandon abandon abandon abandon abandon abandon abandon art',
|
||||
),
|
||||
);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Save as a file'), findsOneWidget);
|
||||
expect(find.byType(SelectableText), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('recovery codes are copied, and cleared a minute later', (
|
||||
tester,
|
||||
) async {
|
||||
mockClipboard(tester);
|
||||
await pumpView(
|
||||
tester,
|
||||
const RecoveryCodesView(codes: ['AAAA-BBBB', 'CCCC-DDDD']),
|
||||
);
|
||||
expect(find.byType(SelectableText), findsNothing);
|
||||
await tester.tap(find.text('Copy'));
|
||||
await tester.pump();
|
||||
expect(clipboard, 'AAAA-BBBB\nCCCC-DDDD');
|
||||
await tester.pump(secretClipboardLifetime);
|
||||
expect(clipboard, '');
|
||||
// The snack bar's own time.
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue