Skip to content

Commit

Permalink
Fix pro token input field tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ashuntu committed Dec 13, 2024
1 parent af14da5 commit 9588d6b
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:provider/provider.dart';
import 'package:ubuntupro/core/agent_api_client.dart';
import 'package:ubuntupro/core/pro_token.dart';
import 'package:ubuntupro/pages/subscribe_now/subscribe_now_model.dart';
import 'package:ubuntupro/pages/subscribe_now/subscribe_now_widgets.dart';
import '../../utils/build_multiprovider_app.dart';
import '../../utils/token_samples.dart' as tks;
import 'subscribe_now_widgets_test.mocks.dart';

@GenerateMocks([AgentApiClient])
void main() {
group('pro token value', () {
test('errors', () async {
Expand Down Expand Up @@ -47,22 +54,14 @@ void main() {

group('pro token input', () {
group('basic flow', () {
final theApp = buildApp(onApply: (_) {}, isExpanded: true);
final theApp = buildApp(onApply: () {}, isExpanded: true);
testWidgets('starts with no error', (tester) async {
await tester.pumpWidget(theApp);

final input = tester.firstWidget<TextField>(find.byType(TextField));

expect(input.decoration!.errorText, isNull);
});
testWidgets('starts with button disabled', (tester) async {
await tester.pumpWidget(theApp);

final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));

expect(button.enabled, isFalse);
});

testWidgets('invalid non-empty tokens', (tester) async {
await tester.pumpWidget(theApp);
Expand All @@ -79,10 +78,6 @@ void main() {
matching: find.text(lang.tokenErrorInvalid),
);
expect(errorText, findsOne);

final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));
expect(button.enabled, isFalse);
}
});

Expand All @@ -102,16 +97,11 @@ void main() {
);
expect(errorText, findsOne);

final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));
expect(button.enabled, isFalse);

// ...except when we delete the content we should have no more errors
await tester.enterText(inputField, '');
await tester.pump();
final input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.error, isNull);
expect(button.enabled, isFalse);
});

testWidgets('good token', (tester) async {
Expand All @@ -125,19 +115,18 @@ void main() {

final input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.error, isNull);
final validText = find.descendant(
final errorText = find.descendant(
of: inputField,
matching: find.text(lang.tokenValid),
matching: find.text(lang.tokenErrorInvalid),
);
expect(validText, findsOne);

final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));
expect(button.enabled, isTrue);
expect(errorText, findsNothing);
});

testWidgets('good token with spaces', (tester) async {
await tester.pumpWidget(theApp);
final inputField = find.byType(TextField);
final context = tester.element(inputField);
final lang = AppLocalizations.of(context);

await tester.enterText(
inputField,
Expand All @@ -149,14 +138,17 @@ void main() {
final input = tester.firstWidget<TextField>(inputField);
expect(input.decoration!.errorText, isNull);

final button =
tester.firstWidget<ElevatedButton>(find.byType(ElevatedButton));
expect(button.enabled, isTrue);
final errorText = find.descendant(
of: inputField,
matching: find.text(lang.tokenErrorInvalid),
);
expect(errorText, findsNothing);
});
});

