Skip to content

Commit

Permalink
test(cat-voices): integration tests start automating onboarding (#1435)
Browse files Browse the repository at this point in the history
* wip

* wip

* dic fix
  • Loading branch information
oldGreg5 authored Dec 24, 2024
1 parent af34b4b commit f7b9f2c
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 3 deletions.
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ Wireframes
Wmissing
Wnullable
Woverlength
Writedown
xcassets
xcconfig
xcfilelist
Expand Down
50 changes: 50 additions & 0 deletions catalyst_voices/apps/voices/integration_test/onboarding_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:catalyst_voices/app/view/app.dart';
import 'package:catalyst_voices/configs/bootstrap.dart';
import 'package:catalyst_voices/routes/routes.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:integration_test/integration_test.dart';
import 'package:patrol_finders/patrol_finders.dart';

import 'pageobject/app_bar_page.dart';
import 'pageobject/onboarding_page.dart';

void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
late final GoRouter router;

setUpAll(() async {
router = buildAppRouter();
await bootstrap(router: router);
});

setUp(() async {
await registerDependencies();
router.go(const DiscoveryRoute().location);
});

tearDown(() async {
await restartDependencies();
});

patrolWidgetTest(
'Onboarding - visitor - get started button works',
(PatrolTester $) async {
await $.pumpWidgetAndSettle(App(routerConfig: router));
await $(AppBarPage.getStartedBtn)
.tap(settleTimeout: const Duration(seconds: 10));
expect($(OnboardingPage.registrationInfoPanel), findsOneWidget);
expect($(OnboardingPage.registrationDetailsPanel), findsOneWidget);
},
);

patrolWidgetTest(
'Onboarding - visitor - get started screen looks as expected',
(PatrolTester $) async {
await $.pumpWidgetAndSettle(App(routerConfig: router));
await $(AppBarPage.getStartedBtn)
.tap(settleTimeout: const Duration(seconds: 10));
await OnboardingPage.getStartedScreenLooksAsExpected($);
},
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ import 'package:flutter/material.dart';

class AppBarPage {
static const spacesDrawerButton = Key('DrawerButton');
static const getStartedBtn = Key('GetStartedButton');

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
library dashboard_page;

import 'package:flutter/material.dart';

class CommonPage {
static const decoratorData = Key('DecoratorData');
static const decoratorIconBefore = Key('DecoratorIconBefore');
static const decoratorIconAfter = Key('DecoratorIconAfter');
static const dialogCloseButton = Key('DialogCloseButton');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
library dashboard_page;

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol_finders/patrol_finders.dart';

import '../types/registration_state.dart';
import '../utils/translations_utils.dart';
import 'common_page.dart';

class OnboardingPage {
static const registrationInfoPanel = Key('RegistrationInfoPanel');
static const registrationDetailsPanel = Key('RegistrationDetailsPanel');
static const registrationInfoLearnMoreButton = Key('LearnMoreButton');
static const headerTitle = Key('HeaderTitle');
static const headerSubtitle = Key('HeaderSubtitle');
static const headerBody = Key('HeaderBody');
static const registrationInfoPictureContainer = Key('PictureContainer');
static const registrationInfoTaskPicture = Key('TaskPictureIconBox');
static const registrationDetailsTitle = Key('RegistrationDetailsTitle');
static const registrationDetailsBody = Key('RegistrationDetailsBody');

static Future<String?> infoPartHeaderTitleText(PatrolTester $) async {
return $(registrationInfoPanel).$(headerTitle).text;
}

static Future<String?> infoPartHeaderSubtitleText(PatrolTester $) async {
return $(registrationInfoPanel).$(headerSubtitle).text;
}

static Future<String?> infoPartHeaderBodyText(PatrolTester $) async {
return $(registrationInfoPanel).$(headerBody).text;
}

static Future<String?> infoPartLearnMoreButtonText(PatrolTester $) async {
final child = find.descendant(
of: $(registrationInfoPanel).$(CommonPage.decoratorData),
matching: find.byType(Text),
);
return $(child).text;
}

static Finder infoPartTaskPicture(PatrolTester $) {
final child = find.descendant(
of: $(registrationInfoPanel).$(registrationInfoPictureContainer),
matching: find.byType(IconTheme),
);
return child;
}

static String? detailsPartGetStartedTitle(PatrolTester $) {
final child = find.descendant(
of: $(registrationDetailsPanel).$(registrationDetailsTitle),
matching: find.byType(Text),
);
return $(child).text;
}

static String? detailsPartGetStartedBody(PatrolTester $) {
final child = find.descendant(
of: $(registrationDetailsPanel).$(registrationDetailsBody),
matching: find.byType(Text),
);
return $(child).text;
}

static String? detailsPartGetStartedQuestionText(PatrolTester $) {
return $(registrationDetailsPanel).$(const Key('GetStartedQuestion')).text;
}

static Future<PatrolFinder> detailsPartGetStartedCreateNewBtn(
PatrolTester $,
) async {
return $(registrationDetailsPanel)
.$(const Key('CreateAccountType.createNew'));
}

static Future<PatrolFinder> detailsPartGetStartedRecoverBtn(
PatrolTester $,
) async {
return $(registrationDetailsPanel)
.$(const Key('CreateAccountType.recover'));
}

static Future<void> getStartedScreenLooksAsExpected(PatrolTester $) async {
await registrationInfoPanelLooksAsExpected($, RegistrationState.getStarted);
await registrationDetailsPanelLooksAsExpected(
$,
RegistrationState.getStarted,
);
}

static Future<void> registrationInfoPanelLooksAsExpected(
PatrolTester $,
RegistrationState step,
) async {
switch (step) {
case RegistrationState.getStarted:
expect(await infoPartHeaderTitleText($), T.get('Get Started'));
expect(await infoPartLearnMoreButtonText($), T.get('Learn More'));
expect(infoPartTaskPicture($), findsOneWidget);
break;
case RegistrationState.checkYourKeychain:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.createKeychain:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.keychainCreated:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.keychainRestoreInfo:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.keychainRestoreInput:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.keychainRestoreStart:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.keychainRestoreSuccess:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.mnemonicInput:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.mnemonicVerified:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.mnemonicWritedown:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.passwordInfo:
// TODO: Handle this case.
throw UnimplementedError();
case RegistrationState.passwordInput:
// TODO: Handle this case.
throw UnimplementedError();
}
}

static Future<void> registrationDetailsPanelLooksAsExpected(
PatrolTester $, RegistrationState getStarted,) async {
expect(
detailsPartGetStartedTitle($),
T.get('Welcome to Catalyst'),
);
expect(
detailsPartGetStartedBody($), isNotEmpty,
);
expect(
detailsPartGetStartedQuestionText($),
T.get('What do you want to do?'),
);
expect(
await detailsPartGetStartedCreateNewBtn($),
findsOneWidget,
);
expect(
await detailsPartGetStartedRecoverBtn($),
findsOneWidget,
);
expect(
$(CommonPage.dialogCloseButton),
findsOneWidget,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
enum RegistrationState {
checkYourKeychain,
createKeychain,
getStarted,
keychainCreated,
keychainRestoreInfo,
keychainRestoreInput,
keychainRestoreStart,
keychainRestoreSuccess,
mnemonicInput,
mnemonicVerified,
mnemonicWritedown,
passwordInfo,
passwordInput;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//wrapper that we should adapt to read actual i18n translations we use in the app
//it will also support different locales once we have it
//right now this class is here so we can easily replace this implementation and know where
class T {
static String get(String key, {String? locale}) {
return key;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class GetStartedPanel extends StatelessWidget {
),
const SizedBox(height: 32),
Text(
key: const Key('GetStartedQuestion'),
context.l10n.accountCreationGetStatedWhatNext,
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colors.textOnPrimaryLevel0,
Expand All @@ -40,7 +41,7 @@ class GetStartedPanel extends StatelessWidget {
children: CreateAccountType.values
.map<Widget>((type) {
return RegistrationTile(
key: ValueKey(type),
key: Key(type.toString()),
icon: type._icon,
title: type._getTitle(context.l10n),
subtitle: type._getSubtitle(context.l10n),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class RegistrationDetailsPanel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocSelector<RegistrationCubit, RegistrationState, RegistrationStep>(
key: const Key('RegistrationDetailsPanel'),
selector: (state) => state.step,
builder: (context, state) {
return switch (state) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class RegistrationInfoPanel extends StatelessWidget {
);

return InformationPanel(
key: const Key('RegistrationInfoPanel'),
title: headerStrings.title,
subtitle: headerStrings.subtitle,
body: headerStrings.body,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class InformationPanel extends StatelessWidget {
body: body,
),
const SizedBox(height: 12),
Expanded(child: Center(child: picture)),
Expanded(key: const Key('PictureContainer'), child: Center(child: picture)),
const SizedBox(height: 12),
_Footer(
progress: progress,
Expand Down Expand Up @@ -64,16 +64,19 @@ class _Header extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
key: const Key('HeaderTitle'),
title,
style: theme.textTheme.titleLarge?.copyWith(color: textColor),
),
if (subtitle != null)
Text(
key: const Key('HeaderSubtitle'),
subtitle,
style: theme.textTheme.titleMedium?.copyWith(color: textColor),
),
if (body != null)
Text(
key: const Key('HeaderBody'),
body,
style: theme.textTheme.bodyMedium?.copyWith(color: textColor),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ class RegistrationStageMessage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DefaultTextStyle(
key: const Key('RegistrationDetailsTitle'),
style: theme.textTheme.titleMedium!.copyWith(color: textColor),
child: title,
),
SizedBox(height: spacing),
DefaultTextStyle(
key: const Key('RegistrationDetailsBody'),
style: theme.textTheme.bodyMedium!.copyWith(color: textColor),
child: subtitle,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class VoicesLearnMoreButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return VoicesTextButton(
key: const Key('LearnMoreButton'),
trailing: VoicesAssets.icons.externalLink.buildIcon(),
onTap: onTap,
child: Text(context.l10n.learnMore),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,17 @@ class AffixDecorator extends StatelessWidget {
children: [
if (prefix != null) ...[
IconTheme(
key: const Key('DecoratorIconBefore'),
data: iconTheme ?? IconTheme.of(context),
child: prefix,
),
SizedBox(width: gap),
],
Flexible(child: child),
Flexible(key: const Key('DecoratorData'),child: child,),
if (suffix != null) ...[
SizedBox(width: gap),
IconTheme(
key: const Key('DecoratorIconAfter'),
data: iconTheme ?? IconTheme.of(context),
child: suffix,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class _DialogCloseButton extends StatelessWidget {
);

return Align(
key: const Key('DialogCloseButton'),
alignment: Alignment.topRight,
child: IconButtonTheme(
data: const IconButtonThemeData(style: buttonStyle),
Expand Down

0 comments on commit f7b9f2c

Please sign in to comment.