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

Added automation tests for Example local app #1924

Merged
merged 2 commits into from
Mar 24, 2025
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
43 changes: 43 additions & 0 deletions starter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,46 @@ To incorporate Ensemble pages to your existing Flutter App, see `example_existin
## Build Tips
### Windows
- If any issue is faced on `flutter pub get`, clean Pub cache either by running `flutter pub cache clean` command or emptying the contents of 'C:\Users\username\AppData\Local\Pub\Cache' directory.

## Testing

This starter project includes support for testing Ensemble apps using Flutter's integration testing framework. Widgets can be easily found in tests using testIds.

### Writing Tests

To write integration tests for your Ensemble app:

1. Add testIds to your widgets in YAML:
```yaml
Button:
testId: navigate_button
label: Navigate to Goodbye Screen
onTap:
navigateScreen:
name: Goodbye
```

2. See the example integration test file at `integration_test/app_test.dart` that shows how to:
- Find widgets by testId using ValueKey
- Interact with widgets (tap, enter text, etc.)
- Navigate between screens
- Verify widget states

3. Run the tests with:
```
flutter test integration_test/app_test.dart
```

### Setting Up Test Dependencies

The integration_test dependencies are already configured in the starter project. If you're creating a new project, ensure you have these in your pubspec.yaml:

```yaml
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
```

For more information on Flutter integration tests, see the [Flutter Testing documentation](https://docs.flutter.dev/testing/integration-tests).
31 changes: 24 additions & 7 deletions starter/ensemble/apps/helloApp/screens/Goodbye.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
View:
header:
title: Goodbye
title: Goodbye Screen

body:
Column:
styles:
padding: 24
gap: 16
children:
- Text:
styles: { fontSize: 20 }
text: See you later

testId: goodbye_title
text: Goodbye!
styles:
fontSize: 32
fontWeight: bold

- Text:
testId: goodbye_message
text: Thank you for visiting our app.
styles:
fontSize: 18

- Button:
label: Go Home
testId: back_button
label: Go Back
styles:
marginTop: 24
backgroundColor: '#4285F4'
padding: 12
onTap:
navigateScreen:
name: Hello Home
navigateBack: {}
29 changes: 24 additions & 5 deletions starter/ensemble/apps/helloApp/screens/Hello Home.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ View:
# Optional - style the screen
styles:
scrollableView: true

# Optional - set the header for the screen
header:
title: r@title

onLoad: |
saveName('John','Doe');
console.log('name is ='+getName());
console.log(env.apiURL)
console.log(secrets.testSecret)

# Specify the body of the screen
body:
Column:
Expand All @@ -24,27 +25,45 @@ View:
gap: 8
children:
- Text:
testId: greeting_text
className: title
text: Hello ${ensemble.storage.helloApp.name.first}
${ensemble.storage.helloApp.name.last}
text: Hello ${ensemble.storage.helloApp.name.first} ${ensemble.storage.helloApp.name.last}

- Text:
testId: description_text
text: Welcome to our Ensemble app
styles:
color: '#444444'

- MyCustomWidget1:
testId: custom_widget
inputs:
inputName: ${ensemble.storage.helloApp.name}

- Button:
testId: api_button
label: Call API in imported library
onTap: |-
callMockMethod();
console.log('name is ='+getName());
console.log('name is ='+getName());

- Button:
testId: navigate_button
label: Navigate to Goodbye Screen
styles:
marginTop: 16
padding: 12
onTap:
navigateScreen:
name: Goodbye

- Image:
testId: logo_image
source: logo.svg
styles:
width: 200
height: 200
marginTop: 24

Global: |-
//var myVar = myButton.label;
Expand Down
82 changes: 82 additions & 0 deletions starter/integration_test/app_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:ensemble_starter/main.dart' as app;

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

group('Ensemble App Testing', () {
testWidgets('Navigate through screens with delays', (WidgetTester tester) async {
// Start the app
print('Starting the app...');
app.main();

// Wait for the app to fully load
print('Waiting for app to load...');
await tester.pumpAndSettle(const Duration(seconds: 5));

print('App loaded, now testing navigation...');

// Verify we can find all expected elements on the first screen
final greetingTextFinder = find.byKey(const ValueKey('greeting_text'));
final descriptionTextFinder = find.byKey(const ValueKey('description_text'));
final navigateButtonFinder = find.byKey(const ValueKey('navigate_button'));
final apiButtonFinder = find.byKey(const ValueKey('api_button'));

expect(greetingTextFinder, findsOneWidget, reason: 'Should find greeting text');
expect(descriptionTextFinder, findsOneWidget, reason: 'Should find description text');
expect(navigateButtonFinder, findsOneWidget, reason: 'Should find navigate button');
expect(apiButtonFinder, findsOneWidget, reason: 'Should find API button');

print('Found all expected widgets on the first screen');

// Add a delay to observe the first screen
print('Pausing to observe first screen...');
await Future.delayed(const Duration(seconds: 3));

// Navigate to the Goodbye screen
print('Tapping navigate button to go to Goodbye screen...');
await tester.tap(navigateButtonFinder);
await tester.pumpAndSettle();

// Add a delay to observe the Goodbye screen
print('Pausing to observe Goodbye screen...');
await Future.delayed(const Duration(seconds: 5));

// Look for elements on the Goodbye screen
final goodbyeTitleFinder = find.byKey(const ValueKey('goodbye_title'));
final backButtonFinder = find.byKey(const ValueKey('back_button'));

expect(goodbyeTitleFinder, findsOneWidget, reason: 'Should find goodbye title');
expect(backButtonFinder, findsOneWidget, reason: 'Should find back button');

print('Found all expected widgets on the Goodbye screen');

// Navigate back to the first screen
print('Tapping back button to return to first screen...');
await tester.tap(backButtonFinder);
await tester.pumpAndSettle();

// Add a delay to observe the first screen again
print('Pausing to observe return to first screen...');
await Future.delayed(const Duration(seconds: 3));

// Verify we're back on the first screen
expect(navigateButtonFinder, findsOneWidget, reason: 'Should be back on first screen');

print('Successfully returned to first screen');

// Test the API button
print('Tapping API button...');
await tester.tap(apiButtonFinder, warnIfMissed: false); // Added warnIfMissed: false to suppress the warning
await tester.pumpAndSettle();

// Add a final delay to observe any effects from the API button
print('Pausing to observe effects of API button...');
await Future.delayed(const Duration(seconds: 3));

print('Test completed successfully');
});
});
}