Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion lib/screens/verify_seed/verify_seed_page.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:no_screenshot/no_screenshot.dart';
import 'package:realunit_wallet/generated/i18n.dart';
import 'package:realunit_wallet/packages/service/wallet_service.dart';
import 'package:realunit_wallet/packages/wallet/wallet.dart';
Expand All @@ -26,9 +27,32 @@ class VerifySeedPage extends StatelessWidget {
);
}

class VerifySeedView extends StatelessWidget {
class VerifySeedView extends StatefulWidget {
const VerifySeedView({super.key});

@override
State<VerifySeedView> createState() => _VerifySeedViewState();
}

class _VerifySeedViewState extends State<VerifySeedView> {
// @no-integration-test: no_screenshot suppresses the OS screenshot /
// app-switcher thumbnail / screen-recording via a platform channel — the
// real effect is only observable on a device, not in a widget/golden test.
@override
void initState() {
// Seed words are entered/visible here — block screenshots and the
// app-switcher snapshot like the other seed screens. Re-enabled on dispose
// so other screens stay screenshot-able.
NoScreenshot.instance.screenshotOff();
super.initState();
}

@override
void dispose() {
NoScreenshot.instance.screenshotOn();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down
14 changes: 14 additions & 0 deletions test/goldens/screens/verify_seed/verify_seed_golden_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
Expand All @@ -16,6 +17,12 @@ void main() {
late _MockVerifySeedCubit verifySeedCubit;
late MockHomeBloc homeBloc;

// VerifySeedView toggles screenshot protection in initState/dispose via the
// no_screenshot method channel. Stub it so the render does not throw a
// MissingPluginException in the headless golden harness.
const noScreenshotChannel =
MethodChannel('com.flutterplaza.no_screenshot_methods');

setUp(() {
verifySeedCubit = _MockVerifySeedCubit();
homeBloc = MockHomeBloc();
Expand All @@ -26,6 +33,13 @@ void main() {
),
);
when(() => homeBloc.state).thenReturn(const HomeState());
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(noScreenshotChannel, (_) async => true);
});

tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(noScreenshotChannel, null);
});

Widget buildSubject() => MultiBlocProvider(
Expand Down
31 changes: 31 additions & 0 deletions test/screens/verify_seed/verify_seed_page_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -182,6 +183,36 @@ void main() {
});
});

// The verify-seed screen (where the user re-enters real seed words) had no
// screenshot protection, unlike the sibling seed screens. It must disable
// screenshots on init and re-enable on dispose so it never lands in the
// app-switcher snapshot / a recording.
testWidgets('disables screenshots on init and re-enables on dispose',
(tester) async {
const channel = MethodChannel('com.flutterplaza.no_screenshot_methods');
final calls = <String>[];
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(call) async {
calls.add(call.method);
return true;
},
);
addTearDown(
() => tester.binding.defaultBinaryMessenger
.setMockMethodCallHandler(channel, null),
);

await tester.pumpApp(buildSubject(const VerifySeedView()));
expect(calls, contains('screenshotOff'),
reason: 'seed screen must block screenshots on init');

// Replace the screen so VerifySeedView is disposed.
await tester.pumpApp(buildSubject(const SizedBox.shrink()));
expect(calls, contains('screenshotOn'),
reason: 'leaving the seed screen must re-enable screenshots');
});

group('$BlocListener', () {
testWidgets('sends $LoadWalletEvent with the committed wallet when verified',
(tester) async {
Expand Down
Loading