simvx.core.ui.testing¶
Headless UI test harness — validate widgets without GPU or window.
Provides UITestHarness for frame simulation, input injection, and draw capture. All tests run on CPU only; no Vulkan, GLFW, or display required.
Example: from simvx.core import Button, VBoxContainer, SceneTree from simvx.core.ui.testing import UITestHarness
def test_button_click():
root = VBoxContainer()
btn = root.add_child(Button("Save"))
harness = UITestHarness(root)
clicks = []
btn.pressed.connect(lambda: clicks.append(1))
harness.click(btn)
assert len(clicks) == 1
Module Contents¶
Classes¶
One recorded draw command. |
|
Mock renderer that records draw commands for assertion. |
|
Headless UI test harness for programmatic widget testing. |
Data¶
API¶
- simvx.core.ui.testing.__all__¶
[‘UITestHarness’, ‘DrawLog’, ‘DrawCall’]
- class simvx.core.ui.testing.DrawCall[source]¶
One recorded draw command.
- type: str¶
None
- x: float¶
0.0
- y: float¶
0.0
- w: float¶
0.0
- h: float¶
0.0
- colour: tuple[float, ...]¶
()
- text: str = <Multiline-String>¶
- scale: float¶
1.0
- x2: float¶
0.0
- y2: float¶
0.0
- class simvx.core.ui.testing.DrawLog[source]¶
Mock renderer that records draw commands for assertion.
Implements the same interface as Draw2D so widgets can render into it. All geometry is captured but not rasterized.
Example: harness.tick() assert “Save” in harness.draw_log.texts() assert harness.draw_log.rects_at(100, 50)
Initialization
- draw_line_coloured(x1: float, y1: float, x2: float, y2: float, colour: tuple = (1, 1, 1, 1))[source]¶
- draw_text_coloured(text: str, x: float, y: float, scale: float = 1.0, colour: tuple = (1, 1, 1, 1))[source]¶
- text_width(text: str, scale: float = 1.0) float[source]¶
Approximate text width: 8px per character at scale 1.0.
- set_colour(r=255, g=255, b=255, a=255)[source]¶
Track current colour (no-op for recording; colour is per-call).
- fill_rect_gradient(x: float, y: float, w: float, h: float, colour_top: tuple, colour_bottom: tuple)[source]¶
- draw_gradient_rect(x: float, y: float, w: float, h: float, colour_top: tuple, colour_bottom: tuple)[source]¶
- draw_filled_circle(cx: float, cy: float, radius: float, colour: tuple = (1, 1, 1, 1), segments: int = 24)[source]¶
- rects_at(x: float, y: float) list[simvx.core.ui.testing.DrawCall][source]¶
All fill_rect calls whose bounds contain (x, y).
- calls_of_type(call_type: str) list[simvx.core.ui.testing.DrawCall][source]¶
All calls of a specific type.
- class simvx.core.ui.testing.UITestHarness(root: simvx.core.ui.core.Control, screen_size: tuple[float, float] = (1280, 720))[source]¶
Headless UI test harness for programmatic widget testing.
Creates a SceneTree with the given root control, provides input injection helpers, frame simulation, draw capture, and widget lookup.
Example: panel = VBoxContainer() btn = panel.add_child(Button(“OK”)) harness = UITestHarness(panel)
harness.click(btn) harness.type_text("hello") harness.press_key("enter") harness.tick() assert harness.draw_log.has_text("OK")Initialization
- property root: simvx.core.ui.core.Control¶
- teardown()[source]¶
Tear down the scene tree, breaking reference cycles to free memory.
Idempotent — safe to call multiple times. Recursively severs every parent↔child and node↔tree back-reference so the entire node graph becomes immediately reclaimable by refcount alone (no GC needed).
- tick(dt: float = 1 / 60, count: int = 1)[source]¶
Advance count frames: process → draw for each.
Draw log is cleared before the first frame, then accumulates across all frames in this tick call.
- process_only(dt: float = 1 / 60, count: int = 1)[source]¶
Advance frames without drawing (faster for logic-only tests).
- click(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], button: int = 1)[source]¶
Click (press + release) at widget center or screen position.
- double_click(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], button: int = 1)[source]¶
Two rapid clicks at the same position.
- mouse_down(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], button: int = 1)[source]¶
Press mouse button without releasing.
- mouse_up(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], button: int = 1)[source]¶
Release mouse button.
- mouse_move(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float])[source]¶
Move mouse to position (triggers hover/mouse_over updates).
- drag(start: simvx.core.ui.core.Control | simvx.core.math.types.Vec2, end: simvx.core.ui.core.Control | simvx.core.math.types.Vec2, steps: int = 5, button: int = 1)[source]¶
Simulate mouse drag with intermediate moves.
- press_key(key: str, release: bool = True)[source]¶
Send key press (and optionally release) event.
Supports modifier combos: “ctrl+s”, “shift+enter”, “escape”. For modifier combos, temporarily sets Input._keys so SceneTree builds the correct combo_key string.
- scroll(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], direction: str = 'down', amount: int = 1)[source]¶
Send scroll events. direction is ‘up’ or ‘down’.
- touch(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], finger_id: int = 0)[source]¶
Touch down on a control (like a tap press). Routes through UI as mouse.
- touch_up(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], finger_id: int = 0)[source]¶
Touch release on a control.
- tap(target: simvx.core.ui.core.Control | simvx.core.math.types.Vec2 | tuple[float, float], finger_id: int = 0)[source]¶
Full tap gesture (touch down + release).
- set_focus(control: simvx.core.ui.core.Control)[source]¶
Directly focus a control.
- find(path: str) simvx.core.ui.core.Control | None[source]¶
Find widget by slash-separated name path from root.
Example: harness.find(“Layout/TabContainer/Editor”)
- find_all(widget_type: type) list[simvx.core.ui.core.Control][source]¶
Find all widgets of a given type in the tree.
- find_by_text(text: str) simvx.core.ui.core.Control | None[source]¶
Find first Label or Button whose .text contains the given string.
- find_by_name(name: str) simvx.core.ui.core.Control | None[source]¶
Find first widget with the given name (depth-first).
- assert_visible(widget: simvx.core.ui.core.Control, msg: str = '')[source]¶
Assert widget is visible.
Assert widget is not visible.
- assert_focused(widget: simvx.core.ui.core.Control, msg: str = '')[source]¶
Assert widget has focus.
- assert_text(widget: simvx.core.ui.core.Control, expected: str, msg: str = '')[source]¶
Assert widget.text matches expected.
- assert_text_contains(widget: simvx.core.ui.core.Control, substring: str, msg: str = '')[source]¶
Assert widget.text contains substring.