yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
test_workflow_generator.cc
Go to the documentation of this file.
1// test_workflow_generator.cc
2// Implementation of natural language to test workflow conversion
3
5
6#include <regex>
7
8#include "absl/strings/ascii.h"
9#include "absl/strings/match.h"
10#include "absl/strings/str_cat.h"
11#include "absl/strings/str_format.h"
12#include "absl/strings/str_replace.h"
13
14namespace yaze {
15namespace cli {
16
17std::string TestStep::ToString() const {
18 switch (type) {
20 return absl::StrFormat("Click(%s)", target);
22 return absl::StrFormat("Type(%s, \"%s\"%s)", target, text,
23 clear_first ? ", clear_first" : "");
25 return absl::StrFormat("Wait(%s, %dms)", condition, timeout_ms);
27 return absl::StrFormat("Assert(%s)", condition);
29 return "Screenshot()";
30 }
31 return "Unknown";
32}
33
34std::string TestWorkflow::ToString() const {
35 std::string result = absl::StrCat("Workflow: ", description, "\n");
36 for (size_t i = 0; i < steps.size(); ++i) {
37 absl::StrAppend(&result, " ", i + 1, ". ", steps[i].ToString(), "\n");
38 }
39 return result;
40}
41
42absl::StatusOr<TestWorkflow> TestWorkflowGenerator::GenerateWorkflow(
43 const std::string& prompt) {
44 std::string normalized_prompt = absl::AsciiStrToLower(prompt);
45
46 // Try pattern matching in order of specificity
47 std::string editor_name, input_name, text, button_name;
48
49 // Pattern 1: "Open <Editor> and verify it loads"
50 if (MatchesOpenAndVerify(normalized_prompt, &editor_name)) {
51 return BuildOpenAndVerifyWorkflow(editor_name);
52 }
53
54 // Pattern 2: "Open <Editor> editor"
55 if (MatchesOpenEditor(normalized_prompt, &editor_name)) {
56 return BuildOpenEditorWorkflow(editor_name);
57 }
58
59 // Pattern 3: "Type '<text>' in <input>"
60 if (MatchesTypeInput(normalized_prompt, &input_name, &text)) {
61 return BuildTypeInputWorkflow(input_name, text);
62 }
63
64 // Pattern 4: "Click <button>"
65 if (MatchesClickButton(normalized_prompt, &button_name)) {
66 return BuildClickButtonWorkflow(button_name);
67 }
68
69 // If no patterns match, return helpful error
70 return absl::InvalidArgumentError(
71 absl::StrFormat("Unable to parse prompt: \"%s\"\n\n"
72 "Supported patterns:\n"
73 " - Open <Editor> editor\n"
74 " - Open <Editor> and verify it loads\n"
75 " - Type '<text>' in <input>\n"
76 " - Click <button>\n\n"
77 "Examples:\n"
78 " - Open Overworld editor\n"
79 " - Open Dungeon editor and verify it loads\n"
80 " - Type 'zelda3.sfc' in filename input\n"
81 " - Click Open ROM button",
82 prompt));
83}
84
85bool TestWorkflowGenerator::MatchesOpenEditor(const std::string& prompt,
86 std::string* editor_name) {
87 // Match: "open <name> editor" or "open <name>"
88 std::regex pattern(R"(open\s+(\w+)(?:\s+editor)?)");
89 std::smatch match;
90 if (std::regex_search(prompt, match, pattern) && match.size() > 1) {
91 *editor_name = match[1].str();
92 return true;
93 }
94 return false;
95}
96
97bool TestWorkflowGenerator::MatchesOpenAndVerify(const std::string& prompt,
98 std::string* editor_name) {
99 // Match: "open <name> and verify" or "open <name> editor and verify it loads"
100 std::regex pattern(R"(open\s+(\w+)(?:\s+editor)?\s+and\s+verify)");
101 std::smatch match;
102 if (std::regex_search(prompt, match, pattern) && match.size() > 1) {
103 *editor_name = match[1].str();
104 return true;
105 }
106 return false;
107}
108
109bool TestWorkflowGenerator::MatchesTypeInput(const std::string& prompt,
110 std::string* input_name,
111 std::string* text) {
112 // Match: "type 'text' in <input>" or "type \"text\" in <input>"
113 std::regex pattern(R"(type\s+['"]([^'"]+)['"]\s+in(?:to)?\s+(\w+))");
114 std::smatch match;
115 if (std::regex_search(prompt, match, pattern) && match.size() > 2) {
116 *text = match[1].str();
117 *input_name = match[2].str();
118 return true;
119 }
120 return false;
121}
122
123bool TestWorkflowGenerator::MatchesClickButton(const std::string& prompt,
124 std::string* button_name) {
125 // Match: "click <button>" or "click <button> button"
126 std::regex pattern(R"(click\s+([\w\s]+?)(?:\s+button)?\s*$)");
127 std::smatch match;
128 if (std::regex_search(prompt, match, pattern) && match.size() > 1) {
129 *button_name = match[1].str();
130 return true;
131 }
132 return false;
133}
134
136 const std::string& name) {
137 std::string normalized = name;
138 // Capitalize first letter
139 if (!normalized.empty()) {
140 normalized[0] = std::toupper(normalized[0]);
141 }
142 // Add " Editor" suffix if not present
143 if (!absl::StrContains(absl::AsciiStrToLower(normalized), "editor")) {
144 absl::StrAppend(&normalized, " Editor");
145 }
146 return normalized;
147}
148
150 const std::string& editor_name) {
151 std::string normalized_name = NormalizeEditorName(editor_name);
152
153 TestWorkflow workflow;
154 workflow.description = absl::StrFormat("Open %s", normalized_name);
155
156 // Step 1: Click the editor button
157 TestStep click_step;
158 click_step.type = TestStepType::kClick;
159 click_step.target = absl::StrFormat(
160 "button:%s", absl::StrReplaceAll(normalized_name, {{" Editor", ""}}));
161 workflow.steps.push_back(click_step);
162
163 // Step 2: Wait for editor window to appear
164 TestStep wait_step;
165 wait_step.type = TestStepType::kWait;
166 wait_step.condition = absl::StrFormat("window_visible:%s", normalized_name);
167 wait_step.timeout_ms = 5000;
168 workflow.steps.push_back(wait_step);
169
170 return workflow;
171}
172
174 const std::string& editor_name) {
175 // Start with basic open workflow
176 TestWorkflow workflow = BuildOpenEditorWorkflow(editor_name);
177 workflow.description =
178 absl::StrFormat("Open and verify %s", NormalizeEditorName(editor_name));
179
180 // Add assertion step
181 TestStep assert_step;
182 assert_step.type = TestStepType::kAssert;
183 assert_step.condition =
184 absl::StrFormat("visible:%s", NormalizeEditorName(editor_name));
185 workflow.steps.push_back(assert_step);
186
187 return workflow;
188}
189
191 const std::string& input_name, const std::string& text) {
192 TestWorkflow workflow;
193 workflow.description = absl::StrFormat("Type '%s' into %s", text, input_name);
194
195 // Step 1: Click input to focus
196 TestStep click_step;
197 click_step.type = TestStepType::kClick;
198 click_step.target = absl::StrFormat("input:%s", input_name);
199 workflow.steps.push_back(click_step);
200
201 // Step 2: Type the text
202 TestStep type_step;
203 type_step.type = TestStepType::kType;
204 type_step.target = absl::StrFormat("input:%s", input_name);
205 type_step.text = text;
206 type_step.clear_first = true;
207 workflow.steps.push_back(type_step);
208
209 return workflow;
210}
211
213 const std::string& button_name) {
214 TestWorkflow workflow;
215 workflow.description = absl::StrFormat("Click '%s' button", button_name);
216
217 TestStep click_step;
218 click_step.type = TestStepType::kClick;
219 click_step.target = absl::StrFormat("button:%s", button_name);
220 workflow.steps.push_back(click_step);
221
222 return workflow;
223}
224
225} // namespace cli
226} // namespace yaze
bool MatchesClickButton(const std::string &prompt, std::string *button_name)
bool MatchesTypeInput(const std::string &prompt, std::string *input_name, std::string *text)
bool MatchesOpenEditor(const std::string &prompt, std::string *editor_name)
TestWorkflow BuildOpenEditorWorkflow(const std::string &editor_name)
TestWorkflow BuildTypeInputWorkflow(const std::string &input_name, const std::string &text)
TestWorkflow BuildClickButtonWorkflow(const std::string &button_name)
bool MatchesOpenAndVerify(const std::string &prompt, std::string *editor_name)
std::string NormalizeEditorName(const std::string &name)
TestWorkflow BuildOpenAndVerifyWorkflow(const std::string &editor_name)
absl::StatusOr< TestWorkflow > GenerateWorkflow(const std::string &prompt)
Generate a test workflow from a natural language prompt.
std::string ToString() const
std::vector< TestStep > steps
std::string ToString() const