Layer | Use it for | Main tools |
|---|---|---|
Unit tests | Reducer rules, selector functions, state invariants, small pure helpers. | Ordinary Rust tests. |
Effect tests | Proving a reducer requested a job, service, capability, resource, or runtime effect. | ReducerContext, Effects, action input inspection. |
Headless integration tests | Component conversion, layout, display output, semantic output, runtime interaction without a real window. | fission-test, TestHarness, TestDriver. |
Live shell tests | Real shell input, focus, IME, scrolling, screenshots, accessibility metadata, window behavior, and end-to-end release screenshots. | fission-test-driver, LiveTestClient, /cmd. |
Target smoke tests | Generated platform host builds and starts for Web, Android, iOS, desktop, terminal, static, or SSR targets. | fission test, target smoke commands, package readiness. |
#[test]
fn add_draft_todo_adds_item_and_clears_draft() {
let mut state = TodoState {
draft: "Publish release".into(),
next_id: 7,
..Default::default()
};
add_draft_todo(&mut state);
assert_eq!(state.todos.len(), 1);
assert_eq!(state.todos[0].id, 7);
assert_eq!(state.todos[0].title, "Publish release");
assert!(state.draft.is_empty());
assert_eq!(state.next_id, 8);
}
use fission::core::{ActionInput, ActionRegistry, Effects, ReducerContext};
#[test]
fn load_todos_marks_loading_and_requests_job() {
let mut state = TodoState::default();
let mut registry = ActionRegistry::new();
let mut effects = Effects::new(1, &mut registry);
let input = ActionInput::None;
let mut ctx = ReducerContext {
effects: &mut effects,
input: &input,
};
load_todos(&mut state, &mut ctx);
assert!(state.loading);
assert_eq!(effects.out.len(), 1);
}
use fission_test::TestHarness;
use fission_test::prelude::DisplayOp;
#[test]
fn todo_screen_renders_seed_items() {
let state = TodoState {
todos: vec![Todo {
id: 1,
title: "Write release notes".into(),
done: false,
}],
next_id: 2,
..Default::default()
};
let mut harness = TestHarness::new(state).with_root_widget(TodoApp);
harness.pump().expect("pump todo app");
let display_list = harness.get_last_display_list().expect("display list");
assert!(display_list.ops.iter().any(|op| matches!(
op,
DisplayOp::DrawText { text, .. } if text == "Write release notes"
)));
}
Command::new(env!("CARGO_BIN_EXE_todo"))
.env("FISSION_TEST_CONTROL_PORT", control_port.to_string())
.spawn()?;
use fission_test_driver::{LiveTestClient, SelectorQuery};
let client = LiveTestClient::connect(control_port);
client.wait_for_ready(15_000)?;
client.fill_text_semantic_identifier("todo.title", "Publish 0.9.0")?;
client.tap_semantic_identifier("todo.add")?;
client.wait_for_visible(
SelectorQuery::semantic_identifier("todo.row.0"),
5_000,
)?;
client.screenshot(".artifacts/screenshots/todo-added.png")?;
TextInput {
id: Some(WidgetId::explicit("todo-title-input")),
value: view.state().draft.clone(),
label: Some("New task".into()),
on_change: Some(update_draft),
..Default::default()
}
.semantics_identifier("todo.title")
Button {
variant: ButtonVariant::Primary,
child: Some(Text::new("Add task").into()),
on_press: Some(add),
..Default::default()
}
.semantics_identifier("todo.add")
Endpoint | Meaning |
|---|---|
GET /health | Returns when the test-control server is alive. |
POST /cmd | Accepts one JSON command with a cmd field and returns a JSON response. |
curl -s http://127.0.0.1:9876/cmd \
-H 'content-type: application/json' \
-d '{"cmd":"TapSelector","query":{"selector":{"kind":"semantic_identifier","identifier":"todo.add"}}}'
curl -s http://127.0.0.1:9876/cmd \
-H 'content-type: application/json' \
-d '{"cmd":"CaptureScreenshot"}' \
| jq -r '.png_base64' \
| base64 --decode > .artifacts/manual/todo.png
Requirement | Best first test |
|---|---|
A reducer rejects invalid data | Unit reducer test. |
A reducer requests a network job | Effect test. |
A screen renders an empty state | Headless fission-test test. |
A button is reachable by screen readers and automation | Headless semantic assertion or live GetTree. |
A partially visible button can be clicked after scrolling | Live selector test. |
A text field handles focus, IME, paste, or keyboard input | Live test. |
A modal, popover, drawer, menu, or portal can be used | Live selector test. |
The shipped app starts on a device or browser | Target smoke or package test. |
Store screenshots are correct | LiveTest screenshot scenario plus human review. |