scene_test
A craft_runner builder that collects
every scene page-object in your test suite into one PatrolTester extension, so
tests reach a scene as $.loginScene instead of constructing it by hand.
await $.loginScene.enterPhone('0910000000');
await $.loginScene.submit();
await $.homeScene.expectGreeting('Ahmed');
Setup
dev_dependencies:
scene_test: ^0.10.0
Declare the builder in craft_runner.yaml:
roots: [test]
builders:
scene_test:SceneCraftBuilder:
Then run craft_runner craft, or craft_runner watch to regenerate on save.
Writing a scene
A scene is a class extending Scene, in a file ending _scene.dart. Scene is
yours to define — the builder only matches on the name:
// test/core/scene.dart
abstract class Scene {
late PatrolTester $;
}
// test/features/login/login_scene.dart
class LoginScene extends Scene {
Future<void> enterPhone(String phone) => $(#phoneField).enterText(phone);
Future<void> submit() => $(#submitButton).tap();
}
The builder finds it and writes test/core/scenes.craft.dart:
extension PatrolTesterExtension on PatrolTester {
LoginScene get loginScene => LoginScene()..$ = this;
}
Naming
The getter is the class name with a lowercased first letter, a trailing Scene
stripped, then Scene appended — so LoginScene and Login both become
loginScene. Imports are emitted relative to the output file and sorted, and
scenes are ordered by class name, so the generated file is stable across runs.
Requirements and limits
- Only
*_scene.dartfiles are scanned. A scene inlogin_page.dartis silently ignored. - The superclass must be written literally as
Scene. Matching is syntactic (craft_runner never resolves types), so an aliased import or an intermediate base class won't be detected. - The output path is fixed at
test/core/scenes.craft.dart. patrol_findersis not a dependency of this package — it only appears in the generated import, so your project supplies it.
License
MIT