diff --git a/.github/workflows/pinball_theme.yaml b/.github/workflows/pinball_theme.yaml new file mode 100644 index 00000000..f6fa14aa --- /dev/null +++ b/.github/workflows/pinball_theme.yaml @@ -0,0 +1,18 @@ +name: pinball_theme + +on: + push: + paths: + - "packages/pinball_theme/**" + - ".github/workflows/pinball_theme.yaml" + + pull_request: + paths: + - "packages/pinball_theme/**" + - ".github/workflows/pinball_theme.yaml" + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + working_directory: packages/pinball_theme \ No newline at end of file diff --git a/.gitignore b/.gitignore index bd315f72..eeb2b0f6 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,5 @@ app.*.map.json !.idea/codeStyles/ !.idea/dictionaries/ !.idea/runConfigurations/ + +.firebase diff --git a/README.md b/README.md index b51926b5..b91a1c98 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,33 @@ Update the `CFBundleLocalizations` array in the `Info.plist` at `ios/Runner/Info } ``` +### Deploy application to Firebase hosting + +Follow the following steps to deploy the application. + +## Firebase CLI + +Install and authenticate with [Firebase CLI tools](https://firebase.google.com/docs/cli) + +## Build the project using the desired environment + +```bash +# Development +$ flutter build web --release --target lib/main_development.dart + +# Staging +$ flutter build web --release --target lib/main_staging.dart + +# Production +$ flutter build web --release --target lib/main_production.dart +``` + +## Deploy + +```bash +$ firebase deploy +``` + [coverage_badge]: coverage_badge.svg [flutter_localizations_link]: https://api.flutter.dev/flutter/flutter_localizations/flutter_localizations-library.html [internationalization_link]: https://flutter.dev/docs/development/accessibility-and-localization/internationalization diff --git a/assets/images/components/ball.png b/assets/images/components/ball.png new file mode 100644 index 00000000..af80811b Binary files /dev/null and b/assets/images/components/ball.png differ diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..80e2ae69 --- /dev/null +++ b/firebase.json @@ -0,0 +1,11 @@ +{ + "hosting": { + "public": "build/web", + "site": "ashehwkdkdjruejdnensjsjdne", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ] + } +} diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index e285b14b..2d9dddf0 100644 --- a/lib/game/components/ball.dart +++ b/lib/game/components/ball.dart @@ -1,23 +1,31 @@ +import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; -import 'package:flame_forge2d/body_component.dart'; -import 'package:flutter/material.dart'; -import 'package:forge2d/forge2d.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; -class Ball extends BodyComponent +class Ball extends PositionBodyComponent with BlocComponent { Ball({ required Vector2 position, - }) : _position = position { - // TODO(alestiago): Use asset instead of color when provided. - paint = Paint()..color = const Color(0xFFFFFFFF); - } + }) : _position = position, + super(size: ballSize); + + static final ballSize = Vector2.all(2); final Vector2 _position; + static const spritePath = 'components/ball.png'; + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = await gameRef.loadSprite(spritePath); + positionComponent = SpriteComponent(sprite: sprite, size: ballSize); + } + @override Body createBody() { - final shape = CircleShape()..radius = 2; + final shape = CircleShape()..radius = ballSize.x / 2; final fixtureDef = FixtureDef(shape)..density = 1; diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index 7bf58273..b784b8cb 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -3,6 +3,9 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/components/components.dart'; +/// {@template wall} +/// A continuos generic and [BodyType.static] barrier that divides a game area. +/// {@endtemplate} class Wall extends BodyComponent { Wall({ required this.start, @@ -29,6 +32,12 @@ class Wall extends BodyComponent { } } +/// {@template bottom_wall} +/// [Wall] located at the bottom of the board. +/// +/// Collisions with [BottomWall] are listened by +/// [BottomWallBallContactCallback]. +/// {@endtemplate} class BottomWall extends Wall { BottomWall(Forge2DGame game) : super( @@ -40,6 +49,9 @@ class BottomWall extends Wall { ); } +/// {@template bottom_wall_ball_contact_callback} +/// Listens when a [Ball] falls into a [BottomWall]. +/// {@endtemplate} class BottomWallBallContactCallback extends ContactCallback { @override void begin(Ball ball, BottomWall wall, Contact contact) { diff --git a/lib/game/game.dart b/lib/game/game.dart index 253dcc9f..ad02533d 100644 --- a/lib/game/game.dart +++ b/lib/game/game.dart @@ -1,4 +1,5 @@ export 'bloc/game_bloc.dart'; export 'components/components.dart'; +export 'game_assets.dart'; export 'pinball_game.dart'; export 'view/view.dart'; diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart new file mode 100644 index 00000000..964aeda1 --- /dev/null +++ b/lib/game/game_assets.dart @@ -0,0 +1,11 @@ +import 'package:pinball/game/game.dart'; + +/// Add methods to help loading and caching game assets. +extension PinballGameAssetsX on PinballGame { + /// Pre load the initial assets of the game. + Future preLoadAssets() async { + await Future.wait([ + images.load(Ball.spritePath), + ]); + } +} diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 91e12854..7c701c09 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -1,10 +1,14 @@ +import 'dart:async'; + import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; class PinballGame extends Forge2DGame with FlameBloc { void spawnBall() { - add(Ball(position: ballStartingPosition)); + add( + Ball(position: ballStartingPosition), + ); } // TODO(erickzanardo): Change to the plumber position @@ -18,10 +22,15 @@ class PinballGame extends Forge2DGame with FlameBloc { @override Future onLoad() async { - spawnBall(); addContactCallback(BallScorePointsCallback()); await add(BottomWall(this)); addContactCallback(BottomWallBallContactCallback()); } + + @override + void onAttach() { + super.onAttach(); + spawnBall(); + } } diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 28834907..02f5b34c 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -23,9 +23,26 @@ class PinballGamePage extends StatelessWidget { } } -class PinballGameView extends StatelessWidget { +class PinballGameView extends StatefulWidget { const PinballGameView({Key? key}) : super(key: key); + @override + State createState() => _PinballGameViewState(); +} + +class _PinballGameViewState extends State { + late PinballGame _game; + + @override + void initState() { + super.initState(); + + // TODO(erickzanardo): Revisit this when we start to have more assets + // this could expose a Stream (maybe even a cubit?) so we could show the + // the loading progress with some fancy widgets. + _game = PinballGame()..preLoadAssets(); + } + @override Widget build(BuildContext context) { return BlocListener( @@ -39,7 +56,7 @@ class PinballGameView extends StatelessWidget { ); } }, - child: GameWidget(game: PinballGame()), + child: GameWidget(game: _game), ); } } diff --git a/lib/theme/cubit/theme_cubit.dart b/lib/theme/cubit/theme_cubit.dart new file mode 100644 index 00000000..7ba79e59 --- /dev/null +++ b/lib/theme/cubit/theme_cubit.dart @@ -0,0 +1,13 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +part 'theme_state.dart'; + +class ThemeCubit extends Cubit { + ThemeCubit() : super(const ThemeState.initial()); + + void characterSelected(CharacterTheme characterTheme) { + emit(ThemeState(PinballTheme(characterTheme: characterTheme))); + } +} diff --git a/lib/theme/cubit/theme_state.dart b/lib/theme/cubit/theme_state.dart new file mode 100644 index 00000000..13b3ea5f --- /dev/null +++ b/lib/theme/cubit/theme_state.dart @@ -0,0 +1,13 @@ +part of 'theme_cubit.dart'; + +class ThemeState extends Equatable { + const ThemeState(this.theme); + + const ThemeState.initial() + : theme = const PinballTheme(characterTheme: DashTheme()); + + final PinballTheme theme; + + @override + List get props => [theme]; +} diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart new file mode 100644 index 00000000..fcf5d9ee --- /dev/null +++ b/lib/theme/theme.dart @@ -0,0 +1 @@ +export 'cubit/theme_cubit.dart'; diff --git a/packages/pinball_theme/.gitignore b/packages/pinball_theme/.gitignore new file mode 100644 index 00000000..d6130351 --- /dev/null +++ b/packages/pinball_theme/.gitignore @@ -0,0 +1,39 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# VSCode related +.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/packages/pinball_theme/README.md b/packages/pinball_theme/README.md new file mode 100644 index 00000000..e9730e1b --- /dev/null +++ b/packages/pinball_theme/README.md @@ -0,0 +1,11 @@ +# pinball_theme + +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] +[![License: MIT][license_badge]][license_link] + +Package containing themes for pinball game. + +[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg +[license_link]: https://opensource.org/licenses/MIT +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis \ No newline at end of file diff --git a/packages/pinball_theme/analysis_options.yaml b/packages/pinball_theme/analysis_options.yaml new file mode 100644 index 00000000..3742fc3d --- /dev/null +++ b/packages/pinball_theme/analysis_options.yaml @@ -0,0 +1 @@ +include: package:very_good_analysis/analysis_options.2.4.0.yaml \ No newline at end of file diff --git a/packages/pinball_theme/lib/pinball_theme.dart b/packages/pinball_theme/lib/pinball_theme.dart new file mode 100644 index 00000000..0206fa7b --- /dev/null +++ b/packages/pinball_theme/lib/pinball_theme.dart @@ -0,0 +1,4 @@ +library pinball_theme; + +export 'src/pinball_theme.dart'; +export 'src/themes/themes.dart'; diff --git a/packages/pinball_theme/lib/src/pinball_theme.dart b/packages/pinball_theme/lib/src/pinball_theme.dart new file mode 100644 index 00000000..a766a129 --- /dev/null +++ b/packages/pinball_theme/lib/src/pinball_theme.dart @@ -0,0 +1,23 @@ +import 'package:equatable/equatable.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +/// {@template pinball_theme} +/// Defines all theme assets and attributes. +/// +/// Game components should have a getter specified here to load their +/// corresponding assets for the game. +/// {@endtemplate} +class PinballTheme extends Equatable { + /// {@macro pinball_theme} + const PinballTheme({ + required CharacterTheme characterTheme, + }) : _characterTheme = characterTheme; + + final CharacterTheme _characterTheme; + + /// [CharacterTheme] for the chosen character. + CharacterTheme get characterTheme => _characterTheme; + + @override + List get props => [_characterTheme]; +} diff --git a/packages/pinball_theme/lib/src/themes/android_theme.dart b/packages/pinball_theme/lib/src/themes/android_theme.dart new file mode 100644 index 00000000..59c16bd9 --- /dev/null +++ b/packages/pinball_theme/lib/src/themes/android_theme.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +/// {@template android_theme} +/// Defines Android character theme assets and attributes. +/// {@endtemplate} +class AndroidTheme extends CharacterTheme { + /// {@macro android_theme} + const AndroidTheme(); + + @override + Color get ballColor => Colors.green; +} diff --git a/packages/pinball_theme/lib/src/themes/character_theme.dart b/packages/pinball_theme/lib/src/themes/character_theme.dart new file mode 100644 index 00000000..8f81486a --- /dev/null +++ b/packages/pinball_theme/lib/src/themes/character_theme.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +/// {@template character_theme} +/// Base class for creating character themes. +/// +/// Character specific game components should have a getter specified here to +/// load their corresponding assets for the game. +/// {@endtemplate} +abstract class CharacterTheme extends Equatable { + /// {@macro character_theme} + const CharacterTheme(); + + /// Ball color for this theme. + Color get ballColor; + + @override + List get props => [ballColor]; +} diff --git a/packages/pinball_theme/lib/src/themes/dash_theme.dart b/packages/pinball_theme/lib/src/themes/dash_theme.dart new file mode 100644 index 00000000..e4875a11 --- /dev/null +++ b/packages/pinball_theme/lib/src/themes/dash_theme.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +/// {@template dash_theme} +/// Defines Dash character theme assets and attributes. +/// {@endtemplate} +class DashTheme extends CharacterTheme { + /// {@macro dash_theme} + const DashTheme(); + + @override + Color get ballColor => Colors.blue; +} diff --git a/packages/pinball_theme/lib/src/themes/dino_theme.dart b/packages/pinball_theme/lib/src/themes/dino_theme.dart new file mode 100644 index 00000000..07776771 --- /dev/null +++ b/packages/pinball_theme/lib/src/themes/dino_theme.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +/// {@template dino_theme} +/// Defines Dino character theme assets and attributes. +/// {@endtemplate} +class DinoTheme extends CharacterTheme { + /// {@macro dino_theme} + const DinoTheme(); + + @override + Color get ballColor => Colors.grey; +} diff --git a/packages/pinball_theme/lib/src/themes/sparky_theme.dart b/packages/pinball_theme/lib/src/themes/sparky_theme.dart new file mode 100644 index 00000000..5264bad6 --- /dev/null +++ b/packages/pinball_theme/lib/src/themes/sparky_theme.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +/// {@template sparky_theme} +/// Defines Sparky character theme assets and attributes. +/// {@endtemplate} +class SparkyTheme extends CharacterTheme { + /// {@macro sparky_theme} + const SparkyTheme(); + + @override + Color get ballColor => Colors.orange; +} diff --git a/packages/pinball_theme/lib/src/themes/themes.dart b/packages/pinball_theme/lib/src/themes/themes.dart new file mode 100644 index 00000000..d4062a4f --- /dev/null +++ b/packages/pinball_theme/lib/src/themes/themes.dart @@ -0,0 +1,5 @@ +export 'android_theme.dart'; +export 'character_theme.dart'; +export 'dash_theme.dart'; +export 'dino_theme.dart'; +export 'sparky_theme.dart'; diff --git a/packages/pinball_theme/pubspec.yaml b/packages/pinball_theme/pubspec.yaml new file mode 100644 index 00000000..e9b3f215 --- /dev/null +++ b/packages/pinball_theme/pubspec.yaml @@ -0,0 +1,17 @@ +name: pinball_theme +description: Package containing themes for pinball game. +version: 1.0.0+1 +publish_to: none + +environment: + sdk: ">=2.16.0 <3.0.0" + +dependencies: + equatable: ^2.0.3 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + very_good_analysis: ^2.4.0 \ No newline at end of file diff --git a/packages/pinball_theme/test/src/pinball_theme_test.dart b/packages/pinball_theme/test/src/pinball_theme_test.dart new file mode 100644 index 00000000..899eec64 --- /dev/null +++ b/packages/pinball_theme/test/src/pinball_theme_test.dart @@ -0,0 +1,28 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('PinballTheme', () { + const characterTheme = SparkyTheme(); + + test('can be instantiated', () { + expect(PinballTheme(characterTheme: characterTheme), isNotNull); + }); + + test('supports value equality', () { + expect( + PinballTheme(characterTheme: characterTheme), + equals(PinballTheme(characterTheme: characterTheme)), + ); + }); + + test('characterTheme is correct', () { + expect( + PinballTheme(characterTheme: characterTheme).characterTheme, + equals(characterTheme), + ); + }); + }); +} diff --git a/packages/pinball_theme/test/src/themes/android_theme_test.dart b/packages/pinball_theme/test/src/themes/android_theme_test.dart new file mode 100644 index 00000000..a6148042 --- /dev/null +++ b/packages/pinball_theme/test/src/themes/android_theme_test.dart @@ -0,0 +1,21 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('AndroidTheme', () { + test('can be instantiated', () { + expect(AndroidTheme(), isNotNull); + }); + + test('supports value equality', () { + expect(AndroidTheme(), equals(AndroidTheme())); + }); + + test('ballColor is correct', () { + expect(AndroidTheme().ballColor, equals(Colors.green)); + }); + }); +} diff --git a/packages/pinball_theme/test/src/themes/dash_theme_test.dart b/packages/pinball_theme/test/src/themes/dash_theme_test.dart new file mode 100644 index 00000000..0d5c8293 --- /dev/null +++ b/packages/pinball_theme/test/src/themes/dash_theme_test.dart @@ -0,0 +1,21 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('DashTheme', () { + test('can be instantiated', () { + expect(DashTheme(), isNotNull); + }); + + test('supports value equality', () { + expect(DashTheme(), equals(DashTheme())); + }); + + test('ballColor is correct', () { + expect(DashTheme().ballColor, equals(Colors.blue)); + }); + }); +} diff --git a/packages/pinball_theme/test/src/themes/dino_theme_test.dart b/packages/pinball_theme/test/src/themes/dino_theme_test.dart new file mode 100644 index 00000000..6efd8cbd --- /dev/null +++ b/packages/pinball_theme/test/src/themes/dino_theme_test.dart @@ -0,0 +1,21 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('DinoTheme', () { + test('can be instantiated', () { + expect(DinoTheme(), isNotNull); + }); + + test('supports value equality', () { + expect(DinoTheme(), equals(DinoTheme())); + }); + + test('ballColor is correct', () { + expect(DinoTheme().ballColor, equals(Colors.grey)); + }); + }); +} diff --git a/packages/pinball_theme/test/src/themes/sparky_theme_test.dart b/packages/pinball_theme/test/src/themes/sparky_theme_test.dart new file mode 100644 index 00000000..513ca219 --- /dev/null +++ b/packages/pinball_theme/test/src/themes/sparky_theme_test.dart @@ -0,0 +1,21 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('SparkyTheme', () { + test('can be instantiated', () { + expect(SparkyTheme(), isNotNull); + }); + + test('supports value equality', () { + expect(SparkyTheme(), equals(SparkyTheme())); + }); + + test('ballColor is correct', () { + expect(SparkyTheme().ballColor, equals(Colors.orange)); + }); + }); +} diff --git a/pubspec.lock b/pubspec.lock index 5dee0c71..4b375ff7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -140,21 +140,21 @@ packages: name: flame url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-releasecandidate.1" + version: "1.1.0-releasecandidate.2" flame_bloc: dependency: "direct main" description: name: flame_bloc url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-releasecandidate.1" + version: "1.2.0-releasecandidate.2" flame_forge2d: dependency: "direct main" description: name: flame_forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.9.0-releasecandidate.1" + version: "0.9.0-releasecandidate.2" flame_test: dependency: "direct dev" description: @@ -331,6 +331,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + pinball_theme: + dependency: "direct main" + description: + path: "packages/pinball_theme" + relative: true + source: path + version: "1.0.0+1" pool: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1d0c8ada..41b0d081 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,9 +9,9 @@ environment: dependencies: bloc: ^8.0.2 equatable: ^2.0.3 - flame: ^1.1.0-releasecandidate.1 - flame_bloc: ^1.2.0-releasecandidate.1 - flame_forge2d: ^0.9.0-releasecandidate.1 + flame: ^1.1.0-releasecandidate.2 + flame_bloc: ^1.2.0-releasecandidate.2 + flame_forge2d: ^0.9.0-releasecandidate.2 flutter: sdk: flutter flutter_bloc: ^8.0.1 @@ -20,6 +20,8 @@ dependencies: geometry: path: packages/geometry intl: ^0.17.0 + pinball_theme: + path: packages/pinball_theme dev_dependencies: bloc_test: ^9.0.2 @@ -33,3 +35,6 @@ dev_dependencies: flutter: uses-material-design: true generate: true + + assets: + - assets/images/components/ diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index b32d16d5..7ac3ceff 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -79,7 +79,7 @@ void main() { final fixture = ball.body.fixtures[0]; expect(fixture.shape.shapeType, equals(ShapeType.circle)); - expect(fixture.shape.radius, equals(2)); + expect(fixture.shape.radius, equals(1)); }, ); }); diff --git a/test/theme/cubit/theme_cubit_test.dart b/test/theme/cubit/theme_cubit_test.dart new file mode 100644 index 00000000..1f2d24e0 --- /dev/null +++ b/test/theme/cubit/theme_cubit_test.dart @@ -0,0 +1,22 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/theme/theme.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('ThemeCubit', () { + test('initial state has Dash character theme', () { + final themeCubit = ThemeCubit(); + expect(themeCubit.state.theme.characterTheme, equals(const DashTheme())); + }); + + blocTest( + 'charcterSelected emits selected character theme', + build: ThemeCubit.new, + act: (bloc) => bloc.characterSelected(const SparkyTheme()), + expect: () => [ + const ThemeState(PinballTheme(characterTheme: SparkyTheme())), + ], + ); + }); +} diff --git a/test/theme/cubit/theme_state_test.dart b/test/theme/cubit/theme_state_test.dart new file mode 100644 index 00000000..49a2a387 --- /dev/null +++ b/test/theme/cubit/theme_state_test.dart @@ -0,0 +1,19 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/theme/theme.dart'; + +void main() { + group('ThemeState', () { + test('can be instantiated', () { + expect(const ThemeState.initial(), isNotNull); + }); + + test('supports value equality', () { + expect( + ThemeState.initial(), + equals(const ThemeState.initial()), + ); + }); + }); +}