feat(automation): add view.select dispatcher handler + tests

This commit is contained in:
SoftFever
2026-06-03 21:18:50 +08:00
parent 7ef89fdb9d
commit 9a16fb7c2e
5 changed files with 99 additions and 1 deletions

View File

@@ -20,6 +20,7 @@ public:
std::vector<std::vector<KeyChord>> sent_keys;
int screenshot_window_count = 0;
std::vector<std::vector<std::string>> opened_paths; // paths of each open_files()
std::vector<std::string> selected_views; // view of each select_view()
// Canned outputs (set by tests).
UiNode tree; // default tree for dump_tree
@@ -28,6 +29,8 @@ public:
bool click_result = true;
int open_return_count = 0; // value open_files() returns
bool open_should_fail = false; // when true, open_files() throws kErrLoadFailed
int select_view_index = 0; // value select_view() returns
bool select_view_should_fail = false; // when true, select_view() throws kErrNotFound
// Optional: per-call tree provider (overrides `tree` when set).
std::function<UiNode(int /*call_index*/)> tree_provider;
@@ -59,6 +62,12 @@ public:
throw AutomationError(kErrLoadFailed, "mock load failed");
return open_return_count;
}
int select_view(const std::string& view) override {
selected_views.push_back(view);
if (select_view_should_fail)
throw AutomationError(kErrNotFound, "mock view not found");
return select_view_index;
}
};
}}} // namespace

View File

@@ -267,3 +267,62 @@ TEST_CASE("automation.version capabilities include file.open", "[automation][rpc
for (const auto& c : caps) if (c == "file.open") found = true;
CHECK(found);
}
TEST_CASE("view.select routes the view name to the backend", "[automation][rpc]") {
MockUiBackend mock;
mock.select_view_index = 1;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",1},{"method","view.select"},
{"params",{{"view","prepare"}}}});
CHECK(resp.at("result").at("ok") == true);
CHECK(resp.at("result").at("view") == "prepare");
CHECK(resp.at("result").at("index") == 1);
REQUIRE(mock.selected_views.size() == 1);
CHECK(mock.selected_views[0] == "prepare");
}
TEST_CASE("view.select with missing view -> invalid params", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",2},{"method","view.select"},
{"params", json::object()}});
CHECK(resp.at("error").at("code") == kInvalidParams);
CHECK(mock.selected_views.empty());
}
TEST_CASE("view.select with non-string view -> invalid params", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",3},{"method","view.select"},
{"params",{{"view", 42}}}});
CHECK(resp.at("error").at("code") == kInvalidParams);
CHECK(mock.selected_views.empty());
}
TEST_CASE("view.select with empty view -> invalid params", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",4},{"method","view.select"},
{"params",{{"view",""}}}});
CHECK(resp.at("error").at("code") == kInvalidParams);
CHECK(mock.selected_views.empty());
}
TEST_CASE("view.select unavailable view -> not found (1001)", "[automation][rpc]") {
MockUiBackend mock;
mock.select_view_should_fail = true;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",5},{"method","view.select"},
{"params",{{"view","calibration"}}}});
CHECK(resp.at("error").at("code") == kErrNotFound);
}
TEST_CASE("automation.version capabilities include view.select", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",6},{"method","automation.version"}});
const auto& caps = resp.at("result").at("capabilities");
bool found = false;
for (const auto& c : caps) if (c == "view.select") found = true;
CHECK(found);
}