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 "absl/strings/ascii.h"
7#include "absl/strings/match.h"
8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_format.h"
10#include "absl/strings/str_replace.h"
11
12#include <regex>
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(
72 "Unable to parse prompt: \"%s\"\n\n"
73 "Supported patterns:\n"
74 " - Open <Editor> editor\n"
75 " - Open <Editor> and verify it loads\n"
76 " - Type '<text>' in <input>\n"
77 " - Click <button>\n\n"
78 "Examples:\n"
79 " - Open Overworld editor\n"
80 " - Open Dungeon editor and verify it loads\n"
81 " - Type 'zelda3.sfc' in filename input\n"
82 " - Click Open ROM button",
83 prompt));
84}
85
86bool TestWorkflowGenerator::MatchesOpenEditor(const std::string& prompt,
87 std::string* editor_name) {
88 // Match: "open <name> editor" or "open <name>"
89 std::regex pattern(R"(open\s+(\w+)(?:\s+editor)?)");
90 std::smatch match;
91 if (std::regex_search(prompt, match, pattern) && match.size() > 1) {
92 *editor_name = match[1].str();
93 return true;
94 }
95 return false;
96}
97
98bool TestWorkflowGenerator::MatchesOpenAndVerify(const std::string& prompt,
99 std::string* editor_name) {
100 // Match: "open <name> and verify" or "open <name> editor and verify it loads"
101 std::regex pattern(R"(open\s+(\w+)(?:\s+editor)?\s+and\s+verify)");
102 std::smatch match;
103 if (std::regex_search(prompt, match, pattern) && match.size() > 1) {
104 *editor_name = match[1].str();
105 return true;
106 }
107 return false;
108}
109
110bool TestWorkflowGenerator::MatchesTypeInput(const std::string& prompt,
111 std::string* input_name,
112 std::string* text) {
113 // Match: "type 'text' in <input>" or "type \"text\" in <input>"
114 std::regex pattern(R"(type\s+['"]([^'"]+)['"]\s+in(?:to)?\s+(\w+))");
115 std::smatch match;
116 if (std::regex_search(prompt, match, pattern) && match.size() > 2) {
117 *text = match[1].str();
118 *input_name = match[2].str();
119 return true;
120 }
121 return false;
122}
123
124bool TestWorkflowGenerator::MatchesClickButton(const std::string& prompt,
125 std::string* button_name) {
126 // Match: "click <button>" or "click <button> button"
127 std::regex pattern(R"(click\s+([\w\s]+?)(?:\s+button)?\s*$)");
128 std::smatch match;
129 if (std::regex_search(prompt, match, pattern) && match.size() > 1) {
130 *button_name = match[1].str();
131 return true;
132 }
133 return false;
134}
135
136std::string TestWorkflowGenerator::NormalizeEditorName(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("button:%s",
160 absl::StrReplaceAll(normalized_name,
161 {{" Editor", ""}}));
162 workflow.steps.push_back(click_step);
163
164 // Step 2: Wait for editor window to appear
165 TestStep wait_step;
166 wait_step.type = TestStepType::kWait;
167 wait_step.condition = absl::StrFormat("window_visible:%s", normalized_name);
168 wait_step.timeout_ms = 5000;
169 workflow.steps.push_back(wait_step);
170
171 return workflow;
172}
173
175 const std::string& editor_name) {
176 // Start with basic open workflow
177 TestWorkflow workflow = BuildOpenEditorWorkflow(editor_name);
178 workflow.description = absl::StrFormat("Open and verify %s",
179 NormalizeEditorName(editor_name));
180
181 // Add assertion step
182 TestStep assert_step;
183 assert_step.type = TestStepType::kAssert;
184 assert_step.condition = absl::StrFormat("visible:%s",
185 NormalizeEditorName(editor_name));
186 workflow.steps.push_back(assert_step);
187
188 return workflow;
189}
190
192 const std::string& input_name, const std::string& text) {
193 TestWorkflow workflow;
194 workflow.description = absl::StrFormat("Type '%s' into %s", text, input_name);
195
196 // Step 1: Click input to focus
197 TestStep click_step;
198 click_step.type = TestStepType::kClick;
199 click_step.target = absl::StrFormat("input:%s", input_name);
200 workflow.steps.push_back(click_step);
201
202 // Step 2: Type the text
203 TestStep type_step;
204 type_step.type = TestStepType::kType;
205 type_step.target = absl::StrFormat("input:%s", input_name);
206 type_step.text = text;
207 type_step.clear_first = true;
208 workflow.steps.push_back(type_step);
209
210 return workflow;
211}
212
214 const std::string& button_name) {
215 TestWorkflow workflow;
216 workflow.description = absl::StrFormat("Click '%s' button", button_name);
217
218 TestStep click_step;
219 click_step.type = TestStepType::kClick;
220 click_step.target = absl::StrFormat("button:%s", button_name);
221 workflow.steps.push_back(click_step);
222
223 return workflow;
224}
225
226} // namespace cli
227} // 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.
Main namespace for the application.
std::string ToString() const
std::vector< TestStep > steps
std::string ToString() const