added comments and integration test

main
plush2408 2024-06-18 01:46:57 +02:00
parent f6fa31196e
commit 5ebe14df32
12 changed files with 290 additions and 77 deletions

View File

@ -8,15 +8,16 @@ void main() {
); );
} }
// Main application widget
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
theme: halloweenTheme, theme: halloweenTheme, // Apply custom theme
home: const PlayerRegistry(), home: const PlayerRegistry(), // Set initial screen to PlayerRegistry
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false, // Disable debug banner
); );
} }
} }

View File

@ -1,14 +1,22 @@
import 'package:werwolf/models/role.dart'; import 'package:werwolf/models/role.dart';
import 'player.dart'; import 'player.dart';
class Game { class Game {
// List to store player objects
List<Player> players = []; List<Player> players = [];
// List to store player names
List playernames = []; List playernames = [];
// Initial number of werewolves
int numWolves = 1; int numWolves = 1;
// Map to store special roles and their activation status
Map specialRoles = <Role, bool>{}; Map specialRoles = <Role, bool>{};
// Constructor to initialize the game with player names
Game({required this.playernames}) { Game({required this.playernames}) {
// Initialize specialRoles map, setting all special roles to false
for (Role role in Role.values) { for (Role role in Role.values) {
if (role != Role.dorfbewohner && role != Role.werwolf) { if (role != Role.dorfbewohner && role != Role.werwolf) {
specialRoles[role] = false; specialRoles[role] = false;
@ -16,43 +24,64 @@ class Game {
} }
} }
// Method to increment the number of werewolves
void incrementWolves() { void incrementWolves() {
// Increment only if conditions are met: enough players and balance maintained
if (numWolves < playernames.length - 1 && if (numWolves < playernames.length - 1 &&
(playernames.length) >= ((numWolves + 1) * 3)) { (playernames.length) >= ((numWolves + 1) * 3)) {
numWolves++; numWolves++;
} }
} }
// Method to decrement the number of werewolves
void decrementWolves() { void decrementWolves() {
// Decrement only if there's more than one werewolf
if (numWolves > 1) { if (numWolves > 1) {
numWolves--; numWolves--;
} }
} }
// Method to get the current number of werewolves
int getWolves() { int getWolves() {
return numWolves; return numWolves;
} }
// Method to get all players with their assigned roles
List<Player> getAllPlayers() { List<Player> getAllPlayers() {
// Clear the players list to start fresh
players.clear(); players.clear();
// List to hold roles randomly assigned to players
List<Role> randomRoles = []; List<Role> randomRoles = [];
// Add werewolf roles to the list
for (var i = 0; i < numWolves; i++) { for (var i = 0; i < numWolves; i++) {
randomRoles.add(Role.werwolf); randomRoles.add(Role.werwolf);
} }
// Add active special roles to the list
for (var specialRole in specialRoles.keys) { for (var specialRole in specialRoles.keys) {
if (specialRoles[specialRole]) { if (specialRoles[specialRole]) {
randomRoles.add(specialRole); randomRoles.add(specialRole);
} }
} }
// Fill the remaining roles with dorfbewohner
for (var i = randomRoles.length; i < playernames.length; i++) { for (var i = randomRoles.length; i < playernames.length; i++) {
randomRoles.add(Role.dorfbewohner); randomRoles.add(Role.dorfbewohner);
} }
// Shuffle the roles to ensure randomness
randomRoles.shuffle(); randomRoles.shuffle();
// Assign roles to players and create Player objects
for (var playerName in playernames) { for (var playerName in playernames) {
players players
.add(Player(name: playerName, role: randomRoles.last, isDead: false)); .add(Player(name: playerName, role: randomRoles.last, isDead: false));
randomRoles.removeLast(); randomRoles.removeLast();
} }
// Return the list of players with their roles
return players; return players;
} }
} }

View File

@ -1,9 +1,14 @@
import 'package:werwolf/models/role.dart'; import 'package:werwolf/models/role.dart';
// Define a class named Player
class Player { class Player {
// Declare a string variable to store the player's name
String name; String name;
// Declare a variable of type Role to store the player's role
Role role; Role role;
// Declare a boolean variable to indicate whether the player is dead
bool isDead; bool isDead;
// Constructor for the Player class with required parameters for name, role, and isDead status
Player({required this.name, required this.role, required this.isDead}); Player({required this.name, required this.role, required this.isDead});
} }

View File

@ -1,18 +1,21 @@
// Enum representing different roles in the game
enum Role { dorfbewohner, werwolf, joker, seher, doctor } enum Role { dorfbewohner, werwolf, joker, seher, doctor }
// Extension on the Role enum to provide string representations
extension RoleExtension on Role { extension RoleExtension on Role {
// Getter to convert enum value to its string representation
String get stringValue { String get stringValue {
switch (this) { switch (this) {
case Role.dorfbewohner: case Role.dorfbewohner:
return 'Dorfbewohner'; return 'Dorfbewohner'; // Returns 'Dorfbewohner' for the dorfbewohner role
case Role.werwolf: case Role.werwolf:
return 'Werwolf'; return 'Werwolf'; // Returns 'Werwolf' for the werwolf role
case Role.joker: case Role.joker:
return 'Joker'; return 'Joker'; // Returns 'Joker' for the joker role
case Role.seher: case Role.seher:
return 'Seher'; return 'Seher'; // Returns 'Seher' for the seher role
case Role.doctor: case Role.doctor:
return 'Doctor'; return 'Doctor'; // Returns 'Doctor' for the doctor role
} }
} }
} }

View File

@ -7,6 +7,7 @@ import 'package:werwolf/screens/gameboard.dart';
import '../models/player.dart'; import '../models/player.dart';
import '../models/role.dart'; import '../models/role.dart';
// FlipingCard is a StatefulWidget that takes a list of players as input
class FlipingCard extends StatefulWidget { class FlipingCard extends StatefulWidget {
final List<Player> players; final List<Player> players;
const FlipingCard({required this.players, super.key}); const FlipingCard({required this.players, super.key});
@ -15,17 +16,19 @@ class FlipingCard extends StatefulWidget {
State<FlipingCard> createState() => _FlipingCardState(); State<FlipingCard> createState() => _FlipingCardState();
} }
// State class for FlipingCard
class _FlipingCardState extends State<FlipingCard> { class _FlipingCardState extends State<FlipingCard> {
int index = 0; int index = 0; // Index to keep track of the current player
late FlipCardController _controller; late FlipCardController _controller; // Controller for the flip card
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller = FlipCardController(); _controller = FlipCardController(); // Initialize the flip card controller
} }
// Method to render the content of the flip card
_renderContent(context) { _renderContent(context) {
return Card( return Card(
elevation: 0.0, elevation: 0.0,
@ -56,7 +59,7 @@ class _FlipingCardState extends State<FlipingCard> {
), ),
), ),
const Text( const Text(
'Klick um deine Rolle zu sehen!', 'Click to see your role!',
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@ -124,14 +127,14 @@ class _FlipingCardState extends State<FlipingCard> {
onPressed: () { onPressed: () {
setState(() { setState(() {
if (index > 0 && index <= widget.players.length) { if (index > 0 && index <= widget.players.length) {
index--; index--; // Go to the previous player
if (!_controller.state!.isFront) { if (!_controller.state!.isFront) {
_controller.toggleCardWithoutAnimation(); _controller.toggleCardWithoutAnimation();
} }
} }
}); });
}, },
child: const Text("Zurück"), child: const Text("Back"),
), ),
), ),
Flexible( Flexible(
@ -139,11 +142,12 @@ class _FlipingCardState extends State<FlipingCard> {
onPressed: () { onPressed: () {
setState(() { setState(() {
if (index >= 0 && index < widget.players.length - 1) { if (index >= 0 && index < widget.players.length - 1) {
index++; index++; // Go to the next player
if (!_controller.state!.isFront) { if (!_controller.state!.isFront) {
_controller.toggleCardWithoutAnimation(); _controller.toggleCardWithoutAnimation();
} }
} else if (index == widget.players.length - 1) { } else if (index == widget.players.length - 1) {
// Navigate to the game board if it's the last player
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
@ -155,8 +159,8 @@ class _FlipingCardState extends State<FlipingCard> {
}); });
}, },
child: Text(index != widget.players.length - 1 child: Text(index != widget.players.length - 1
? "Nächster Spieler" ? "Next Player"
: "Spiel anfangen!"), : "Start Game!"),
), ),
), ),
], ],

View File

@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../models/player.dart'; import '../models/player.dart';
import '../models/role.dart'; import '../models/role.dart';
// Main widget for displaying the player grid
class PlayerGridView extends StatefulWidget { class PlayerGridView extends StatefulWidget {
final List<Player> players; final List<Player> players;
@ -14,7 +15,7 @@ class PlayerGridView extends StatefulWidget {
} }
class _PlayerGridViewState extends State<PlayerGridView> { class _PlayerGridViewState extends State<PlayerGridView> {
bool isNight = true; bool isNight = true; // Variable to track whether it is night or day
@override @override
void initState() { void initState() {
@ -35,7 +36,7 @@ class _PlayerGridViewState extends State<PlayerGridView> {
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Text( Text(
isNight ? 'Nacht' : 'Tag', isNight ? 'Nacht' : 'Tag', // Display whether it is night or day
), ),
], ],
), ),
@ -44,21 +45,21 @@ class _PlayerGridViewState extends State<PlayerGridView> {
IconButton( IconButton(
icon: const Icon(Icons.info), icon: const Icon(Icons.info),
onPressed: () { onPressed: () {
_showRolesDialog(); _showRolesDialog(); // Show dialog with player roles
}, },
), ),
], ],
leading: IconButton( leading: IconButton(
icon: const Icon(FontAwesomeIcons.xmark), icon: const Icon(FontAwesomeIcons.xmark),
onPressed: () { onPressed: () {
Navigator.popUntil(context, ModalRoute.withName('/')); Navigator.popUntil(context, ModalRoute.withName('/')); // Return to main screen
}, },
), ),
), ),
body: Container( body: Container(
color: isNight color: isNight
? const Color(0xff2d2d2d) ? const Color(0xff2d2d2d)
: const Color.fromARGB(255, 194, 216, 225), : const Color.fromARGB(255, 194, 216, 225), // Set background color based on night/day
padding: const EdgeInsets.only(left: 15, right: 15), padding: const EdgeInsets.only(left: 15, right: 15),
child: Column( child: Column(
children: [ children: [
@ -66,7 +67,7 @@ class _PlayerGridViewState extends State<PlayerGridView> {
child: GridView.builder( child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount:
MediaQuery.of(context).size.shortestSide < 600 ? 2 : 4, MediaQuery.of(context).size.shortestSide < 600 ? 2 : 4, // Adjust grid based on screen size
mainAxisSpacing: 10, mainAxisSpacing: 10,
crossAxisSpacing: 10, crossAxisSpacing: 10,
), ),
@ -78,14 +79,14 @@ class _PlayerGridViewState extends State<PlayerGridView> {
onTap: () { onTap: () {
setState(() { setState(() {
if (!widget.players[index].isDead) { if (!widget.players[index].isDead) {
_killPlayer(widget.players[index]); _killPlayer(widget.players[index]); // Mark player as dead
} }
}); });
}, },
child: Card( child: Card(
color: widget.players[index].isDead color: widget.players[index].isDead
? Colors.grey ? Colors.grey
: Theme.of(context).colorScheme.primary, : Theme.of(context).colorScheme.primary, // Change card color based on player status
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -98,7 +99,7 @@ class _PlayerGridViewState extends State<PlayerGridView> {
), ),
if (widget.players[index].isDead) if (widget.players[index].isDead)
const Icon(Icons.close, const Icon(Icons.close,
color: Colors.red, size: 48), color: Colors.red, size: 48), // Show icon if player is dead
], ],
), ),
), ),
@ -113,7 +114,7 @@ class _PlayerGridViewState extends State<PlayerGridView> {
Padding( Padding(
padding: const EdgeInsets.only(bottom: 60, top: 10), padding: const EdgeInsets.only(bottom: 60, top: 10),
child: ElevatedButton( child: ElevatedButton(
onPressed: _changePhase, onPressed: _changePhase, // Change phase between night and day
child: Text(isNight ? 'Tag skippen' : 'Nacht skippen'), child: Text(isNight ? 'Tag skippen' : 'Nacht skippen'),
), ),
), ),
@ -127,14 +128,14 @@ class _PlayerGridViewState extends State<PlayerGridView> {
if (isNight) { if (isNight) {
if (player.role != Role.werwolf) { if (player.role != Role.werwolf) {
player.isDead = true; player.isDead = true;
_checkWinCondition(); _checkWinCondition(); // Check win condition after killing player
isNight = false; isNight = false;
} }
} else { } else {
player.isDead = true; player.isDead = true;
_checkWinCondition(); _checkWinCondition(); // Check win condition after killing player
if (player.role == Role.joker) { if (player.role == Role.joker) {
_showWinDialog('Der Joker hat gewonnen!'); _showWinDialog('Der Joker hat gewonnen!'); // Show win dialog for Joker
} }
isNight = true; isNight = true;
} }
@ -142,11 +143,7 @@ class _PlayerGridViewState extends State<PlayerGridView> {
} }
void _changePhase() { void _changePhase() {
if (isNight) { isNight = !isNight; // Toggle between night and day
isNight = false;
} else {
isNight = true;
}
setState(() {}); setState(() {});
} }
@ -159,9 +156,9 @@ class _PlayerGridViewState extends State<PlayerGridView> {
.length; .length;
if (countWolves == 0) { if (countWolves == 0) {
_showWinDialog('Die Dorfbewohner haben gewonnen!'); _showWinDialog('Die Dorfbewohner haben gewonnen!'); // Show win dialog for villagers
} else if (countWolves >= countVillagers) { } else if (countWolves >= countVillagers) {
_showWinDialog('Die Werwölfe haben gewonnen!'); _showWinDialog('Die Werwölfe haben gewonnen!'); // Show win dialog for werewolves
} }
} }
@ -176,7 +173,7 @@ class _PlayerGridViewState extends State<PlayerGridView> {
TextButton( TextButton(
child: const Text('Spiel beenden'), child: const Text('Spiel beenden'),
onPressed: () { onPressed: () {
Navigator.popUntil(context, ModalRoute.withName('/')); Navigator.popUntil(context, ModalRoute.withName('/')); // Return to main screen
}, },
), ),
], ],
@ -217,7 +214,7 @@ class _PlayerGridViewState extends State<PlayerGridView> {
TextButton( TextButton(
child: const Text('OK'), child: const Text('OK'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop(); // Close dialog
}, },
), ),
], ],

View File

@ -1,25 +1,28 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:werwolf/screens/settings.dart'; import 'package:werwolf/screens/settings.dart';
// Define the PlayerRegistry StatefulWidget
class PlayerRegistry extends StatefulWidget { class PlayerRegistry extends StatefulWidget {
const PlayerRegistry({super.key}); const PlayerRegistry({super.key});
@override @override
// Create the state for the PlayerRegistry widget
// ignore: library_private_types_in_public_api // ignore: library_private_types_in_public_api
_PlayerRegistryState createState() => _PlayerRegistryState(); _PlayerRegistryState createState() => _PlayerRegistryState();
} }
// Define the state for the PlayerRegistry widget
class _PlayerRegistryState extends State<PlayerRegistry> { class _PlayerRegistryState extends State<PlayerRegistry> {
final TextEditingController _playerController = TextEditingController(); final TextEditingController _playerController = TextEditingController(); // Controller for player name input
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>(); // Key for form validation
String _errorMessage = ""; String _errorMessage = ""; // Error message string
List<String> playernames = []; List<String> playernames = []; // List to store player names
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("Werwolf"), title: const Text("Werwolf"), // App bar title
centerTitle: true, centerTitle: true,
), ),
body: Padding( body: Padding(
@ -32,8 +35,8 @@ class _PlayerRegistryState extends State<PlayerRegistry> {
key: _formKey, key: _formKey,
controller: _playerController, controller: _playerController,
decoration: InputDecoration( decoration: InputDecoration(
errorText: _errorMessage == "" ? null : _errorMessage, errorText: _errorMessage == "" ? null : _errorMessage, // Display error message if exists
labelText: 'Spielername', labelText: 'Spielername', // Label for text field
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary), color: Theme.of(context).colorScheme.primary),
@ -54,14 +57,14 @@ class _PlayerRegistryState extends State<PlayerRegistry> {
onSubmitted: (value) { onSubmitted: (value) {
setState(() { setState(() {
if (_playerController.text.isEmpty) { if (_playerController.text.isEmpty) {
_errorMessage = "Spielername ist leer!"; _errorMessage = "Spielername ist leer!"; // Error for empty name
} else if (playernames.contains(value)) { } else if (playernames.contains(value)) {
_errorMessage = "Dieser Spieler existiert bereits"; _errorMessage = "Dieser Spieler existiert bereits"; // Error for duplicate name
_playerController.clear(); _playerController.clear();
} else { } else {
_errorMessage = ""; _errorMessage = "";
_playerController.clear(); _playerController.clear();
playernames.add(value); playernames.add(value); // Add valid player name to list
} }
}); });
}, },
@ -73,15 +76,15 @@ class _PlayerRegistryState extends State<PlayerRegistry> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text( title: Text(
playernames[index], playernames[index], // Display player name
), ),
trailing: IconButton( trailing: IconButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
playernames.remove(playernames[index]); playernames.remove(playernames[index]); // Remove player name from list
}); });
}, },
icon: const Icon(Icons.remove)), icon: const Icon(Icons.remove)), // Remove icon button
); );
}, },
), ),
@ -93,16 +96,16 @@ class _PlayerRegistryState extends State<PlayerRegistry> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => GameSettings( builder: (context) => GameSettings(
playernames: playernames, playernames: playernames, // Pass player names to next screen
), ),
)); ));
} else { } else {
setState(() { setState(() {
_errorMessage = "Es müssen mindestens 6 Spieler sein!"; _errorMessage = "Es müssen mindestens 6 Spieler sein!"; // Error for not enough players
}); });
} }
}, },
child: const Text('Spiel einstellen'), child: const Text('Spiel einstellen'), // Button to start game settings
), ),
const Padding(padding: EdgeInsets.all(30)) const Padding(padding: EdgeInsets.all(30))
], ],