testWidgets('apply', (tester) async {
var called = false;
final theApp = buildApp(isExpanded: true, onApply: (_) => called = true);
final theApp = buildApp(isExpanded: true, onApply: () => called = true);
await tester.pumpWidget(theApp);

final inputField = find.byType(TextField);
Expand All @@ -165,15 +157,15 @@ void main() {
await tester.pump();

expect(called, isFalse);
final button = find.byType(ElevatedButton);
await tester.tap(button);
// simulate an enter key/submission of the text field
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle();
expect(called, isTrue);
});

testWidgets('apply on submit', (tester) async {
var called = false;
final theApp = buildApp(isExpanded: true, onApply: (_) => called = true);
final theApp = buildApp(isExpanded: true, onApply: () => called = true);
await tester.pumpWidget(theApp);

final textFieldFinder = find.byType(TextField);
Expand All @@ -187,7 +179,7 @@ void main() {
});

testWidgets('token error enum l10n', (tester) async {
final theApp = buildApp(isExpanded: true, onApply: (_) {});
final theApp = buildApp(isExpanded: true, onApply: () {});
await tester.pumpWidget(theApp);
final context = tester.element(find.byType(ProTokenInputField));
final lang = AppLocalizations.of(context);
Expand All @@ -198,17 +190,21 @@ void main() {
});
}

MaterialApp buildApp({
required void Function(ProToken) onApply,
Widget buildApp({
required void Function() onApply,
bool isExpanded = false,
}) {
return MaterialApp(
home: Scaffold(
return buildSingleRouteMultiProviderApp(
child: Scaffold(
body: ProTokenInputField(
onApply: onApply,
onSubmit: onApply,
isExpanded: isExpanded,
),
),
localizationsDelegates: AppLocalizations.localizationsDelegates,
providers: [
ChangeNotifierProvider.value(
value: SubscribeNowModel(MockAgentApiClient()),
),
],
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Mocks generated by Mockito 5.4.4 from annotations
// in ubuntupro/test/pages/subcribe_now/subscribe_now_widgets_test.dart.
// Do not manually edit this file.

// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i5;
import 'dart:io' as _i6;

import 'package:agentapi/agentapi.dart' as _i2;
import 'package:grpc/grpc.dart' as _i4;
import 'package:mockito/mockito.dart' as _i1;
import 'package:ubuntupro/core/agent_api_client.dart' as _i3;

// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
// ignore_for_file: deprecated_member_use_from_same_package
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class

class _FakeUIClient_0 extends _i1.SmartFake implements _i2.UIClient {
_FakeUIClient_0(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}

class _FakeSubscriptionInfo_1 extends _i1.SmartFake
implements _i2.SubscriptionInfo {
_FakeSubscriptionInfo_1(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}

class _FakeLandscapeSource_2 extends _i1.SmartFake
implements _i2.LandscapeSource {
_FakeLandscapeSource_2(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}

class _FakeConfigSources_3 extends _i1.SmartFake implements _i2.ConfigSources {
_FakeConfigSources_3(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}

/// A class which mocks [AgentApiClient].
///
/// See the documentation for Mockito's code generation for more information.
class MockAgentApiClient extends _i1.Mock implements _i3.AgentApiClient {
MockAgentApiClient() {
_i1.throwOnMissingStub(this);
}

@override
_i2.UIClient Function(_i4.ClientChannel) get stubFactory =>
(super.noSuchMethod(
Invocation.getter(#stubFactory),
returnValue: (_i4.ClientChannel __p0) => _FakeUIClient_0(
this,
Invocation.getter(#stubFactory),
),
) as _i2.UIClient Function(_i4.ClientChannel));

@override
_i5.Stream<_i3.ConnectionEvent> get onConnectionChanged =>
(super.noSuchMethod(
Invocation.getter(#onConnectionChanged),
returnValue: _i5.Stream<_i3.ConnectionEvent>.empty(),
) as _i5.Stream<_i3.ConnectionEvent>);

@override
_i5.Future<bool> connectTo(
String? host,
int? port,
_i6.Directory? certsDir,
) =>
(super.noSuchMethod(
Invocation.method(
#connectTo,
[
host,
port,
certsDir,
],
),
returnValue: _i5.Future<bool>.value(false),
) as _i5.Future<bool>);

@override
_i5.Future<_i2.SubscriptionInfo> applyProToken(String? token) =>
(super.noSuchMethod(
Invocation.method(
#applyProToken,
[token],
),
returnValue:
_i5.Future<_i2.SubscriptionInfo>.value(_FakeSubscriptionInfo_1(
this,
Invocation.method(
#applyProToken,
[token],
),
)),
) as _i5.Future<_i2.SubscriptionInfo>);

@override
_i5.Future<_i2.LandscapeSource> applyLandscapeConfig(String? config) =>
(super.noSuchMethod(
Invocation.method(
#applyLandscapeConfig,
[config],
),
returnValue:
_i5.Future<_i2.LandscapeSource>.value(_FakeLandscapeSource_2(
this,
Invocation.method(
#applyLandscapeConfig,
[config],
),
)),
) as _i5.Future<_i2.LandscapeSource>);

@override
_i5.Future<bool> ping() => (super.noSuchMethod(
Invocation.method(
#ping,
[],
),
returnValue: _i5.Future<bool>.value(false),
) as _i5.Future<bool>);

@override
_i5.Future<_i2.ConfigSources> configSources() => (super.noSuchMethod(
Invocation.method(
#configSources,
[],
),
returnValue: _i5.Future<_i2.ConfigSources>.value(_FakeConfigSources_3(
this,
Invocation.method(
#configSources,
[],
),
)),
) as _i5.Future<_i2.ConfigSources>);

@override
_i5.Future<_i2.SubscriptionInfo> notifyPurchase() => (super.noSuchMethod(
Invocation.method(
#notifyPurchase,
[],
),
returnValue:
_i5.Future<_i2.SubscriptionInfo>.value(_FakeSubscriptionInfo_1(
this,
Invocation.method(
#notifyPurchase,
[],
),
)),
) as _i5.Future<_i2.SubscriptionInfo>);
}

0 comments on commit 9588d6b

Please sign in to comment.