3#include "absl/flags/declare.h"
4#include "absl/flags/flag.h"
5#include "absl/strings/numbers.h"
6#include "absl/strings/str_format.h"
7#include "absl/strings/str_join.h"
25 const bool has_target = parser.
GetString(
"target").has_value();
26 const bool has_widget_key = parser.
GetString(
"widget-key").has_value();
27 if (has_target == has_widget_key) {
28 return absl::InvalidArgumentError(
29 "Provide exactly one of --target or --widget-key");
31 return absl::OkStatus();
36 const bool has_condition = parser.
GetString(
"condition").has_value();
37 const bool has_widget_key = parser.
GetString(
"widget-key").has_value();
38 if (!has_condition && !has_widget_key) {
39 return absl::InvalidArgumentError(
40 "Provide at least one of --condition or --widget-key");
42 return absl::OkStatus();
46 const std::string& arg_name,
48 if (!parser.
GetString(arg_name).has_value()) {
51 return parser.
GetInt(arg_name);
65 auto status = client->
Connect();
67 return absl::UnavailableError(
"Failed to connect to GUI server: " +
68 std::string(status.message()));
70 return absl::OkStatus();
77 return ValidateExactlyOneTargetOrWidget(parser);
82 if (
auto selector_status = ValidateExactlyOneTargetOrWidget(parser);
83 !selector_status.ok()) {
84 return selector_status;
86 if (!parser.
GetString(
"text").has_value()) {
87 return absl::InvalidArgumentError(
"Missing required argument: --text");
89 return absl::OkStatus();
94 return ValidateConditionOrWidget(parser);
99 return ValidateConditionOrWidget(parser);
105 auto tile_id_str = parser.
GetString(
"tile").value();
106 auto x_str = parser.
GetString(
"x").value();
107 auto y_str = parser.
GetString(
"y").value();
110 if (!ParseHexString(tile_id_str, &tile_id) || !absl::SimpleAtoi(x_str, &x) ||
111 !absl::SimpleAtoi(y_str, &y)) {
112 return absl::InvalidArgumentError(
"Invalid tile ID or coordinate format.");
116 auto status = client.
Connect();
118 return absl::UnavailableError(
"Failed to connect to GUI server: " +
119 std::string(status.message()));
123 std::string canvas_id =
"overworld";
124 status = client.
SetTile(canvas_id, x, y, tile_id);
127 formatter.
AddField(
"tile_id", absl::StrFormat(
"0x%03X", tile_id));
131 formatter.
AddField(
"status",
"Success");
133 formatter.
AddField(
"status",
"Failed");
134 formatter.
AddField(
"error", std::string(status.message()));
144 auto target = parser.
GetString(
"target").value_or(
"");
145 auto widget_key = parser.
GetString(
"widget-key").value_or(
"");
146 auto click_type_str = parser.
GetString(
"click-type").value_or(
"left");
149 if (click_type_str ==
"right")
151 else if (click_type_str ==
"middle")
153 else if (click_type_str ==
"double")
157 if (
auto status = ConnectGuiClient(&client); !status.ok()) {
161 auto result = client.
Click(target, click_type, widget_key);
164 formatter.
AddField(
"target", target);
165 formatter.
AddField(
"widget_key", widget_key);
166 formatter.
AddField(
"click_type", click_type_str);
169 formatter.
AddField(
"status", result->success ?
"Success" :
"Failed");
170 AddSelectorResolutionFields(*result, formatter);
171 if (!result->success) {
172 formatter.
AddField(
"error", result->message);
174 formatter.
AddField(
"execution_time_ms",
175 static_cast<int>(result->execution_time.count()));
177 formatter.
AddField(
"status",
"Error");
178 formatter.
AddField(
"error", std::string(result.status().message()));
182 return result.status();
188 auto target = parser.
GetString(
"target").value_or(
"");
189 auto widget_key = parser.
GetString(
"widget-key").value_or(
"");
190 auto text = parser.
GetString(
"text").value_or(
"");
191 const bool clear_first = parser.
HasFlag(
"clear-first");
194 if (
auto status = ConnectGuiClient(&client); !status.ok()) {
198 auto result = client.
Type(target, text, clear_first, widget_key);
201 formatter.
AddField(
"target", target);
202 formatter.
AddField(
"widget_key", widget_key);
203 formatter.
AddField(
"clear_first", clear_first);
206 formatter.
AddField(
"status", result->success ?
"Success" :
"Failed");
207 AddSelectorResolutionFields(*result, formatter);
208 if (!result->success) {
209 formatter.
AddField(
"error", result->message);
211 formatter.
AddField(
"execution_time_ms",
212 static_cast<int>(result->execution_time.count()));
214 formatter.
AddField(
"status",
"Error");
215 formatter.
AddField(
"error", std::string(result.status().message()));
219 return result.status();
225 auto condition = parser.
GetString(
"condition").value_or(
"");
226 auto widget_key = parser.
GetString(
"widget-key").value_or(
"");
228 auto timeout_or = ReadIntArgOrDefault(parser,
"timeout-ms", 5000);
229 if (!timeout_or.ok()) {
230 return timeout_or.status();
232 auto poll_interval_or = ReadIntArgOrDefault(parser,
"poll-interval-ms", 100);
233 if (!poll_interval_or.ok()) {
234 return poll_interval_or.status();
236 int timeout_ms = *timeout_or;
237 int poll_interval_ms = *poll_interval_or;
238 if (timeout_ms <= 0) {
239 return absl::InvalidArgumentError(
"--timeout-ms must be > 0");
241 if (poll_interval_ms <= 0) {
242 return absl::InvalidArgumentError(
"--poll-interval-ms must be > 0");
246 if (
auto status = ConnectGuiClient(&client); !status.ok()) {
251 client.
Wait(condition, timeout_ms, poll_interval_ms, widget_key);
254 formatter.
AddField(
"condition", condition);
255 formatter.
AddField(
"widget_key", widget_key);
256 formatter.
AddField(
"timeout_ms", timeout_ms);
257 formatter.
AddField(
"poll_interval_ms", poll_interval_ms);
260 formatter.
AddField(
"status", result->success ?
"Success" :
"Failed");
261 AddSelectorResolutionFields(*result, formatter);
262 if (!result->success) {
263 formatter.
AddField(
"error", result->message);
266 static_cast<int>(result->execution_time.count()));
268 formatter.
AddField(
"status",
"Error");
269 formatter.
AddField(
"error", std::string(result.status().message()));
273 return result.status();
279 auto condition = parser.
GetString(
"condition").value_or(
"");
280 auto widget_key = parser.
GetString(
"widget-key").value_or(
"");
283 if (
auto status = ConnectGuiClient(&client); !status.ok()) {
287 auto result = client.
Assert(condition, widget_key);
290 formatter.
AddField(
"condition", condition);
291 formatter.
AddField(
"widget_key", widget_key);
294 formatter.
AddField(
"status", result->success ?
"Success" :
"Failed");
295 AddSelectorResolutionFields(*result, formatter);
296 if (!result->expected_value.empty()) {
297 formatter.
AddField(
"expected_value", result->expected_value);
299 if (!result->actual_value.empty()) {
300 formatter.
AddField(
"actual_value", result->actual_value);
302 if (!result->success) {
303 formatter.
AddField(
"error", result->message);
306 formatter.
AddField(
"status",
"Error");
307 formatter.
AddField(
"error", std::string(result.status().message()));
311 return result.status();
317 auto window = parser.
GetString(
"window").value_or(
"");
318 auto type_str = parser.
GetString(
"type").value_or(
"all");
321 bool is_summary = (
GetName() ==
"gui-summarize-widgets");
324 if (
auto status = ConnectGuiClient(&client); !status.ok()) {
335 formatter.
BeginObject(is_summary ?
"GUI Summary" :
"Widget Discovery");
336 formatter.
AddField(
"window_filter", window);
339 formatter.
AddField(
"total_widgets", result->total_widgets);
340 formatter.
AddField(
"status",
"Success");
343 for (
const auto& win : result->windows) {
344 if (!win.visible && is_summary)
348 formatter.
AddField(
"name", win.name);
349 formatter.
AddField(
"visible", win.visible);
352 std::vector<std::string> highlights;
353 for (
const auto& w : win.widgets) {
354 if (w.type ==
"button" || w.type ==
"input" || w.type ==
"menu") {
355 highlights.push_back(absl::StrFormat(
"%s (%s)", w.label, w.type));
357 if (highlights.size() > 10)
360 formatter.
AddField(
"key_elements", absl::StrJoin(highlights,
", "));
364 for (
const auto& widget : win.widgets) {
367 formatter.
AddArrayItem(absl::StrFormat(
"%s (%s) - %s", widget.label,
368 widget.type, widget.path));
376 formatter.
AddField(
"status",
"Error");
377 formatter.
AddField(
"error", std::string(result.status().message()));
381 return result.status();
387 auto region = parser.
GetString(
"region").value_or(
"full");
388 auto image_format = parser.
GetString(
"format").value_or(
"PNG");
391 if (
auto status = ConnectGuiClient(&client); !status.ok()) {
395 auto result = client.
Screenshot(region, image_format);
398 formatter.
AddField(
"region", region);
399 formatter.
AddField(
"image_format", image_format);
402 formatter.
AddField(
"status", result->success ?
"Success" :
"Failed");
403 if (result->success) {
404 formatter.
AddField(
"output_path", result->message);
407 if (!absl::GetFlag(FLAGS_quiet)) {
408 std::cerr <<
"\n📸 \033[1;32mScreenshot captured!\033[0m\n";
409 std::cerr <<
" Path: \033[1;34m" << result->message <<
"\033[0m\n\n";
412 formatter.
AddField(
"error", result->message);
415 formatter.
AddField(
"status",
"Error");
416 formatter.
AddField(
"error", std::string(result.status().message()));
420 return result.status();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status SetTile(const std::string &canvas_id, int x, int y, int tile_id)
Client for automating YAZE GUI through gRPC.
absl::StatusOr< AutomationResult > Type(const std::string &target, const std::string &text, bool clear_first=false, const std::string &widget_key="")
Type text into an input field.
absl::StatusOr< AutomationResult > Screenshot(const std::string ®ion="full", const std::string &format="PNG")
Capture a screenshot.
absl::Status Connect()
Connect to the test harness server.
absl::StatusOr< DiscoverWidgetsResult > DiscoverWidgets(const DiscoverWidgetsQuery &query)
absl::StatusOr< AutomationResult > Assert(const std::string &condition, const std::string &widget_key="")
Assert a GUI state condition.
absl::StatusOr< AutomationResult > Wait(const std::string &condition, int timeout_ms=5000, int poll_interval_ms=100, const std::string &widget_key="")
Wait for a condition to be met.
absl::StatusOr< AutomationResult > Click(const std::string &target, ClickType type=ClickType::kLeft, const std::string &widget_key="")
Click a GUI element.
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
Utility for parsing common CLI argument patterns.
std::optional< std::string > GetString(const std::string &name) const
Parse a named argument (e.g., –format=json or –format json)
bool HasFlag(const std::string &name) const
Check if a flag is present.
absl::StatusOr< int > GetInt(const std::string &name) const
Parse an integer argument (supports hex with 0x prefix)
ABSL_DECLARE_FLAG(std::string, gui_server_address)
void AddSelectorResolutionFields(const AutomationResult &result, resources::OutputFormatter &formatter)
absl::Status ValidateConditionOrWidget(const resources::ArgumentParser &parser)
absl::Status ConnectGuiClient(GuiAutomationClient *client)
absl::Status ValidateExactlyOneTargetOrWidget(const resources::ArgumentParser &parser)
absl::StatusOr< int > ReadIntArgOrDefault(const resources::ArgumentParser &parser, const std::string &arg_name, int default_value)
bool ParseHexString(absl::string_view str, int *out)
ClickType
Type of click action to perform.
Result of a GUI automation action.
std::string resolved_widget_key
std::string resolved_path