View File

@ -4,6 +4,7 @@ import 'package:werwolf/screens/flippingcards.dart';
import '../models/game.dart'; import '../models/game.dart';
import '../models/role.dart'; import '../models/role.dart';
// Main widget for game settings
class GameSettings extends StatefulWidget { class GameSettings extends StatefulWidget {
final List<String> playernames; final List<String> playernames;
const GameSettings({required this.playernames, super.key}); const GameSettings({required this.playernames, super.key});
@ -17,7 +18,7 @@ class _GameSettingsState extends State<GameSettings> {
@override @override
void initState() { void initState() {
game = Game(playernames: widget.playernames); game = Game(playernames: widget.playernames); // Initialize the game with player names
super.initState(); super.initState();
} }
@ -27,7 +28,7 @@ class _GameSettingsState extends State<GameSettings> {
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
"Werwolf", "Werwolf",
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge, // Use large title style from theme
), ),
centerTitle: true, centerTitle: true,
), ),
@ -36,8 +37,8 @@ class _GameSettingsState extends State<GameSettings> {
child: Column( child: Column(
children: [ children: [
Text( Text(
"Anzahl der Spieler ${widget.playernames.length}", "Anzahl der Spieler ${widget.playernames.length}", // Display number of players
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge, // Use large title style from theme
), ),
const Divider( const Divider(
height: 60, height: 60,
@ -47,14 +48,13 @@ class _GameSettingsState extends State<GameSettings> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( Text(
"Anzahl der Werwölfe ${game.getWolves()}", "Anzahl der Werwölfe ${game.getWolves()}", // Display number of werewolves
style: style: Theme.of(context).textTheme.bodyLarge, // Use large body style from theme
Theme.of(context).textTheme.bodyLarge,
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
game.decrementWolves(); game.decrementWolves(); // Decrease the number of werewolves
}); });
}, },
icon: const Icon(Icons.remove, color: Colors.white), icon: const Icon(Icons.remove, color: Colors.white),
@ -62,7 +62,7 @@ class _GameSettingsState extends State<GameSettings> {
IconButton( IconButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
game.incrementWolves(); game.incrementWolves(); // Increase the number of werewolves
}); });
}, },
icon: const Icon(Icons.add, color: Colors.white), icon: const Icon(Icons.add, color: Colors.white),
@ -75,9 +75,8 @@ class _GameSettingsState extends State<GameSettings> {
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 20), padding: const EdgeInsets.only(top: 8.0, bottom: 20),
child: Text( child: Text(
"Spezielle Rollen", "Spezielle Rollen", // Display special roles section
style: style: Theme.of(context).textTheme.titleLarge, // Use large title style from theme
Theme.of(context).textTheme.titleLarge,
), ),
), ),
Expanded( Expanded(
@ -89,22 +88,20 @@ class _GameSettingsState extends State<GameSettings> {
Role role = Role.values[index]; Role role = Role.values[index];
return ListTile( return ListTile(
title: Text( title: Text(
role.stringValue, role.stringValue, // Display role name
style: Theme.of(context) style: Theme.of(context).textTheme.bodyLarge, // Use large body style from theme
.textTheme
.bodyLarge,
), ),
trailing: Switch( trailing: Switch(
value: game.specialRoles[role], value: game.specialRoles[role],
onChanged: (bool value) { onChanged: (bool value) {
setState(() { setState(() {
game.specialRoles[role] = value; game.specialRoles[role] = value; // Toggle special role
}); });
}, },
), ),
); );
} }
return Container(); return Container(); // Return empty container if not a special role
}, },
), ),
), ),
@ -113,13 +110,12 @@ class _GameSettingsState extends State<GameSettings> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => FlipingCard(players: game.getAllPlayers()), // Start game
FlipingCard(players: game.getAllPlayers()),
), ),
); );
}, },
child: const Text( child: const Text(
'Spiel starten!', 'Spiel starten!', // Start game button text
), ),
), ),
const Padding(padding: EdgeInsets.all(30)), const Padding(padding: EdgeInsets.all(30)),

