test: tested pinball_components

pull/234/head
alestiago 3 years ago
parent 99689ea4da
commit 25128b614b

@ -16,12 +16,12 @@ class AlienZone extends Blueprint {
components: [
AlienBumper.a(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(-32.52, -9.1),
AlienBumper.b(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(-22.89, -17.35),
],

@ -8,6 +8,6 @@ export 'flutter_forest.dart';
export 'game_flow_controller.dart';
export 'google_word/google_word.dart';
export 'launcher.dart';
export 'scoring_behaviour.dart';
export 'scoring_behavior.dart';
export 'sparky_fire_zone.dart';
export 'wall.dart';

@ -20,22 +20,22 @@ class FlutterForest extends Component
children: [
Signpost(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(8.35, -58.3),
DashNestBumper.main(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(18.55, -59.35),
DashNestBumper.a(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(8.95, -51.95),
DashNestBumper.b(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(23.3, -46.75),
DashAnimatronic()..position = Vector2(20, -66),

@ -1,5 +1,3 @@
import 'dart:async';
import 'package:flame/components.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
@ -8,10 +6,10 @@ import 'package:pinball_flame/pinball_flame.dart';
class GoogleWordBonusBehaviour extends Component
with HasGameRef<PinballGame>, ParentIsA<GoogleWord> {
@override
Future<void> onLoad() async {
await super.onLoad();
final googleLetters = parent.children.whereType<GoogleLetter>();
void onMount() {
super.onMount();
final googleLetters = parent.children.whereType<GoogleLetter>();
for (final letter in googleLetters) {
letter.bloc.stream.listen((_) {
final achievedBonus = googleLetters

@ -1,7 +1,3 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'dart:async';
import 'package:flame/components.dart';
import 'package:pinball/game/components/google_word/behaviors/behaviors.dart';
import 'package:pinball_components/pinball_components.dart';
@ -21,12 +17,7 @@ class GoogleWord extends Component {
GoogleLetter(3)..initialPosition = position + Vector2(2.88, -1.75),
GoogleLetter(4)..initialPosition = position + Vector2(8.33, -0.65),
GoogleLetter(5)..initialPosition = position + Vector2(12.92, 1.82),
GoogleWordBonusBehaviour(),
],
);
@override
Future<void> onLoad() async {
await super.onLoad();
await add(GoogleWordBonusBehaviour());
}
}

@ -9,32 +9,14 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template scoring_behaviour}
///
/// {@endtemplate}
class ScoringBehaviour extends Component
with ContactCallbacks, HasGameRef<PinballGame>, ParentIsA<BodyComponent> {
class ScoringBehavior extends ContactBehavior with HasGameRef<PinballGame> {
/// {@macro scoring_behaviour}
ScoringBehaviour({
ScoringBehavior({
required int points,
}) : _points = points;
final int _points;
@override
Future<void> onLoad() async {
await super.onLoad();
final userData = parent.body.userData;
if (userData is ContactCallbacksGroup) {
userData.addContactCallbacks(this);
} else if (userData is ContactCallbacks) {
final notifier = ContactCallbacksGroup()
..addContactCallbacks(userData)
..addContactCallbacks(this);
parent.body.userData = notifier;
} else {
parent.body.userData = this;
}
}
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);

@ -18,17 +18,17 @@ class SparkyFireZone extends Blueprint {
components: [
SparkyBumper.a(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(-22.9, -41.65),
SparkyBumper.b(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(-21.25, -57.9),
SparkyBumper.c(
children: [
ScoringBehaviour(points: 20),
ScoringBehavior(points: 20),
],
)..initialPosition = Vector2(-3.3, -52.55),
SparkyComputerSensor()..initialPosition = Vector2(-13, -49.8),

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/alien_bumper/behaviors/behaviors.dart';
import 'package:pinball_flame/pinball_flame.dart';
@ -25,14 +26,14 @@ class AlienBumper extends BodyComponent with InitialPosition {
super(
priority: RenderPriority.alienBumper,
children: [
ContactBehavior(),
SpriteBehavior(),
AlienBumperBallContactBehavior(),
AlienBumperBlinkingBehavior(),
_AlienBumperSpriteGroupComponent(
offAssetPath: offAssetPath,
onAssetPath: onAssetPath,
state: bloc.state,
),
if (children != null) ...children,
...?children,
],
renderBody: false,
);
@ -61,11 +62,17 @@ class AlienBumper extends BodyComponent with InitialPosition {
children: children,
);
/// {@macro alien_bumper}
@visibleForTesting
AlienBumper.test({
required this.bloc,
}) : _majorRadius = 3.52,
_minorRadius = 2.97;
final double _majorRadius;
final double _minorRadius;
// TODO(alestiago): Evaluate testing this.
final AlienBumperCubit bloc;
@override

@ -1,4 +1,3 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';

@ -2,16 +2,14 @@ import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template alien_bumper_sprite_behavior}
///
/// {@template alien_bumper_blinking_behavior}
/// Makes a [SparkyBumper] blink back to [SparkyBumperState.active] when
/// [SparkyBumperState.inactive].
/// {@endtemplate}
class SpriteBehavior extends TimerComponent with ParentIsA<AlienBumper> {
/// {@macro alien_bumper_sprite_behavior}
SpriteBehavior()
: super(
period: 0.05,
removeOnFinish: false,
);
class AlienBumperBlinkingBehavior extends TimerComponent
with ParentIsA<AlienBumper> {
/// {@macro alien_bumper_blinking_behavior}
AlienBumperBlinkingBehavior() : super(period: 0.05);
void _onNewState(AlienBumperState state) {
switch (state) {
@ -36,6 +34,6 @@ class SpriteBehavior extends TimerComponent with ParentIsA<AlienBumper> {
@override
void onTick() {
super.onTick();
parent.bloc.onAnimated();
parent.bloc.onBlinked();
}
}

@ -1,2 +1,2 @@
export 'alien_bumper_ball_contact_behaviour.dart';
export 'sprite_behavior.dart';
export 'alien_bumper_ball_contact_behavior.dart';
export 'alien_bumper_blinking_behavior.dart';

@ -9,7 +9,7 @@ class AlienBumperCubit extends Cubit<AlienBumperState> {
emit(AlienBumperState.inactive);
}
void onAnimated() {
void onBlinked() {
emit(AlienBumperState.active);
}
}

@ -27,7 +27,7 @@ class DashNestBumper extends BodyComponent with InitialPosition {
inactiveAssetPath: inactiveAssetPath,
position: spritePosition,
),
if (children != null) ...children,
...?children,
],
renderBody: false,
);

@ -13,16 +13,24 @@ export 'cubit/google_letter_cubit.dart';
/// {@endtemplate}
class GoogleLetter extends BodyComponent with InitialPosition {
/// {@macro google_letter}
GoogleLetter(int index)
: super(
GoogleLetter(
int index, {
GoogleLetterCubit? bloc,
}) : bloc = bloc ?? GoogleLetterCubit(),
super(
children: [
ContactBehavior(),
GoogleLetterBallContactBehavior(),
_GoogleLetterSprite(_GoogleLetterSprite.spritePaths[index])
],
);
// TODO(alestiago): Evaluate testing this.
final GoogleLetterCubit bloc = GoogleLetterCubit();
final GoogleLetterCubit bloc;
@override
void onRemove() {
bloc.close();
super.onRemove();
}
@override
Body createBody() {
@ -57,23 +65,11 @@ class _GoogleLetterSprite extends SpriteComponent
final String _path;
void _onNewState(GoogleLetterState state) {
switch (state) {
case GoogleLetterState.active:
add(_GoogleLetterColorEffect(color: Colors.green));
break;
case GoogleLetterState.inactive:
add(
_GoogleLetterColorEffect(color: Colors.red),
);
break;
}
}
@override
Future<void> onLoad() async {
await super.onLoad();
parent.bloc.stream.listen(_onNewState);
// TODO(alisonryan2002): Make SpriteGroupComponent.
// parent.bloc.stream.listen();
// TODO(alestiago): Used cached assets.
final sprite = await gameRef.loadSprite(_path);
@ -82,13 +78,3 @@ class _GoogleLetterSprite extends SpriteComponent
size = sprite.originalSize / 5;
}
}
class _GoogleLetterColorEffect extends ColorEffect {
_GoogleLetterColorEffect({
required Color color,
}) : super(
color,
const Offset(0, 1),
EffectController(duration: 0.25),
);
}

@ -52,7 +52,7 @@ class Signpost extends BodyComponent with InitialPosition {
priority: RenderPriority.signpost,
children: [
_SignpostSpriteComponent(),
if (children != null) ...children,
...?children,
],
renderBody: false,
);

@ -1,2 +1,2 @@
export 'sparky_bumper_ball_contact_behaviour.dart';
export 'sprite_behavior.dart';
export 'sparky_bumper_ball_contact_behavior.dart';
export 'sparky_bumper_blinking_behavior.dart';

@ -2,16 +2,14 @@ import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template alien_bumper_sprite_behavior}
///
/// {@template sparky_bumper_blinking_behavior}
/// Makes a [SparkyBumper] blink back to [SparkyBumperState.active] when
/// [SparkyBumperState.inactive].
/// {@endtemplate}
class SpriteBehavior extends TimerComponent with ParentIsA<SparkyBumper> {
/// {@macro alien_bumper_sprite_behavior}
SpriteBehavior()
: super(
period: 0.05,
removeOnFinish: false,
);
class SparkyBumperBlinkingBehavior extends TimerComponent
with ParentIsA<SparkyBumper> {
/// {@macro sparky_bumper_sprite_behavior}
SparkyBumperBlinkingBehavior() : super(period: 0.05);
void _onNewState(SparkyBumperState state) {
switch (state) {
@ -36,6 +34,6 @@ class SpriteBehavior extends TimerComponent with ParentIsA<SparkyBumper> {
@override
void onTick() {
super.onTick();
parent.bloc.onAnimated();
parent.bloc.onBlinked();
}
}

@ -7,12 +7,9 @@ class SparkyBumperCubit extends Cubit<SparkyBumperState> {
void onBallContacted() {
emit(SparkyBumperState.inactive);
// Future<void>.delayed(const Duration(milliseconds: 500)).whenComplete(
// () => emit(AlienBumperState.active),
// );
}
void onAnimated() {
void onBlinked() {
emit(SparkyBumperState.active);
}
}

@ -2,6 +2,7 @@ import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/sparky_bumper/behaviors/behaviors.dart';
import 'package:pinball_flame/pinball_flame.dart';
@ -26,15 +27,15 @@ class SparkyBumper extends BodyComponent with InitialPosition {
super(
priority: RenderPriority.sparkyBumper,
children: [
ContactBehavior(),
SpriteBehavior(),
SparkyBumperBallContactBehavior(),
SparkyBumperBlinkingBehaviour(),
_SparkyBumperSpriteGroupComponent(
onAssetPath: onAssetPath,
offAssetPath: offAssetPath,
position: spritePosition,
state: bloc.state,
),
if (children != null) ...children,
...?children,
],
renderBody: false,
);
@ -78,12 +79,24 @@ class SparkyBumper extends BodyComponent with InitialPosition {
children: children,
);
/// {@macro sparky_bumper}
@visibleForTesting
SparkyBumper.test({
required this.bloc,
}) : _majorRadius = 3,
_minorRadius = 2.2;
final double _majorRadius;
final double _minorRadius;
// TODO(alestiago): Evaluate testing this.
final SparkyBumperCubit bloc;
@override
void onRemove() {
bloc.close();
super.onRemove();
}
@override
Body createBody() {
final shape = EllipseShape(

@ -25,6 +25,7 @@ dependencies:
path: ../pinball_theme
dev_dependencies:
bloc_test: ^9.0.3
flame_test: ^1.3.0
flutter_test:
sdk: flutter

@ -16,3 +16,9 @@ class MockGame extends Mock implements Forge2DGame {}
class MockContact extends Mock implements Contact {}
class MockComponent extends Mock implements Component {}
class MockAlienBumperCubit extends Mock implements AlienBumperCubit {}
class MockGoogleLetterCubit extends Mock implements GoogleLetterCubit {}
class MockSparkyBumperCubit extends Mock implements SparkyBumperCubit {}

@ -1,9 +1,12 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/alien_bumper/behaviors/behaviors.dart';
import '../../../helpers/helpers.dart';
@ -19,39 +22,54 @@ void main() {
group('AlienBumper', () {
flameTester.test('"a" loads correctly', (game) async {
final bumper = AlienBumper.a();
await game.ensureAdd(bumper);
expect(game.contains(bumper), isTrue);
final alienBumper = AlienBumper.a();
await game.ensureAdd(alienBumper);
expect(game.contains(alienBumper), isTrue);
});
flameTester.test('"b" loads correctly', (game) async {
final bumper = AlienBumper.b();
await game.ensureAdd(bumper);
expect(game.contains(bumper), isTrue);
final alienBumper = AlienBumper.b();
await game.ensureAdd(alienBumper);
expect(game.contains(alienBumper), isTrue);
});
flameTester.test('animate switches between on and off sprites',
(game) async {
final bumper = AlienBumper.a();
await game.ensureAdd(bumper);
flameTester.test('closes bloc when removed', (game) async {
final bloc = MockAlienBumperCubit();
whenListen(
bloc,
const Stream<AlienBumperState>.empty(),
initialState: AlienBumperState.active,
);
when(bloc.close).thenAnswer((_) async {});
final alienBumper = AlienBumper.test(bloc: bloc);
final spriteGroupComponent = bumper.firstChild<SpriteGroupComponent>()!;
await game.ensureAdd(alienBumper);
game.remove(alienBumper);
await game.ready();
expect(
spriteGroupComponent.current,
equals(AlienBumperState.active),
);
verify(bloc.close).called(1);
});
expect(
spriteGroupComponent.current,
equals(AlienBumperState.inactive),
);
group('adds', () {
flameTester.test('new children', (game) async {
final component = Component();
final alienBumper = AlienBumper.a(
children: [component],
);
await game.ensureAdd(alienBumper);
expect(alienBumper.children, contains(component));
});
expect(
spriteGroupComponent.current,
equals(AlienBumperState.active),
);
flameTester.test('an AlienBumperBallContactBehaviour', (game) async {
final alienBumper = AlienBumper.a();
await game.ensureAdd(alienBumper);
expect(
alienBumper.children
.whereType<AlienBumperBallContactBehavior>()
.single,
isNotNull,
);
});
});
});
}

@ -0,0 +1,51 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/alien_bumper/behaviors/alien_bumper_ball_contact_behavior.dart';
import '../../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
group(
'AlienBumperBallContactBehavior',
() {
test('can be instantiated', () {
expect(
AlienBumperBallContactBehavior(),
isA<AlienBumperBallContactBehavior>(),
);
});
group(
'beginContact',
() {
flameTester.test('emits onBallContacted when contacts with a ball',
(game) async {
final behavior = AlienBumperBallContactBehavior();
final bloc = MockAlienBumperCubit();
whenListen(
bloc,
const Stream<AlienBumperState>.empty(),
initialState: AlienBumperState.active,
);
final alienBumper = AlienBumper.test(bloc: bloc);
await alienBumper.add(behavior);
await game.ensureAdd(alienBumper);
behavior.beginContact(MockBall(), MockContact());
verify(alienBumper.bloc.onBallContacted).called(1);
});
},
);
},
);
}

@ -0,0 +1,45 @@
import 'dart:async';
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/alien_bumper/behaviors/alien_bumper_blinking_behavior.dart';
import '../../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
group(
'AlienBumperBlinkingBehaviour',
() {
flameTester.test(
'calls onBlinked after 0.5 seconds when inactive',
(game) async {
// TODO(alestiago): Make this pass.
final behavior = AlienBumperBlinkingBehavior();
final bloc = MockAlienBumperCubit();
final streamController =
StreamController<AlienBumperState>.broadcast();
whenListen(
bloc,
streamController.stream,
initialState: AlienBumperState.active,
);
final alienBumper = AlienBumper.test(bloc: bloc);
await alienBumper.add(behavior);
await game.ensureAdd(alienBumper);
streamController.sink.add(AlienBumperState.inactive);
game.update(0.05);
verify(bloc.onBlinked).called(1);
},
);
},
);
}

@ -0,0 +1,24 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
void main() {
group(
'AlienBumperCubit',
() {
blocTest<AlienBumperCubit, AlienBumperState>(
'onBallContacted emits inactive',
build: AlienBumperCubit.new,
act: (bloc) => bloc.onBallContacted(),
expect: () => [AlienBumperState.inactive],
);
blocTest<AlienBumperCubit, AlienBumperState>(
'onBlinked emits active',
build: AlienBumperCubit.new,
act: (bloc) => bloc.onBlinked(),
expect: () => [AlienBumperState.active],
);
},
);
}

@ -73,5 +73,14 @@ void main() {
equals(DashNestBumperSpriteState.inactive),
);
});
flameTester.test('adds new children', (game) async {
final component = Component();
final dashNestBumper = DashNestBumper.a(
children: [component],
);
await game.ensureAdd(dashNestBumper);
expect(dashNestBumper.children, contains(component));
});
});
}

@ -0,0 +1,51 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/google_letter/behaviors/google_letter_ball_contact_behavior.dart';
import '../../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
group(
'GoogleLetterBallContactBehavior',
() {
test('can be instantiated', () {
expect(
GoogleLetterBallContactBehavior(),
isA<GoogleLetterBallContactBehavior>(),
);
});
group(
'beginContact',
() {
flameTester.test('emits onBallContacted when contacts with a ball',
(game) async {
final behavior = GoogleLetterBallContactBehavior();
final bloc = MockGoogleLetterCubit();
whenListen(
bloc,
const Stream<GoogleLetterState>.empty(),
initialState: GoogleLetterState.active,
);
final googleLetter = GoogleLetter(0, bloc: bloc);
await googleLetter.add(behavior);
await game.ensureAdd(googleLetter);
behavior.beginContact(MockBall(), MockContact());
verify(googleLetter.bloc.onBallContacted).called(1);
});
},
);
},
);
}

@ -0,0 +1,24 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
void main() {
group(
'GoogleLetterBumperCubit',
() {
blocTest<GoogleLetterCubit, GoogleLetterState>(
'onBallContacted emits active',
build: GoogleLetterCubit.new,
act: (bloc) => bloc.onBallContacted(),
expect: () => [GoogleLetterState.active],
);
blocTest<GoogleLetterCubit, GoogleLetterState>(
'onReset emits inactive',
build: GoogleLetterCubit.new,
act: (bloc) => bloc.onReset(),
expect: () => [GoogleLetterState.inactive],
);
},
);
}

@ -1,8 +1,11 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/google_letter/behaviors/behaviors.dart';
import '../../../helpers/helpers.dart';
@ -81,5 +84,33 @@ void main() {
expect(() => GoogleLetter(-1), throwsA(isA<RangeError>()));
expect(() => GoogleLetter(6), throwsA(isA<RangeError>()));
});
flameTester.test('closes bloc when removed', (game) async {
final bloc = MockGoogleLetterCubit();
whenListen(
bloc,
const Stream<GoogleLetterState>.empty(),
initialState: GoogleLetterState.active,
);
when(bloc.close).thenAnswer((_) async {});
final googleLetter = GoogleLetter(0, bloc: bloc);
await game.ensureAdd(googleLetter);
game.remove(googleLetter);
await game.ready();
verify(bloc.close).called(1);
});
flameTester.test('adds a GoogleLetterBallContactBehaviour', (game) async {
final googleLetter = GoogleLetter(0);
await game.ensureAdd(googleLetter);
expect(
googleLetter.children
.whereType<GoogleLetterBallContactBehavior>()
.single,
isNotNull,
);
});
});
}

@ -108,7 +108,7 @@ void main() {
});
});
group('LayerSensorBallContactCallback', () {
group('beginContact', () {
late Ball ball;
late Body body;
@ -133,12 +133,14 @@ void main() {
when(() => body.linearVelocity).thenReturn(Vector2(0, -1));
sensor.beginContact(ball, MockContact());
verify(() => ball.layer = sensor.insideLayer).called(1);
verify(() => ball.priority = sensor.insidePriority).called(1);
verify(ball.reorderChildren).called(1);
when(() => ball.layer).thenReturn(sensor.insideLayer);
sensor.beginContact(ball, MockContact());
verify(() => ball.layer = Layer.board);
verify(() => ball.priority = RenderPriority.ballOnBoard).called(1);
verify(ball.reorderChildren).called(1);
@ -156,12 +158,14 @@ void main() {
when(() => body.linearVelocity).thenReturn(Vector2(0, 1));
sensor.beginContact(ball, MockContact());
verify(() => ball.layer = sensor.insideLayer).called(1);
verify(() => ball.priority = sensor.insidePriority).called(1);
verify(ball.reorderChildren).called(1);
when(() => ball.layer).thenReturn(sensor.insideLayer);
sensor.beginContact(ball, MockContact());
verify(() => ball.layer = Layer.board);
verify(() => ball.priority = RenderPriority.ballOnBoard).called(1);
verify(ball.reorderChildren).called(1);

@ -151,5 +151,14 @@ void main() {
expect(spriteComponent.current, SignpostSpriteState.inactive);
},
);
flameTester.test('adds new children', (game) async {
final component = Component();
final signpost = Signpost(
children: [component],
);
await game.ensureAdd(signpost);
expect(signpost.children, contains(component));
});
});
}

@ -0,0 +1,51 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/sparky_bumper/behaviors/sparky_bumper_ball_contact_behavior.dart';
import '../../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
group(
'AlienBumperBallContactBehavior',
() {
test('can be instantiated', () {
expect(
SparkyBumperBallContactBehavior(),
isA<SparkyBumperBallContactBehavior>(),
);
});
group(
'beginContact',
() {
flameTester.test('emits onBallContacted when contacts with a ball',
(game) async {
final behavior = SparkyBumperBallContactBehavior();
final bloc = MockSparkyBumperCubit();
whenListen(
bloc,
const Stream<SparkyBumperState>.empty(),
initialState: SparkyBumperState.active,
);
final sparkyBumper = SparkyBumper.test(bloc: bloc);
await sparkyBumper.add(behavior);
await game.ensureAdd(sparkyBumper);
behavior.beginContact(MockBall(), MockContact());
verify(sparkyBumper.bloc.onBallContacted).called(1);
});
},
);
},
);
}

@ -0,0 +1,45 @@
import 'dart:async';
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/sparky_bumper/behaviors/sparky_bumper_blinking_behavior.dart';
import '../../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(TestGame.new);
group(
'SparkyBumperBlinkingBehaviour',
() {
flameTester.test(
'calls onBlinked after 0.5 seconds when inactive',
(game) async {
// TODO(alestiago): Make this pass.
final behavior = SparkyBumperBlinkingBehavior();
final bloc = MockSparkyBumperCubit();
final streamController =
StreamController<SparkyBumperState>.broadcast();
whenListen(
bloc,
streamController.stream,
initialState: SparkyBumperState.active,
);
final sparkyBumper = SparkyBumper.test(bloc: bloc);
await sparkyBumper.add(behavior);
await game.ensureAdd(sparkyBumper);
streamController.sink.add(SparkyBumperState.inactive);
game.update(0.05);
verify(bloc.onBlinked).called(1);
},
);
},
);
}

@ -0,0 +1,24 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
void main() {
group(
'SparkyBumperCubit',
() {
blocTest<SparkyBumperCubit, SparkyBumperState>(
'onBallContacted emits inactive',
build: SparkyBumperCubit.new,
act: (bloc) => bloc.onBallContacted(),
expect: () => [SparkyBumperState.inactive],
);
blocTest<SparkyBumperCubit, SparkyBumperState>(
'onBlinked emits active',
build: SparkyBumperCubit.new,
act: (bloc) => bloc.onBlinked(),
expect: () => [SparkyBumperState.active],
);
},
);
}

@ -1,8 +1,12 @@
// ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart';
import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/sparky_bumper/behaviors/behaviors.dart';
import '../../../helpers/helpers.dart';
@ -20,22 +24,60 @@ void main() {
group('SparkyBumper', () {
flameTester.test('"a" loads correctly', (game) async {
final bumper = SparkyBumper.a();
await game.ensureAdd(bumper);
expect(game.contains(bumper), isTrue);
final sparkyBumper = SparkyBumper.a();
await game.ensureAdd(sparkyBumper);
expect(game.contains(sparkyBumper), isTrue);
});
flameTester.test('"b" loads correctly', (game) async {
final bumper = SparkyBumper.b();
await game.ensureAdd(bumper);
expect(game.contains(bumper), isTrue);
final sparkyBumper = SparkyBumper.b();
await game.ensureAdd(sparkyBumper);
expect(game.contains(sparkyBumper), isTrue);
});
flameTester.test('"c" loads correctly', (game) async {
final bumper = SparkyBumper.c();
await game.ensureAdd(bumper);
expect(game.contains(bumper), isTrue);
final sparkyBumper = SparkyBumper.c();
await game.ensureAdd(sparkyBumper);
expect(game.contains(sparkyBumper), isTrue);
});
flameTester.test('closes bloc when removed', (game) async {
final bloc = MockSparkyBumperCubit();
whenListen(
bloc,
const Stream<SparkyBumperState>.empty(),
initialState: SparkyBumperState.active,
);
when(bloc.close).thenAnswer((_) async {});
final sparkyBumper = SparkyBumper.test(bloc: bloc);
await game.ensureAdd(sparkyBumper);
game.remove(sparkyBumper);
await game.ready();
verify(bloc.close).called(1);
});
group('adds', () {
flameTester.test('new children', (game) async {
final component = Component();
final sparkyBumper = SparkyBumper.a(
children: [component],
);
await game.ensureAdd(sparkyBumper);
expect(sparkyBumper.children, contains(component));
});
flameTester.test('an SparkyBumperBallContactBehaviour', (game) async {
final sparkyBumper = SparkyBumper.a();
await game.ensureAdd(sparkyBumper);
expect(
sparkyBumper.children
.whereType<SparkyBumperBallContactBehavior>()
.single,
isNotNull,
);
});
});
});
}

@ -3,16 +3,16 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball_flame/pinball_flame.dart';
class ContactBehavior<T extends BodyComponent> extends Component
abstract class ContactBehavior<T extends BodyComponent> extends Component
with ContactCallbacks, ParentIsA<T> {
@override
@mustCallSuper
Future<void> onLoad() async {
final userData = parent.body.userData;
if (userData is ContactCallbacksGroup) {
if (userData is _ContactCallbacksGroup) {
userData.addContactCallbacks(this);
} else if (userData is ContactCallbacks) {
final notifier = ContactCallbacksGroup()
final notifier = _ContactCallbacksGroup()
..addContactCallbacks(userData)
..addContactCallbacks(this);
parent.body.userData = notifier;
@ -22,7 +22,7 @@ class ContactBehavior<T extends BodyComponent> extends Component
}
}
class ContactCallbacksGroup implements ContactCallbacks {
class _ContactCallbacksGroup implements ContactCallbacks {
final List<ContactCallbacks> _contactCallbacks = [];
@override

@ -9,8 +9,7 @@ mixin ParentIsA<T extends Component> on Component {
T get parent => super.parent! as T;
@override
Future<void>? addToParent(Component parent) {
assert(parent is T, 'Parent must be of type $T');
Future<void>? addToParent(covariant T parent) {
return super.addToParent(parent);
}
}

@ -47,7 +47,7 @@ void main() {
'emits Scored event with points',
setUp: (game, tester) async {
const points = 20;
final scoringBehaviour = ScoringBehaviour(points: points);
final scoringBehaviour = ScoringBehavior(points: points);
await game.ensureAdd(scoringBehaviour);
scoringBehaviour.beginContact(ball, MockContact());
@ -64,7 +64,7 @@ void main() {
'plays score sound',
setUp: (game, tester) async {
const points = 20;
final scoringBehaviour = ScoringBehaviour(points: points);
final scoringBehaviour = ScoringBehavior(points: points);
await game.ensureAdd(scoringBehaviour);
scoringBehaviour.beginContact(ball, MockContact());
@ -77,7 +77,7 @@ void main() {
"adds a ScoreText component at Ball's position with points",
setUp: (game, tester) async {
const points = 20;
final scoringBehaviour = ScoringBehaviour(points: points);
final scoringBehaviour = ScoringBehavior(points: points);
await game.ensureAdd(scoringBehaviour);
scoringBehaviour.beginContact(ball, MockContact());

Loading…
Cancel
Save