godot-e2e
Write and run E2E (end-to-end) game tests using the godot-e2e framework. Python controls a live Godot game over TCP — Locator-based semantic queries, expect() auto-retry assertions, and engine log capture make failures self-diagnosing.
Install / Use
npx skills add RandallLiuXin/GodotMaker --skill godot-e2eInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
OtherSupported Platforms
Our assessment of godot-e2e
godot-e2e scores 92/100 on our quality scale, 32nd of 241 Other skills we index (top 14%).
Its SKILL.md is 15 KB long, well organised into 26 sections with 6 code examples: a thorough specification that gives an agent plenty to work with.
It has 549 GitHub stars, a meaningful sign that others use it.
Maintenance, license and trust
- The repository was last updated 16 days ago, so godot-e2e is actively maintained.
- No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
- Its trust signals score 88/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
godot-e2e compared with similar skills
All 4 of these similar skills score higher than godot-e2e; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| godot-e2e (this skill)by RandallLiuXin | 92 | 549 | 16d ago | SKILL.md |
| Agent-Reachby Panniantong | 100 | 89.8k | 18d ago | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 74.4k | today | CLAUDE.md |
| Scraplingby D4Vinci | 100 | 85.5k | today | MCP Server |
| crawl4aiby unclecode | 100 | 84.7k | 8d ago | MCP Server |
Frequently asked questions
- How do I install godot-e2e?
- Run
npx skills add RandallLiuXin/GodotMaker --skill godot-e2e. The install tabs above show the steps for each supported agent. - Which AI agents does godot-e2e work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is godot-e2e safe to use?
- It declares no license and scores 88/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is godot-e2e still maintained?
- The repository was last updated 16 days ago, so godot-e2e is actively maintained.
Skill content
View source on GitHubname: godot-e2e description: | Write and run E2E (end-to-end) game tests using the godot-e2e framework. Python controls a live Godot game over TCP — Locator-based semantic queries, expect() auto-retry assertions, and engine log capture make failures self-diagnosing.
Use this skill whenever you need to:
- Test actual gameplay: player movement, collisions, scoring, scene transitions
- Verify UI interactions: button clicks, label text, menu navigation
- Write integration tests that run the real game (not mocked unit tests)
- Debug E2E test failures or set up E2E test infrastructure
Triggers: "E2E test", "end-to-end test", "gameplay test", "test the game running", "simulate input", "test player movement", "test UI clicks", "godot-e2e", "integration test for game", "test scene transitions".
godot-e2e — E2E Testing for Godot
$ARGUMENTS
godot-e2e is a custom framework with zero LLM training data coverage.
Everything the model needs is in this skill (with deeper detail in
references/). Do not guess — follow these docs exactly.
Architecture
The godot-e2e CLI launches a Godot process and communicates over TCP
(localhost). Enabling the GodotE2E plugin in Project Settings
auto-registers an AutomationServer autoload that receives JSON
commands, executes them on the main thread, and sends back results.
The game runs unmodified — the server is dormant unless launched with
--e2e. Multiple instances can run in parallel (each auto-allocates a
unique port). The framework rests on three pillars: Locator for
semantic node queries, expect() for auto-retry assertions, and
engine log capture so every error carries the Godot logs that
preceded it.
Quick Start — conftest.py + Test File
# conftest.py (per test directory — explicit project path control;
# alternatively set GODOT_E2E_PROJECT_PATH env or pytest.ini
# `godot_e2e_project_path` and use the auto-registered `game` fixture).
# Replace "/root/Main" below with your project's entry-scene root —
# read it from `project.godot`'s `run/main_scene`.
import os
import pytest
from godot_e2e import GodotE2E
GODOT_PROJECT = os.path.join(os.path.dirname(__file__), "..")
GODOT_CONFIG = os.path.join(GODOT_PROJECT, ".claude", "godotmaker.yaml")
def _read_godot_path():
try:
with open(GODOT_CONFIG, "r", encoding="utf-8") as f:
for line in f:
line = line.split("#", 1)[0].strip()
if line.startswith("godot_path:"):
value = line.split(":", 1)[1].strip().strip("\"'")
return value or None
except OSError:
return None
return None
GODOT_PATH = _read_godot_path()
@pytest.fixture(scope="module")
def _game_process():
with GodotE2E.launch(
GODOT_PROJECT,
godot_path=GODOT_PATH,
timeout=15.0,
) as game:
game.wait_for_node("/root/Main", timeout=10.0)
yield game
@pytest.fixture(scope="function")
def game(_game_process):
_game_process.reload_scene()
_game_process.wait_for_node("/root/Main", timeout=5.0)
yield _game_process
# test_player.py
from godot_e2e import expect
def test_player_moves_right(game):
player = game.locator(group="player") # Locator query
initial_x = player.get_property("position:x")
game.input_action("ui_right", True)
game.wait_physics_frames(10)
game.input_action("ui_right", False)
expect(player).to_satisfy(
lambda l: l.get_property("position:x") > initial_x,
description="player moved right",
)
def test_button_starts_game(game):
game.get_by_button("Start").click() # auto-waits actionability
expect(game.locator(name="GameStatus")).to_have_text("Playing")
errors = [e for e in game.collected_logs if e.level == "error"]
assert not errors, f"errors during click: {errors}"
godot-e2e e2e/ -v
API Quick Reference
Launch / Lifecycle
| Method | Description |
|---|---|
| GodotE2E.launch(project_path, godot_path=None, port=0, timeout=10.0, extra_args=None, log_verbosity=None) | Context manager. Launch Godot + connect. port=0 auto-allocates. log_verbosity ∈ "error"/"warning"/"info". |
| GodotE2E.connect(host="127.0.0.1", port=6008, token="") | Connect to already-running Godot. |
| game.close() | Kill Godot process and close connection. |
Locator — Semantic Queries
Locator is lazy: queries re-resolve on every action, so a Locator
created before reload_scene() still works after.
| Constructor | Description |
|---|---|
| game.locator(path=, name=, group=, text=, type=, script=) | At least one kwarg required; AND-composed. name / text accept glob (*, ?). type matches via is X (descendants included, e.g. type="BaseButton" covers Button/CheckBox). |
| game.get_by_text(text) | Sugar for locator(text=text). |
| game.get_by_button(text) | Sugar for locator(type="BaseButton", text=text). |
| Refinement | Returns | Description |
|---|---|---|
| loc.filter(**kwargs) | Locator | Add AND-composed predicates. |
| loc.first() / loc.nth(i) | Locator | Pick first / i-th match. |
| loc.all() | list[Locator] | Snapshot of all matches; [] if none (no raise). |
| loc.locator(**kwargs) | Locator | Sub-query under this Locator's resolved node (parent resolved at action time). |
| Inspection (no raise on miss) | Returns |
|---|---|
| loc.exists() / loc.count() | bool / int |
| loc.is_visible() / loc.is_actionable() | bool (raises on multi-match / missing) |
| Action (re-resolves; requires exactly one match) | Notes |
|---|---|
| loc.click(*, force=False, timeout=5.0) | Auto-waits actionability for Control (visible + mouse_filter + in viewport); Node2D only checks visibility. force=True skips check. Raises NotActionableError on timeout. |
| loc.hover() | Inject InputEventMouseMotion at node's screen position. |
| loc.get_property(prop) / loc.set_property(prop, value) / loc.call(method, args=None) | Same as game.* versions, but path-pinned. |
| loc.wait_visible(*, timeout=5.0) | Block until target passes actionability. Raises NotActionableError with reasons + checks. |
| loc.wait_for_signal(signal_name, timeout=5.0) | Block until resolved node emits signal. |
expect() — Auto-Retry Assertions
expect(locator, *, timeout=5.0, poll_interval=0.05) → LocatorAssertions
re-resolves the Locator on each poll. Lookup errors during polling
(NodeNotFoundError, MultipleMatchesError, CommandError) are
caught; node may appear / disambiguate later.
| Matcher | Passes When |
|---|---|
| to_have_property(name, value) | locator.get_property(name) == value |
| to_have_text(text) | Target's text property equals (sugar for property "text"). |
| to_be_visible() | Visible in scene tree (Control / Node2D). |
| to_exist() | Query resolves to ≥1 node. |
| to_satisfy(predicate, *, description=None) | predicate(locator) truthy. Use description= for readable failure messages. |
ExpectationFailedError dual-inherits AssertionError → pytest
renders it as a regular assertion failure, with attributes actual,
observation_captured, matcher, scene_tree, last_error.
Engine Log Capture
| Member | Description |
|---|---|
| game.last_logs / game.collected_logs | List of LogEntry. last_logs cleared each command; collected_logs cleared per test by built-in fixtures. |
| game.reset_collected_logs() | Manual narrowing — reset window before a sub-assertion. |
| game.set_log_verbosity(level) / game.set_log_buffer_size(size) | Runtime tuning. Levels: "error" / "warning" (default) / "info". |
| LogEntry fields | level / message / function / file / line (last three populated for engine errors only). |
| Every GodotE2EError carries .logs | List of LogEntry from the failing command's response. Empty when capture inactive. |
Pytest auto-includes captured godot logs section on failure (built-in
plugin, no setup required). Buffer overflow synthesizes a "<N entries dropped>" warning.
Raw-Path Operations
Direct on game — used when you have a stable known path and a
Locator query would just add ceremony (typically root-level
singletons, autoloads, the Main entry node). Node ops
(game.get_property / set_property / call / find_by_group /
query_nodes / get_tree / batch), input (input_action /
input_key / input_mouse_* / press_action / press_key /
click / click_node), waits (wait_physics_frames /
wait_process_frames / wait_seconds / wait_for_node /
wait_for_signal / wait_for_property), scenes (get_scene /
change_scene / reload_scene / screenshot).
Types & Exceptions
from godot_e2e import (
Vector2, Vector2i, Vector3, Vector3i, Rect2, Rect2i,
Color, Transform2D, NodePath,
LogEntry, LogVerbosity, parse_log_entries,
expect, Locator, LocatorAssertions,
)
| Exception | When |
|---|---|
| NodeNotFoundError | Node path doesn't exist. |
| TimeoutError | wait_for_* exceeded. Has .scene_tree. |
| ConnectionLostError | Godot crashed or TCP dropped. |
| CommandError | Server returned an error. |
| MultipleMatchesError | Locator action without .first()/.nth()/.filter() matched >1. Has .paths. |
| NotActionableError | Locator.click() / wait_visible() timed out waiting for actionability. Has .path, .reasons, .checks. |
| ExpectationFailedError | expect(...) matcher exceeded timeout. Dual-inherits AssertionError. |
All inherit from GodotE2EError (which carries .logs).
Critical Rules
| # | Rule | Detail |
|---|------|--------|
| 1 | Physics frames for movement | After input, use wait_physics_frames for position/collision assertions. wait_process_frames does NOT advance physics. |
| 2 | Hold input for movement | press_action only taps (~4 frames). For sustained movement: input_action(act, True) → wait_physics_frames(N) → input_action(act, False). |
| 3 | input_action needs 2 args | input_action("jump", True) not input_action("jump"). For tap, use press_action("jump"). |
| 4 | Prefer expect() over manual wait + assert | expect(locator).to_have_property(...) retries with structured failure context (scene_tree + last_error) and renders as a normal pytest assertion. Use wait_for_property only when working with raw paths and no Locator is in scope. |
| 5 | Assert direction, not exact values | assert new_x > initial_x not assert pos_x == 450.0. Physics varies per machine. |
| 6 | wait_for_signal timing | Listener registers on arrival — signals emitted before are missed. Use expect().to_have_property for state assertions. |
| 7 | Locators with semantic queries beat hardcoded paths | game.get_by_button("Start") / game.locator(group="player") survives tree restructuring; /root/Main/UI/Menu/StartButton doesn't. Reserve raw paths for unique top-level nodes. |
| 8 | Read .logs on every E2E failure | Every GodotE2EError carries .logs — what Godot printed during the failing command. pytest -v auto-includes captured godot logs on failure. Ignoring it doubles diagnosis time. |
| 9 | Default log verbosity is "warning" | push_error and push_warning are captured; print() is NOT. Bump to "info" (via log_verbosity="info" at launch or game.set_log_verbosity("info") at runtime) only when debugging — at info verbosity log buffer fills 4-10× faster. |
| 10 | Use wait_seconds for Timer-gated waits | wait_process_frames(N) counts frames, not seconds. Under headless uncapped FPS, wait_process_frames(120) finishes in well under 2s. Use wait_seconds(t) or expect() for any wait gated by a Timer or wall-clock seconds. |
Fixture Strategies
| Strategy | Scope | Speed | Isolation | Use when |
|---|---|---|---|---|
| reload_scene | module process + funct
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
89.8kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
74.4kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
Scrapling
85.5k🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
crawl4ai
84.7kOpen-source web crawler and scraper for LLMs and AI agents: any website into clean, LLM-ready Markdown. Run it yourself, or use Crawl4AI Cloud with one key.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