View File

@ -118,6 +118,11 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_driver:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -147,6 +152,11 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@ -171,6 +181,11 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
integration_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
io: io:
dependency: transitive dependency: transitive
description: description:
@ -283,6 +298,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.0" version: "1.9.0"
platform:
dependency: transitive
description:
name: platform
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@ -291,6 +314,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
process:
dependency: transitive
description:
name: process
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
url: "https://pub.dev"
source: hosted
version: "5.0.2"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
@ -384,6 +415,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
sync_http:
dependency: transitive
description:
name: sync_http
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@ -464,6 +503,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.5" version: "2.4.5"
webdriver:
dependency: transitive
description:
name: webdriver
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
webkit_inspection_protocol: webkit_inspection_protocol:
dependency: transitive dependency: transitive
description: description:

View File

@ -17,6 +17,8 @@ dev_dependencies:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0 flutter_lints: ^2.0.0
test: ^1.25.2 test: ^1.25.2
integration_test:
sdk: flutter
flutter: flutter:
uses-material-design: true uses-material-design: true

View File

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:werwolf/main.dart' as app;
import 'package:werwolf/screens/playerregistry.dart';
import 'package:werwolf/screens/settings.dart';
import 'package:werwolf/screens/flippingcards.dart';
import 'package:werwolf/screens/gameboard.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Werwolf Game Integration Tests', () {
testWidgets('Complete game flow', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// Ensure we are on the PlayerRegistry screen
expect(find.byType(PlayerRegistry), findsOneWidget);
// Add players
for (int i = 0; i < 6; i++) {
await tester.enterText(find.byType(TextField), 'Player $i');
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle();
}
// Ensure all players are added
for (int i = 0; i < 6; i++) {
expect(find.text('Player $i'), findsOneWidget);
}
// Navigate to GameSettings screen
await tester.tap(find.text('Spiel einstellen'));
await tester.pumpAndSettle();
expect(find.byType(GameSettings), findsOneWidget);
// Ensure the number of players and wolves are displayed correctly
expect(find.text('Anzahl der Spieler 6'), findsOneWidget);
expect(find.text('Anzahl der Werwölfe 1'), findsOneWidget);
// Toggle a special role
await tester.tap(find.byType(Switch).first);
await tester.pumpAndSettle();
expect((tester.widget(find.byType(Switch).first) as Switch).value, true);
// Navigate to FlipingCard screen
await tester.tap(find.text('Spiel starten!'));
await tester.pumpAndSettle();
expect(find.byType(FlipingCard), findsOneWidget);
// Flip through all players
for (int i = 0; i < 6; i++) {
await tester.tap(find.text('Klick um deine Rolle zu sehen!'));
await tester.pumpAndSettle();
await tester.tap(find.text('Nächster Spieler'));
await tester.pumpAndSettle();
}
// Ensure we navigate to the game board
await tester.tap(find.text('Spiel anfangen!'));
await tester.pumpAndSettle();
expect(find.byType(PlayerGridView), findsOneWidget);
// Tap to kill a player
await tester.tap(find.text('Player 0'));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.close), findsOneWidget);
// Skip phase
await tester.tap(find.text('Tag skippen'));
await tester.pumpAndSettle();
expect(find.text('Nacht skippen'), findsOneWidget);
});
});
}

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:werwolf/screens/settings.dart';
import 'package:werwolf/screens/flippingcards.dart';
void main() {
group('GameSettings Tests', () {
List<String> playerNames = ['Alice', 'Bob', 'Charlie', 'Dave', 'Eve', 'Frank'];
testWidgets('Displays number of players', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(home: GameSettings(playernames: playerNames)));
expect(find.text('Anzahl der Spieler 6'), findsOneWidget);
});
testWidgets('Displays number of wolves and increments/decrements correctly', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(home: GameSettings(playernames: playerNames)));
expect(find.text('Anzahl der Werwölfe 1'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();
expect(find.text('Anzahl der Werwölfe 2'), findsOneWidget);
await tester.tap(find.byIcon(Icons.remove));
await tester.pumpAndSettle();
expect(find.text('Anzahl der Werwölfe 1'), findsOneWidget);
});
testWidgets('Displays special roles and toggles switches', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(home: GameSettings(playernames: playerNames)));
expect(find.text('Spezielle Rollen'), findsOneWidget);
await tester.tap(find.byType(Switch).first);
await tester.pumpAndSettle();
expect((tester.widget(find.byType(Switch).first) as Switch).value, true);
});
testWidgets('Navigates to FlipingCard screen on start', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(home: GameSettings(playernames: playerNames)));
await tester.tap(find.text('Spiel starten!'));
await tester.pumpAndSettle();
expect(find.byType(FlipingCard), findsOneWidget);
});
});
}