Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure app, add integration test #13

Merged
merged 6 commits into from
May 24, 2023
Merged
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
27 changes: 22 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ on:

jobs:
build_pipeline:
# This job will run on ubuntu virtual machine
runs-on: ubuntu-latest
runs-on: macos-latest

strategy:
matrix:
api-level:
- 29

steps:

# Setup Java environment in order to build the Android app.
Expand All @@ -25,26 +30,38 @@ jobs:
- uses: subosito/flutter-action@v1
with:
channel: 'stable' # 'dev', 'alpha', default to: 'stable'
flutter-version: '3.7.6' # you can also specify exact version of flutter
flutter-version: '3.10.1' # you can also specify exact version of flutter

# Get flutter dependencies.
- run: flutter pub get

- run: flutter pub upgrade

- run: flutter pub run intl_utils:generate

- run: flutter pub run build_runner build --delete-conflicting-outputs

- name: local_database
working-directory: ./packages/local_database
run: flutter pub get && flutter pub run build_runner build --delete-conflicting-outputs
run: flutter pub get && flutter pub upgrade && flutter pub run build_runner build --delete-conflicting-outputs

- name: rest_client
working-directory: ./packages/rest_client
run: flutter pub get && flutter pub run build_runner build --delete-conflicting-outputs
run: flutter pub get && flutter pub upgrade && flutter pub run build_runner build --delete-conflicting-outputs

- run: flutter analyze

- run: flutter test


- name: Run integration tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
arch: x86_64
profile: Nexus 6
script: flutter test integration_test --verbose


- run: flutter build apk

Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
<a href="https://github.com/zeref278"><img alt="GitHub: zeref278" src="https://img.shields.io/github/followers/zeref278?label=Follow&style=social" /></a>
<a href="https://github.com/zeref278/flutter_boilerplate"><img src="https://img.shields.io/github/stars/zeref278/flutter_boilerplate?style=social" /></a>

<a href="https://www.buymeacoffee.com/zeref278"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=zeref278&button_colour=5F7FFF&font_colour=ffffff&font_family=Cookie&outline_colour=000000&coffee_colour=FFDD00"></a>
<a href="https://www.buymeacoffee.com/zeref278" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>


</div>

Expand All @@ -25,6 +26,7 @@ A boilerplate project created in flutter using Bloc, Retrofit. Depend on code ge
* DarkTheme
* Multi languages
* Unit tests
* Integration test
* Clean architecture
* Flutter CI

Expand Down Expand Up @@ -91,10 +93,10 @@ flutter pub get && flutter pub run build_runner build --delete-conflicting-outpu
## Folder structure
```
flutter_boilerplate/
|- asssets/ (assets)
|- assets/ (assets)
|- lib/
|- common/ (dimens, spacing, theming)
|- config/ (flavor config)
|- configs/ (flavor config)
|- core/ (bloc observer, theme,...)
|- data/ (repository)
|- features/ (features page)
|- generated/ (code generation includes localization and assets generation)
Expand All @@ -106,8 +108,9 @@ flutter_boilerplate/
|- packages/
|- rest_client/ (api client)
|- local_database/ (local database)
|- tests/
|- app_test/ (mock dependencies)
|- integration_test
|- test/
|- dependencies/ (mock dependencies)
|- features/ (bloc test features)

```
Expand Down
210 changes: 186 additions & 24 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,32 +1,194 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- 'lib/**/*.freezed.dart'
- 'lib/**/*.g.dart'
- 'lib/generated/'
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
- always_declare_return_types
- always_put_required_named_parameters_first
- always_use_package_imports
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catching_errors
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
- avoid_final_parameters
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
- avoid_js_rounded_ints
- avoid_multiple_declarations_per_line
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_print
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null_for_void
- avoid_returning_this
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_type_to_string
- avoid_types_as_parameter_names
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- avoid_void_async
- avoid_web_libraries_in_flutter
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
- cascade_invocations
- cast_nullable_to_non_nullable
- collection_methods_unrelated_type
- combinators_ordering
- comment_references
- conditional_uri_does_not_exist
- constant_identifier_names
- control_flow_in_finally
- curly_braces_in_flow_control_structures
- dangling_library_doc_comments
- depend_on_referenced_packages
- deprecated_consistency
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
- enable_null_safety
- eol_at_end_of_file
- exhaustive_cases
- file_names
- flutter_style_todos
- hash_and_equals
- implicit_call_tearoffs
- implementation_imports
- iterable_contains_unrelated_type
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_annotations
- library_names
- library_prefixes
- library_private_types_in_public_api
- lines_longer_than_80_chars
- list_remove_unrelated_type
- literal_only_boolean_expressions
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_default_cases
- no_duplicate_case_values
- no_leading_underscores_for_library_prefixes
- no_leading_underscores_for_local_identifiers
- no_logic_in_create_state
- no_runtimeType_toString
- non_constant_identifier_names
- noop_primitive_operations
- null_check_on_nullable_type_parameter
- null_closures
- only_throw_errors
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_asserts_with_message
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_null_aware_method_calls
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
- recursive_getters
- require_trailing_commas
- secure_pubspec_urls
- sized_box_for_whitespace
- sized_box_shrink_expand
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
- type_annotate_public_apis
- type_init_formals
- unawaited_futures
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_constructor_name
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_late
- unnecessary_library_directive
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unnecessary_to_list_in_spreads
- unrelated_type_equality_checks
- use_build_context_synchronously
- use_colored_box
- use_decorated_box
- use_enums
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_if_null_to_convert_nulls_to_bools
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_string_buffers
- use_string_in_part_of_directives
- use_super_parameters
- use_test_throws_matchers
- use_to_and_as_if_applicable
- valid_regexps
- void_checks

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
37 changes: 37 additions & 0 deletions integration_test/cases/app_route_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:boilerplate/main.dart' as app;
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../robot_tester/app_robot.dart';
import '../robot_tester/home_robot.dart';
import '../robot_tester/intro_robot.dart';

void main() {
IntroRobot introTester;
HomeRobot homeTester;
AppRobot appRobot;
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

group('app', () {
/// Case: show intro page first use app
testWidgets('Show intro page first use app', (tester) async {
await app.main();
appRobot = AppRobot(tester);
introTester = IntroRobot(tester);
homeTester = HomeRobot(tester);
await introTester.pressStarted();
await tester.pumpAndSettle(const Duration(seconds: 1));
await homeTester.verifyIsHomePage();
});

/// Case: show intro page did not first use app
testWidgets('Show home page did not first use app', (tester) async {
appRobot = AppRobot(tester);
introTester = IntroRobot(tester);
homeTester = HomeRobot(tester);

appRobot.restartApp();
await tester.pumpAndSettle(const Duration(seconds: 1));
await homeTester.verifyIsHomePage();
});
});
}
15 changes: 15 additions & 0 deletions integration_test/robot_tester/app_robot.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:boilerplate/features/app/view/app.dart';
import 'package:flutter/material.dart';

import 'robot_tester_base.dart';

class AppRobot extends RobotTesterBase {
AppRobot(super.tester);
void restartApp() {
runApp(
App(
key: UniqueKey(),
),
);
}
}
14 changes: 14 additions & 0 deletions integration_test/robot_tester/home_robot.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'dart:async';
import 'package:boilerplate/core/keys/app_keys.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'robot_tester_base.dart';

class HomeRobot extends RobotTesterBase {
HomeRobot(super.tester);

FutureOr<void> verifyIsHomePage() async {
await tester.pumpAndSettle();
expect(find.byKey(const Key(WidgetKeys.homeScaffoldKey)), findsOneWidget);
}
}
Loading