tng 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +15 -0
- data/LICENSE.md +32 -0
- data/README.md +506 -0
- data/Rakefile +124 -0
- data/bin/load_dev +22 -0
- data/bin/tng +1096 -0
- data/binaries/tng.bundle +0 -0
- data/binaries/tng.so +0 -0
- data/lib/generators/tng/install_generator.rb +228 -0
- data/lib/tng/analyzers/controller.rb +114 -0
- data/lib/tng/analyzers/model.rb +122 -0
- data/lib/tng/analyzers/service.rb +149 -0
- data/lib/tng/api/http_client.rb +102 -0
- data/lib/tng/railtie.rb +11 -0
- data/lib/tng/services/test_generator.rb +159 -0
- data/lib/tng/services/testng.rb +100 -0
- data/lib/tng/services/user_app_config.rb +77 -0
- data/lib/tng/ui/about_display.rb +71 -0
- data/lib/tng/ui/configuration_display.rb +46 -0
- data/lib/tng/ui/controller_test_flow_display.rb +89 -0
- data/lib/tng/ui/display_banner.rb +54 -0
- data/lib/tng/ui/goodbye_display.rb +50 -0
- data/lib/tng/ui/model_test_flow_display.rb +95 -0
- data/lib/tng/ui/post_install_box.rb +73 -0
- data/lib/tng/ui/service_test_flow_display.rb +89 -0
- data/lib/tng/ui/show_help.rb +89 -0
- data/lib/tng/ui/system_status_display.rb +57 -0
- data/lib/tng/ui/user_stats_display.rb +153 -0
- data/lib/tng/utils.rb +263 -0
- data/lib/tng/version.rb +6 -0
- data/lib/tng.rb +294 -0
- data/tng.gemspec +54 -0
- metadata +283 -0
data/bin/tng
ADDED
@@ -0,0 +1,1096 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Set a clean process name for the terminal
|
5
|
+
$0 = "tng"
|
6
|
+
|
7
|
+
# Add the lib directory to the load path for development
|
8
|
+
lib_path = File.expand_path("../lib", __dir__)
|
9
|
+
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
10
|
+
|
11
|
+
# Load the main gem first to ensure the Rust extension is loaded
|
12
|
+
require "tng"
|
13
|
+
|
14
|
+
require "tty-prompt"
|
15
|
+
require "tty-spinner"
|
16
|
+
require "tty-box"
|
17
|
+
require "tty-table"
|
18
|
+
require "tty-progressbar"
|
19
|
+
require "tty-screen"
|
20
|
+
require "tty-option"
|
21
|
+
require "pastel"
|
22
|
+
require "tng/ui/show_help"
|
23
|
+
require "tng/ui/post_install_box"
|
24
|
+
require "tng/ui/system_status_display"
|
25
|
+
require "tng/ui/configuration_display"
|
26
|
+
require "tng/ui/display_banner"
|
27
|
+
require "tng/ui/user_stats_display"
|
28
|
+
require "tng/ui/about_display"
|
29
|
+
require "tng/services/testng"
|
30
|
+
require "tng/ui/goodbye_display"
|
31
|
+
require "tng/api/http_client"
|
32
|
+
require "tng/ui/controller_test_flow_display"
|
33
|
+
require "tng/ui/model_test_flow_display"
|
34
|
+
require "tng/ui/service_test_flow_display"
|
35
|
+
require "tng/analyzers/controller"
|
36
|
+
require "tng/analyzers/model"
|
37
|
+
require "tng/analyzers/service"
|
38
|
+
require "tng/services/test_generator"
|
39
|
+
require "tng/services/user_app_config"
|
40
|
+
require "tng/utils"
|
41
|
+
|
42
|
+
class CLI
|
43
|
+
include TTY::Option
|
44
|
+
include Tng::Utils
|
45
|
+
|
46
|
+
usage do
|
47
|
+
program "tng"
|
48
|
+
desc "LLM-Powered Rails Test Generator"
|
49
|
+
example "Interactive mode (full UI with search & selection):", "bundle exec tng"
|
50
|
+
example " • Browse and search controllers/models", ""
|
51
|
+
example " • Select specific files from filtered lists", ""
|
52
|
+
example " • Choose test generation options", ""
|
53
|
+
example "", ""
|
54
|
+
example "Direct generation (bypass UI):", ""
|
55
|
+
example "Standard format:", "bundle exec tng --type=controller --file=users_controller"
|
56
|
+
example "Short aliases:", "bundle exec tng -t c -f ping"
|
57
|
+
example "Mixed format:", "bundle exec tng -t=c f=ping"
|
58
|
+
example "All equivalent:", "bundle exec tng --type=model --file=user"
|
59
|
+
example " ", "bundle exec tng -t m -f user"
|
60
|
+
example " ", "bundle exec tng -t=m f=user"
|
61
|
+
end
|
62
|
+
|
63
|
+
option :type do
|
64
|
+
short "-t"
|
65
|
+
long "--type=TYPE"
|
66
|
+
desc "Test type (controller, model, service)"
|
67
|
+
permit %w[controller model service c m mo s se]
|
68
|
+
end
|
69
|
+
|
70
|
+
option :file do
|
71
|
+
short "-f"
|
72
|
+
long "--file=FILE"
|
73
|
+
desc "File name (without .rb extension)"
|
74
|
+
end
|
75
|
+
|
76
|
+
option :method do
|
77
|
+
short "-m"
|
78
|
+
long "--method=METHOD"
|
79
|
+
desc "Method name (for per-method tests)"
|
80
|
+
end
|
81
|
+
|
82
|
+
flag :help do
|
83
|
+
short "-h"
|
84
|
+
long "--help"
|
85
|
+
desc "Show help message"
|
86
|
+
end
|
87
|
+
|
88
|
+
def initialize
|
89
|
+
@prompt = TTY::Prompt.new
|
90
|
+
@pastel = Pastel.new
|
91
|
+
@terminal_width = begin
|
92
|
+
TTY::Screen.width
|
93
|
+
rescue StandardError
|
94
|
+
80
|
95
|
+
end
|
96
|
+
@config_initialized = false
|
97
|
+
@http_client = nil
|
98
|
+
@testng = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def start
|
102
|
+
# Preprocess ARGV to handle mixed formats like: -t=value or f=value
|
103
|
+
normalized_argv = preprocess_arguments(ARGV.dup)
|
104
|
+
|
105
|
+
parse(normalized_argv)
|
106
|
+
|
107
|
+
if params[:help]
|
108
|
+
help_display = ShowHelp.new(@pastel, Tng::VERSION)
|
109
|
+
puts help_display.render
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
# Load Rails environment if we're in a Rails application (only for non-help commands)
|
114
|
+
rails_loaded = load_rails_environment
|
115
|
+
|
116
|
+
# Initialize config and clients after Rails is loaded
|
117
|
+
initialize_config_and_clients if rails_loaded
|
118
|
+
|
119
|
+
if params[:type] && params[:file]
|
120
|
+
# Initialize config for direct generation if not already done
|
121
|
+
initialize_config_and_clients unless @config_initialized
|
122
|
+
run_direct_generation
|
123
|
+
else
|
124
|
+
# Check if we're in a Rails app and if Rails loaded properly
|
125
|
+
if find_rails_root && !rails_loaded && !defined?(Rails)
|
126
|
+
puts @pastel.red("❌ Failed to load Rails environment.")
|
127
|
+
puts @pastel.yellow("Please ensure you're running this command from a Rails application directory.")
|
128
|
+
puts @pastel.dim("Try running: bundle exec tng")
|
129
|
+
return
|
130
|
+
end
|
131
|
+
|
132
|
+
# Check if configuration is set up before proceeding
|
133
|
+
return unless check_configuration
|
134
|
+
|
135
|
+
check_system_status
|
136
|
+
clear_screen
|
137
|
+
puts DisplayBanner.new(@pastel, Tng::VERSION).render
|
138
|
+
main_menu
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def preprocess_arguments(argv)
|
143
|
+
normalized = []
|
144
|
+
|
145
|
+
argv.each do |arg|
|
146
|
+
case arg
|
147
|
+
when /^(type|t)=(.+)$/ # Handle type=value or t=value (without dash)
|
148
|
+
normalized << "--type=#{::Regexp.last_match(2)}"
|
149
|
+
when /^(file|f)=(.+)$/ # Handle file=value or f=value (without dash)
|
150
|
+
normalized << "--file=#{::Regexp.last_match(2)}"
|
151
|
+
when /^(method|m)=(.+)$/ # Handle method=value or m=value (without dash)
|
152
|
+
normalized << "--method=#{::Regexp.last_match(2)}"
|
153
|
+
when /^(help|h)=(.+)$/ # Handle help=value or h=value (without dash)
|
154
|
+
normalized << "--help=#{::Regexp.last_match(2)}"
|
155
|
+
when /^-([tf])=(.+)$/ # Handle -t=value or -f=value (convert to long form)
|
156
|
+
key = ::Regexp.last_match(1) == "t" ? "type" : "file"
|
157
|
+
normalized << "--#{key}=#{::Regexp.last_match(2)}"
|
158
|
+
when /^-([mh])=(.+)$/ # Handle -m=value or -h=value
|
159
|
+
key = ::Regexp.last_match(1) == "m" ? "method" : "help"
|
160
|
+
normalized << "--#{key}=#{::Regexp.last_match(2)}"
|
161
|
+
when /^--\w+=.+$/ # Handle --key=value (already correct)
|
162
|
+
normalized << arg
|
163
|
+
when /^-[tfmh]$/ # Handle single flags like -t, -f, -m, -h (keep as is)
|
164
|
+
normalized << arg
|
165
|
+
when /^--\w+$/ # Handle long flags like --help (keep as is)
|
166
|
+
normalized << arg
|
167
|
+
else
|
168
|
+
# Regular arguments or values
|
169
|
+
normalized << arg
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
normalized
|
174
|
+
end
|
175
|
+
|
176
|
+
def main_menu
|
177
|
+
loop do
|
178
|
+
clear_screen
|
179
|
+
puts DisplayBanner.new(@pastel, Tng::VERSION).render
|
180
|
+
|
181
|
+
choice = @prompt.select(
|
182
|
+
center_text("Select an option:"),
|
183
|
+
cycle: true,
|
184
|
+
per_page: 5,
|
185
|
+
symbols: { marker: "→" }
|
186
|
+
) do |menu|
|
187
|
+
menu.choice "Generate tests", :tests
|
188
|
+
menu.choice "User stats", :stats
|
189
|
+
menu.choice "About", :about
|
190
|
+
menu.choice "Exit", :exit, key: "q"
|
191
|
+
end
|
192
|
+
|
193
|
+
handle_choice(choice)
|
194
|
+
break if choice == :exit
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def handle_choice(choice)
|
199
|
+
case choice
|
200
|
+
when :tests
|
201
|
+
handle_test_generation
|
202
|
+
when :stats
|
203
|
+
user_stats
|
204
|
+
when :about
|
205
|
+
show_about
|
206
|
+
when :exit
|
207
|
+
show_goodbye
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def handle_test_generation
|
212
|
+
choice = @prompt.select(
|
213
|
+
center_text("What would you like to test?"),
|
214
|
+
cycle: true,
|
215
|
+
per_page: 5,
|
216
|
+
symbols: { marker: "→" }
|
217
|
+
) do |menu|
|
218
|
+
menu.choice "Controller", :controller
|
219
|
+
menu.choice "Model", :model
|
220
|
+
menu.choice "Service", :service
|
221
|
+
menu.choice @pastel.cyan("⬅️ Back"), :back
|
222
|
+
end
|
223
|
+
|
224
|
+
case choice
|
225
|
+
when :controller
|
226
|
+
generate_controller_tests
|
227
|
+
when :model
|
228
|
+
generate_model_tests
|
229
|
+
when :service
|
230
|
+
generate_service_tests
|
231
|
+
when :route
|
232
|
+
generate_route_tests
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def generate_controller_tests
|
237
|
+
header = @pastel.bright_white.bold("Scanning for controllers...")
|
238
|
+
puts center_text(header)
|
239
|
+
|
240
|
+
spinner = TTY::Spinner.new(
|
241
|
+
center_text("[:spinner] Analyzing controllers..."),
|
242
|
+
format: :dots,
|
243
|
+
success_mark: @pastel.green("✅"),
|
244
|
+
error_mark: @pastel.red("❌")
|
245
|
+
)
|
246
|
+
spinner.auto_spin
|
247
|
+
controllers = Tng::Analyzers::Controller.files_in_dir("app/controllers").map do |file|
|
248
|
+
# Extract the full path relative to app/controllers to build proper namespace
|
249
|
+
relative_path = file[:path].gsub(%r{^.*app/controllers/}, "").gsub(".rb", "")
|
250
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
251
|
+
|
252
|
+
{
|
253
|
+
name: namespaced_name,
|
254
|
+
path: file[:path]
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
flow_display = ControllerTestFlowDisplay.new(@prompt, @pastel)
|
259
|
+
|
260
|
+
if controllers.empty?
|
261
|
+
spinner.error
|
262
|
+
flow_display.show_no_controllers_message
|
263
|
+
return
|
264
|
+
end
|
265
|
+
|
266
|
+
success_msg = @pastel.green("Found #{controllers.length} controllers")
|
267
|
+
spinner.success(center_text(success_msg))
|
268
|
+
|
269
|
+
controller_choice = flow_display.select_controller(controllers)
|
270
|
+
return if controller_choice == :back
|
271
|
+
|
272
|
+
show_controller_test_options(controller_choice)
|
273
|
+
end
|
274
|
+
|
275
|
+
def generate_model_tests
|
276
|
+
header = @pastel.bright_white.bold("Scanning for models...")
|
277
|
+
puts center_text(header)
|
278
|
+
|
279
|
+
spinner = TTY::Spinner.new(
|
280
|
+
center_text("[:spinner] Analyzing models..."),
|
281
|
+
format: :dots,
|
282
|
+
success_mark: @pastel.green("✅"),
|
283
|
+
error_mark: @pastel.red("❌")
|
284
|
+
)
|
285
|
+
spinner.auto_spin
|
286
|
+
|
287
|
+
models = Tng::Analyzers::Model.files_in_dir("app/models").map do |file|
|
288
|
+
# Extract the full path relative to app/models to build proper namespace
|
289
|
+
relative_path = file[:path].gsub(%r{^.*app/models/}, "").gsub(".rb", "")
|
290
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
291
|
+
|
292
|
+
{
|
293
|
+
name: namespaced_name,
|
294
|
+
path: file[:path]
|
295
|
+
}
|
296
|
+
end
|
297
|
+
|
298
|
+
flow_display = ModelTestFlowDisplay.new(@prompt, @pastel)
|
299
|
+
|
300
|
+
if models.empty?
|
301
|
+
spinner.error
|
302
|
+
flow_display.show_no_models_message
|
303
|
+
return
|
304
|
+
end
|
305
|
+
|
306
|
+
success_msg = @pastel.green("Found #{models.length} models")
|
307
|
+
spinner.success(center_text(success_msg))
|
308
|
+
|
309
|
+
model_choice = flow_display.select_model(models)
|
310
|
+
return if model_choice == :back
|
311
|
+
|
312
|
+
show_model_test_options(model_choice)
|
313
|
+
end
|
314
|
+
|
315
|
+
def generate_service_tests
|
316
|
+
header = @pastel.bright_white.bold("Scanning for services...")
|
317
|
+
puts center_text(header)
|
318
|
+
|
319
|
+
spinner = TTY::Spinner.new(
|
320
|
+
center_text("[:spinner] Analyzing services..."),
|
321
|
+
format: :dots,
|
322
|
+
success_mark: @pastel.green("✅"),
|
323
|
+
error_mark: @pastel.red("❌")
|
324
|
+
)
|
325
|
+
spinner.auto_spin
|
326
|
+
|
327
|
+
services = Tng::Analyzers::Service.files_in_dir.map do |file|
|
328
|
+
# Extract the full path relative to app/services or app/service to build proper namespace
|
329
|
+
relative_path = file[:path].gsub(%r{^.*app/services?/}, "").gsub(".rb", "")
|
330
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
331
|
+
|
332
|
+
{
|
333
|
+
name: namespaced_name,
|
334
|
+
path: file[:path]
|
335
|
+
}
|
336
|
+
end
|
337
|
+
|
338
|
+
flow_display = ServiceTestFlowDisplay.new(@prompt, @pastel)
|
339
|
+
|
340
|
+
if services.empty?
|
341
|
+
spinner.error
|
342
|
+
flow_display.show_no_services_message
|
343
|
+
return
|
344
|
+
end
|
345
|
+
|
346
|
+
success_msg = @pastel.green("Found #{services.length} services")
|
347
|
+
spinner.success(center_text(success_msg))
|
348
|
+
|
349
|
+
service_choice = flow_display.select_service(services)
|
350
|
+
return if service_choice == :back
|
351
|
+
|
352
|
+
show_service_test_options(service_choice)
|
353
|
+
end
|
354
|
+
|
355
|
+
def generate_test_for_controller(controller)
|
356
|
+
header = @pastel.bright_white.bold("🎯 Generating tests for #{controller[:name]}")
|
357
|
+
puts center_text(header)
|
358
|
+
|
359
|
+
# Show controller analysis
|
360
|
+
analysis_msg = @pastel.dim("Analyzing controller structure...")
|
361
|
+
puts center_text(analysis_msg)
|
362
|
+
|
363
|
+
# Center the progress bar
|
364
|
+
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
365
|
+
progress_bar = TTY::ProgressBar.new(
|
366
|
+
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
367
|
+
total: 5,
|
368
|
+
bar_format: :box,
|
369
|
+
complete: @pastel.green("█"),
|
370
|
+
incomplete: @pastel.dim("░"),
|
371
|
+
width: 40
|
372
|
+
)
|
373
|
+
|
374
|
+
step_msg = @pastel.dim("Parsing controller file...")
|
375
|
+
puts center_text(step_msg)
|
376
|
+
progress_bar.advance(1)
|
377
|
+
sleep(0.3)
|
378
|
+
|
379
|
+
step_msg = @pastel.dim("Extracting actions...")
|
380
|
+
puts center_text(step_msg)
|
381
|
+
progress_bar.advance(1)
|
382
|
+
sleep(0.3)
|
383
|
+
|
384
|
+
step_msg = @pastel.dim("Analyzing dependencies...")
|
385
|
+
puts center_text(step_msg)
|
386
|
+
progress_bar.advance(1)
|
387
|
+
sleep(0.3)
|
388
|
+
|
389
|
+
step_msg = @pastel.dim("Generating test code...")
|
390
|
+
puts center_text(step_msg)
|
391
|
+
progress_bar.advance(1)
|
392
|
+
# test for the controller
|
393
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_controller(controller)
|
394
|
+
sleep(0.5)
|
395
|
+
|
396
|
+
step_msg = @pastel.dim("Writing test file...")
|
397
|
+
puts center_text(step_msg)
|
398
|
+
progress_bar.advance(1)
|
399
|
+
sleep(0.2)
|
400
|
+
|
401
|
+
completion_msg = @pastel.green.bold("✅ Test generation completed!")
|
402
|
+
puts center_text(completion_msg)
|
403
|
+
|
404
|
+
# Show post-generation menu if test was generated successfully
|
405
|
+
if result && result[:file_path]
|
406
|
+
show_post_generation_menu(result)
|
407
|
+
else
|
408
|
+
@prompt.keypress(center_text(@pastel.dim("Press any key to continue...")))
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def show_controller_method_selection(controller)
|
413
|
+
header = @pastel.bright_white.bold("🔍 Analyzing methods for #{controller[:name]}")
|
414
|
+
puts center_text(header)
|
415
|
+
|
416
|
+
# Extract methods from controller
|
417
|
+
methods = extract_controller_methods(controller)
|
418
|
+
|
419
|
+
if methods.empty?
|
420
|
+
flow_display = ControllerTestFlowDisplay.new(@prompt, @pastel)
|
421
|
+
flow_display.show_no_methods_message(controller)
|
422
|
+
return
|
423
|
+
end
|
424
|
+
|
425
|
+
# Show method selection UI
|
426
|
+
flow_display = ControllerTestFlowDisplay.new(@prompt, @pastel)
|
427
|
+
method_choice = flow_display.select_controller_method(controller, methods)
|
428
|
+
|
429
|
+
return if method_choice == :back
|
430
|
+
|
431
|
+
# Generate test for selected method
|
432
|
+
generate_test_for_controller_method(controller, method_choice)
|
433
|
+
end
|
434
|
+
|
435
|
+
def extract_controller_methods(controller)
|
436
|
+
methods = Tng::Analyzers::Controller.methods_for_controller(controller[:name])
|
437
|
+
Array(methods)
|
438
|
+
rescue StandardError => e
|
439
|
+
puts center_text(@pastel.red("❌ Error analyzing controller: #{e.message}"))
|
440
|
+
[]
|
441
|
+
end
|
442
|
+
|
443
|
+
def generate_test_for_controller_method(controller, method_info)
|
444
|
+
header = @pastel.bright_white.bold("🎯 Generating test for #{controller[:name]}##{method_info[:name]}")
|
445
|
+
puts center_text(header)
|
446
|
+
|
447
|
+
# Show method analysis
|
448
|
+
analysis_msg = @pastel.dim("Analyzing method structure...")
|
449
|
+
puts center_text(analysis_msg)
|
450
|
+
|
451
|
+
# Center the progress bar
|
452
|
+
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
453
|
+
progress_bar = TTY::ProgressBar.new(
|
454
|
+
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
455
|
+
total: 5,
|
456
|
+
bar_format: :box,
|
457
|
+
complete: @pastel.green("█"),
|
458
|
+
incomplete: @pastel.dim("░"),
|
459
|
+
width: 40
|
460
|
+
)
|
461
|
+
|
462
|
+
step_msg = @pastel.dim("Parsing method details...")
|
463
|
+
puts center_text(step_msg)
|
464
|
+
progress_bar.advance(1)
|
465
|
+
sleep(0.3)
|
466
|
+
|
467
|
+
step_msg = @pastel.dim("Analyzing method dependencies...")
|
468
|
+
puts center_text(step_msg)
|
469
|
+
progress_bar.advance(1)
|
470
|
+
sleep(0.3)
|
471
|
+
|
472
|
+
step_msg = @pastel.dim("Extracting method context...")
|
473
|
+
puts center_text(step_msg)
|
474
|
+
progress_bar.advance(1)
|
475
|
+
sleep(0.3)
|
476
|
+
|
477
|
+
step_msg = @pastel.dim("Generating method test code...")
|
478
|
+
puts center_text(step_msg)
|
479
|
+
progress_bar.advance(1)
|
480
|
+
# Generate test for the specific method
|
481
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_controller_method(controller, method_info)
|
482
|
+
sleep(0.5)
|
483
|
+
|
484
|
+
step_msg = @pastel.dim("Writing test file...")
|
485
|
+
puts center_text(step_msg)
|
486
|
+
progress_bar.advance(1)
|
487
|
+
sleep(0.2)
|
488
|
+
|
489
|
+
completion_msg = @pastel.green.bold("✅ Method test generation completed!")
|
490
|
+
puts center_text(completion_msg)
|
491
|
+
|
492
|
+
# Show post-generation menu if test was generated successfully
|
493
|
+
if result && result[:file_path]
|
494
|
+
show_post_generation_menu(result)
|
495
|
+
else
|
496
|
+
@prompt.keypress(center_text(@pastel.dim("Press any key to continue...")))
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
def show_controller_test_options(controller_choice)
|
501
|
+
flow_display = ControllerTestFlowDisplay.new(@prompt, @pastel)
|
502
|
+
test_option = flow_display.select_test_option(controller_choice)
|
503
|
+
|
504
|
+
return if test_option == :back
|
505
|
+
|
506
|
+
case test_option
|
507
|
+
when :all_possible_tests
|
508
|
+
generate_test_for_controller(controller_choice)
|
509
|
+
when :per_method
|
510
|
+
show_controller_method_selection(controller_choice)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def show_model_test_options(model_choice)
|
515
|
+
flow_display = ModelTestFlowDisplay.new(@prompt, @pastel)
|
516
|
+
test_option = flow_display.select_test_option(model_choice)
|
517
|
+
|
518
|
+
return if test_option == :back
|
519
|
+
|
520
|
+
case test_option
|
521
|
+
when :all_possible_tests
|
522
|
+
generate_test_for_model(model_choice)
|
523
|
+
when :per_method
|
524
|
+
show_model_method_selection(model_choice)
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
def show_service_test_options(service_choice)
|
529
|
+
flow_display = ServiceTestFlowDisplay.new(@prompt, @pastel)
|
530
|
+
test_option = flow_display.select_test_option(service_choice)
|
531
|
+
|
532
|
+
return if test_option == :back
|
533
|
+
|
534
|
+
case test_option
|
535
|
+
when :all_possible_tests
|
536
|
+
generate_test_for_service(service_choice)
|
537
|
+
when :per_method
|
538
|
+
show_service_method_selection(service_choice)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
def generate_test_for_model(model)
|
543
|
+
header = @pastel.bright_white.bold("🎯 Generating tests for #{model[:name]}")
|
544
|
+
puts center_text(header)
|
545
|
+
|
546
|
+
# Show model analysis
|
547
|
+
analysis_msg = @pastel.dim("Analyzing model structure...")
|
548
|
+
puts center_text(analysis_msg)
|
549
|
+
|
550
|
+
# Center the progress bar
|
551
|
+
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
552
|
+
progress_bar = TTY::ProgressBar.new(
|
553
|
+
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
554
|
+
total: 4,
|
555
|
+
bar_format: :box,
|
556
|
+
complete: @pastel.green("█"),
|
557
|
+
incomplete: @pastel.dim("░"),
|
558
|
+
width: 40
|
559
|
+
)
|
560
|
+
|
561
|
+
step_msg = @pastel.dim("Parsing model file...")
|
562
|
+
puts center_text(step_msg)
|
563
|
+
progress_bar.advance(1)
|
564
|
+
sleep(0.3)
|
565
|
+
|
566
|
+
step_msg = @pastel.dim("Analyzing validations...")
|
567
|
+
puts center_text(step_msg)
|
568
|
+
progress_bar.advance(1)
|
569
|
+
sleep(0.3)
|
570
|
+
|
571
|
+
step_msg = @pastel.dim("Analyzing associations...")
|
572
|
+
puts center_text(step_msg)
|
573
|
+
progress_bar.advance(1)
|
574
|
+
sleep(0.3)
|
575
|
+
|
576
|
+
step_msg = @pastel.dim("Generating test file...")
|
577
|
+
puts center_text(step_msg)
|
578
|
+
progress_bar.advance(1)
|
579
|
+
sleep(0.3)
|
580
|
+
|
581
|
+
# test for the model
|
582
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_model(model)
|
583
|
+
sleep(0.5)
|
584
|
+
|
585
|
+
step_msg = @pastel.dim("Writing test file...")
|
586
|
+
puts center_text(step_msg)
|
587
|
+
progress_bar.advance(1)
|
588
|
+
sleep(0.2)
|
589
|
+
p result
|
590
|
+
completion_msg = @pastel.green.bold("✅ Test generation completed!")
|
591
|
+
puts center_text(completion_msg)
|
592
|
+
|
593
|
+
# Show post-generation menu if test was generated successfully
|
594
|
+
if result && result[:file_path]
|
595
|
+
show_post_generation_menu(result)
|
596
|
+
else
|
597
|
+
@prompt.keypress(center_text(@pastel.dim("Press any key to continue...")))
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
def show_model_method_selection(model)
|
602
|
+
header = @pastel.bright_white.bold("🔍 Analyzing methods for #{model[:name]}")
|
603
|
+
puts center_text(header)
|
604
|
+
|
605
|
+
# Extract methods from model
|
606
|
+
methods = extract_model_methods(model)
|
607
|
+
|
608
|
+
if methods.empty?
|
609
|
+
flow_display = ModelTestFlowDisplay.new(@prompt, @pastel)
|
610
|
+
flow_display.show_no_methods_message(model)
|
611
|
+
return
|
612
|
+
end
|
613
|
+
|
614
|
+
# Show method selection UI
|
615
|
+
flow_display = ModelTestFlowDisplay.new(@prompt, @pastel)
|
616
|
+
method_choice = flow_display.select_model_method(model, methods)
|
617
|
+
|
618
|
+
return if method_choice == :back
|
619
|
+
|
620
|
+
# Generate test for selected method
|
621
|
+
generate_test_for_model_method(model, method_choice)
|
622
|
+
end
|
623
|
+
|
624
|
+
def extract_model_methods(model)
|
625
|
+
methods = Tng::Analyzers::Model.methods_for_model(model[:name])
|
626
|
+
Array(methods)
|
627
|
+
rescue StandardError => e
|
628
|
+
puts center_text(@pastel.red("❌ Error analyzing model: #{e.message}"))
|
629
|
+
[]
|
630
|
+
end
|
631
|
+
|
632
|
+
def extract_service_methods(service)
|
633
|
+
methods = Tng::Analyzers::Service.methods_for_service(service[:name])
|
634
|
+
Array(methods)
|
635
|
+
rescue StandardError => e
|
636
|
+
puts center_text(@pastel.red("❌ Error analyzing service: #{e.message}"))
|
637
|
+
[]
|
638
|
+
end
|
639
|
+
|
640
|
+
def generate_test_for_model_method(model, method_info)
|
641
|
+
header = @pastel.bright_white.bold("🎯 Generating test for #{model[:name]}##{method_info[:name]}")
|
642
|
+
puts center_text(header)
|
643
|
+
|
644
|
+
# Show method analysis
|
645
|
+
analysis_msg = @pastel.dim("Analyzing method structure...")
|
646
|
+
puts center_text(analysis_msg)
|
647
|
+
|
648
|
+
# Center the progress bar
|
649
|
+
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
650
|
+
progress_bar = TTY::ProgressBar.new(
|
651
|
+
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
652
|
+
total: 4,
|
653
|
+
bar_format: :box,
|
654
|
+
complete: @pastel.green("█"),
|
655
|
+
incomplete: @pastel.dim("░"),
|
656
|
+
width: 40
|
657
|
+
)
|
658
|
+
|
659
|
+
step_msg = @pastel.dim("Parsing method details...")
|
660
|
+
puts center_text(step_msg)
|
661
|
+
progress_bar.advance(1)
|
662
|
+
sleep(0.3)
|
663
|
+
|
664
|
+
step_msg = @pastel.dim("Analyzing method dependencies...")
|
665
|
+
puts center_text(step_msg)
|
666
|
+
progress_bar.advance(1)
|
667
|
+
sleep(0.3)
|
668
|
+
|
669
|
+
step_msg = @pastel.dim("Generating method test code...")
|
670
|
+
puts center_text(step_msg)
|
671
|
+
progress_bar.advance(1)
|
672
|
+
# Generate test for the specific method
|
673
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_model_method(model, method_info)
|
674
|
+
sleep(0.5)
|
675
|
+
|
676
|
+
step_msg = @pastel.dim("Writing test file...")
|
677
|
+
puts center_text(step_msg)
|
678
|
+
progress_bar.advance(1)
|
679
|
+
sleep(0.2)
|
680
|
+
|
681
|
+
completion_msg = @pastel.green.bold("✅ Method test generation completed!")
|
682
|
+
puts center_text(completion_msg)
|
683
|
+
|
684
|
+
# Show post-generation menu if test was generated successfully
|
685
|
+
if result && result[:file_path]
|
686
|
+
show_post_generation_menu(result)
|
687
|
+
else
|
688
|
+
@prompt.keypress(center_text(@pastel.dim("Press any key to continue...")))
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
def generate_test_for_service_method(service, method_info)
|
693
|
+
header = @pastel.bright_white.bold("🎯 Generating test for #{service[:name]}##{method_info[:name]}")
|
694
|
+
puts center_text(header)
|
695
|
+
|
696
|
+
# Show method analysis
|
697
|
+
analysis_msg = @pastel.dim("Analyzing method structure...")
|
698
|
+
puts center_text(analysis_msg)
|
699
|
+
|
700
|
+
# Center the progress bar
|
701
|
+
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
702
|
+
progress_bar = TTY::ProgressBar.new(
|
703
|
+
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
704
|
+
total: 4,
|
705
|
+
bar_format: :box,
|
706
|
+
complete: @pastel.green("█"),
|
707
|
+
incomplete: @pastel.dim("░"),
|
708
|
+
width: 40
|
709
|
+
)
|
710
|
+
|
711
|
+
step_msg = @pastel.dim("Parsing method details...")
|
712
|
+
puts center_text(step_msg)
|
713
|
+
progress_bar.advance(1)
|
714
|
+
sleep(0.3)
|
715
|
+
|
716
|
+
step_msg = @pastel.dim("Analyzing method dependencies...")
|
717
|
+
puts center_text(step_msg)
|
718
|
+
progress_bar.advance(1)
|
719
|
+
sleep(0.3)
|
720
|
+
|
721
|
+
step_msg = @pastel.dim("Generating method test code...")
|
722
|
+
puts center_text(step_msg)
|
723
|
+
progress_bar.advance(1)
|
724
|
+
# Generate test for the specific method
|
725
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_service_method(service, method_info)
|
726
|
+
sleep(0.5)
|
727
|
+
|
728
|
+
step_msg = @pastel.dim("Writing test file...")
|
729
|
+
puts center_text(step_msg)
|
730
|
+
progress_bar.advance(1)
|
731
|
+
sleep(0.2)
|
732
|
+
|
733
|
+
completion_msg = @pastel.green.bold("✅ Method test generation completed!")
|
734
|
+
puts center_text(completion_msg)
|
735
|
+
|
736
|
+
# Show post-generation menu if test was generated successfully
|
737
|
+
if result && result[:file_path]
|
738
|
+
show_post_generation_menu(result)
|
739
|
+
else
|
740
|
+
@prompt.keypress(center_text(@pastel.dim("Press any key to continue...")))
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
def show_service_method_selection(service)
|
745
|
+
header = @pastel.bright_white.bold("🔍 Analyzing methods for #{service[:name]}")
|
746
|
+
puts center_text(header)
|
747
|
+
|
748
|
+
# Extract methods from service
|
749
|
+
methods = extract_service_methods(service)
|
750
|
+
|
751
|
+
if methods.empty?
|
752
|
+
flow_display = ServiceTestFlowDisplay.new(@prompt, @pastel)
|
753
|
+
flow_display.show_no_methods_message(service)
|
754
|
+
return
|
755
|
+
end
|
756
|
+
|
757
|
+
# Show method selection UI
|
758
|
+
flow_display = ServiceTestFlowDisplay.new(@prompt, @pastel)
|
759
|
+
method_choice = flow_display.select_service_method(service, methods)
|
760
|
+
|
761
|
+
return if method_choice == :back
|
762
|
+
|
763
|
+
# Generate test for selected method
|
764
|
+
generate_test_for_service_method(service, method_choice)
|
765
|
+
end
|
766
|
+
|
767
|
+
def generate_test_for_service(service)
|
768
|
+
header = @pastel.bright_white.bold("🎯 Generating tests for #{service[:name]}")
|
769
|
+
puts center_text(header)
|
770
|
+
|
771
|
+
# Show service analysis
|
772
|
+
analysis_msg = @pastel.dim("Analyzing service structure...")
|
773
|
+
puts center_text(analysis_msg)
|
774
|
+
|
775
|
+
# Center the progress bar
|
776
|
+
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
777
|
+
progress_bar = TTY::ProgressBar.new(
|
778
|
+
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
779
|
+
total: 4,
|
780
|
+
bar_format: :box,
|
781
|
+
complete: @pastel.green("█"),
|
782
|
+
incomplete: @pastel.dim("░"),
|
783
|
+
width: 40
|
784
|
+
)
|
785
|
+
|
786
|
+
step_msg = @pastel.dim("Parsing service file...")
|
787
|
+
puts center_text(step_msg)
|
788
|
+
progress_bar.advance(1)
|
789
|
+
sleep(0.3)
|
790
|
+
|
791
|
+
step_msg = @pastel.dim("Analyzing methods...")
|
792
|
+
puts center_text(step_msg)
|
793
|
+
progress_bar.advance(1)
|
794
|
+
sleep(0.3)
|
795
|
+
|
796
|
+
step_msg = @pastel.dim("Analyzing dependencies...")
|
797
|
+
puts center_text(step_msg)
|
798
|
+
progress_bar.advance(1)
|
799
|
+
sleep(0.3)
|
800
|
+
|
801
|
+
step_msg = @pastel.dim("Generating test file...")
|
802
|
+
puts center_text(step_msg)
|
803
|
+
progress_bar.advance(1)
|
804
|
+
sleep(0.3)
|
805
|
+
|
806
|
+
# test for the service
|
807
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_service(service)
|
808
|
+
sleep(0.5)
|
809
|
+
|
810
|
+
step_msg = @pastel.dim("Writing test file...")
|
811
|
+
puts center_text(step_msg)
|
812
|
+
progress_bar.advance(1)
|
813
|
+
sleep(0.2)
|
814
|
+
|
815
|
+
completion_msg = @pastel.green.bold("✅ Test generation completed!")
|
816
|
+
puts center_text(completion_msg)
|
817
|
+
|
818
|
+
# Show post-generation menu if test was generated successfully
|
819
|
+
if result && result[:file_path]
|
820
|
+
show_post_generation_menu(result)
|
821
|
+
else
|
822
|
+
@prompt.keypress(center_text(@pastel.dim("Press any key to continue...")))
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
# STATS
|
827
|
+
def user_stats
|
828
|
+
stats_data = @testng.get_user_stats
|
829
|
+
UserStatsDisplay.new(@pastel, @prompt).display(stats_data)
|
830
|
+
end
|
831
|
+
|
832
|
+
def check_system_status
|
833
|
+
# Only show status check in interactive mode
|
834
|
+
puts @pastel.dim("🔍 Checking system status...") unless params[:type] && params[:file]
|
835
|
+
|
836
|
+
status = @testng.check_system_status
|
837
|
+
status_ok = SystemStatusDisplay.new(@pastel, params).display(status)
|
838
|
+
|
839
|
+
return if status_ok
|
840
|
+
|
841
|
+
puts
|
842
|
+
puts @pastel.dim("Press any key to exit...")
|
843
|
+
$stdin.getch
|
844
|
+
exit(1)
|
845
|
+
end
|
846
|
+
|
847
|
+
def check_configuration
|
848
|
+
return true if Tng::Services::UserAppConfig.configured?
|
849
|
+
|
850
|
+
missing = Tng::Services::UserAppConfig.missing_config
|
851
|
+
|
852
|
+
ConfigurationDisplay.new(@pastel).display_missing_config(missing)
|
853
|
+
false
|
854
|
+
end
|
855
|
+
|
856
|
+
# ABOUT
|
857
|
+
def show_about
|
858
|
+
AboutDisplay.new(@pastel, @prompt, Tng::VERSION).display
|
859
|
+
end
|
860
|
+
|
861
|
+
# GOODBYE
|
862
|
+
def show_goodbye
|
863
|
+
clear_screen
|
864
|
+
GoodbyeDisplay.new(@pastel).display
|
865
|
+
end
|
866
|
+
|
867
|
+
def run_direct_generation
|
868
|
+
# Normalize type aliases
|
869
|
+
type = normalize_type(params[:type])
|
870
|
+
|
871
|
+
unless %w[controller model service].include?(type)
|
872
|
+
puts @pastel.red("❌ Invalid type: #{params[:type]}")
|
873
|
+
puts @pastel.yellow("Supported types: controller, model, service")
|
874
|
+
return
|
875
|
+
end
|
876
|
+
|
877
|
+
# Check configuration first
|
878
|
+
return unless check_configuration
|
879
|
+
|
880
|
+
# Check system status (but don't show the spinner in direct mode)
|
881
|
+
status = @testng.check_system_status
|
882
|
+
unless SystemStatusDisplay.new(@pastel, params).display(status)
|
883
|
+
puts @pastel.red("❌ System status check failed")
|
884
|
+
return
|
885
|
+
end
|
886
|
+
|
887
|
+
case type
|
888
|
+
when "controller"
|
889
|
+
run_direct_controller_generation
|
890
|
+
when "model"
|
891
|
+
run_direct_model_generation
|
892
|
+
when "service"
|
893
|
+
run_direct_service_generation
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
def normalize_type(type_param)
|
898
|
+
case type_param&.downcase
|
899
|
+
when "c", "controller"
|
900
|
+
"controller"
|
901
|
+
when "m", "mo", "model"
|
902
|
+
"model"
|
903
|
+
when "s", "se", "service"
|
904
|
+
"service"
|
905
|
+
else
|
906
|
+
type_param&.downcase
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
def run_direct_controller_generation
|
911
|
+
file_name = params[:file]
|
912
|
+
file_name += "_controller" unless file_name.end_with?("_controller")
|
913
|
+
|
914
|
+
# Build the expected path
|
915
|
+
controller_path = File.join("app/controllers", "#{file_name}.rb")
|
916
|
+
|
917
|
+
# Check if file exists
|
918
|
+
unless File.exist?(controller_path)
|
919
|
+
puts @pastel.red("❌ Controller file not found: #{controller_path}")
|
920
|
+
return
|
921
|
+
end
|
922
|
+
|
923
|
+
# Extract the full path relative to app/controllers to build proper namespace
|
924
|
+
relative_path = controller_path.gsub(%r{^.*app/controllers/}, "").gsub(".rb", "")
|
925
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
926
|
+
|
927
|
+
controller = {
|
928
|
+
name: namespaced_name,
|
929
|
+
path: controller_path
|
930
|
+
}
|
931
|
+
|
932
|
+
puts @pastel.bright_white("🎯 Generating tests for #{controller[:name]}...")
|
933
|
+
|
934
|
+
# Generate the test directly without progress bars for cleaner output
|
935
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_controller(controller)
|
936
|
+
|
937
|
+
if result && result[:file_path]
|
938
|
+
show_post_generation_menu(result)
|
939
|
+
else
|
940
|
+
puts @pastel.red("❌ Failed to generate test")
|
941
|
+
end
|
942
|
+
end
|
943
|
+
|
944
|
+
def run_direct_model_generation
|
945
|
+
file_name = params[:file]
|
946
|
+
|
947
|
+
# Build the expected path
|
948
|
+
model_path = File.join("app/models", "#{file_name}.rb")
|
949
|
+
|
950
|
+
# Check if file exists
|
951
|
+
unless File.exist?(model_path)
|
952
|
+
puts @pastel.red("❌ Model file not found: #{model_path}")
|
953
|
+
return
|
954
|
+
end
|
955
|
+
|
956
|
+
# Extract the full path relative to app/models to build proper namespace
|
957
|
+
relative_path = model_path.gsub(%r{^.*app/models/}, "").gsub(".rb", "")
|
958
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
959
|
+
|
960
|
+
model = {
|
961
|
+
name: namespaced_name,
|
962
|
+
path: model_path
|
963
|
+
}
|
964
|
+
|
965
|
+
puts @pastel.bright_white("🎯 Generating tests for #{model[:name]}...")
|
966
|
+
|
967
|
+
# Generate the test directly without progress bars for cleaner output
|
968
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_model(model)
|
969
|
+
|
970
|
+
if result && result[:file_path]
|
971
|
+
show_post_generation_menu(result)
|
972
|
+
else
|
973
|
+
puts @pastel.red("❌ Failed to generate test")
|
974
|
+
end
|
975
|
+
end
|
976
|
+
|
977
|
+
def run_direct_service_generation
|
978
|
+
file_name = params[:file]
|
979
|
+
|
980
|
+
# Try different service directories
|
981
|
+
service_dirs = ["app/services", "app/service"]
|
982
|
+
service_path = nil
|
983
|
+
|
984
|
+
service_dirs.each do |dir|
|
985
|
+
potential_path = File.join(dir, "#{file_name}.rb")
|
986
|
+
if File.exist?(potential_path)
|
987
|
+
service_path = potential_path
|
988
|
+
break
|
989
|
+
end
|
990
|
+
end
|
991
|
+
|
992
|
+
# Check if file exists
|
993
|
+
unless service_path
|
994
|
+
puts @pastel.red("❌ Service file not found: #{file_name}.rb")
|
995
|
+
puts @pastel.yellow("Searched in: #{service_dirs.join(", ")}")
|
996
|
+
return
|
997
|
+
end
|
998
|
+
|
999
|
+
# Extract the full path relative to app/services or app/service to build proper namespace
|
1000
|
+
relative_path = service_path.gsub(%r{^.*app/services?/}, "").gsub(".rb", "")
|
1001
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
1002
|
+
|
1003
|
+
service = {
|
1004
|
+
name: namespaced_name,
|
1005
|
+
path: service_path
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
puts @pastel.bright_white("🎯 Generating tests for #{service[:name]}...")
|
1009
|
+
|
1010
|
+
# Generate the test directly without progress bars for cleaner output
|
1011
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_service(service)
|
1012
|
+
|
1013
|
+
if result && result[:file_path]
|
1014
|
+
show_post_generation_menu(result)
|
1015
|
+
else
|
1016
|
+
puts @pastel.red("❌ Failed to generate test")
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def show_post_generation_menu(result)
|
1021
|
+
# Check if we're in direct mode
|
1022
|
+
is_direct_mode = params[:type] && params[:file]
|
1023
|
+
|
1024
|
+
if is_direct_mode
|
1025
|
+
# In direct mode, show info and exit
|
1026
|
+
puts
|
1027
|
+
header = @pastel.bright_white.bold("🎉 Test Generated Successfully!")
|
1028
|
+
puts center_text(header)
|
1029
|
+
puts
|
1030
|
+
|
1031
|
+
# Show file info
|
1032
|
+
info_msg = [
|
1033
|
+
@pastel.cyan("📁 File: #{result[:file_path]}"),
|
1034
|
+
@pastel.yellow("🏃 Run: #{result[:run_command]}")
|
1035
|
+
].join("\n")
|
1036
|
+
puts center_text(info_msg)
|
1037
|
+
puts
|
1038
|
+
|
1039
|
+
# In direct mode, just show the success message and exit
|
1040
|
+
puts center_text(@pastel.green("✅ Test generation completed successfully!"))
|
1041
|
+
return
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
# Interactive mode - show menu loop
|
1045
|
+
loop do
|
1046
|
+
puts
|
1047
|
+
header = @pastel.bright_white.bold("🎉 Test Generated Successfully!")
|
1048
|
+
puts center_text(header)
|
1049
|
+
puts
|
1050
|
+
|
1051
|
+
# Show file info
|
1052
|
+
info_msg = [
|
1053
|
+
@pastel.cyan("📁 File: #{result[:file_path]}"),
|
1054
|
+
@pastel.yellow("🏃 Run: #{result[:run_command]}")
|
1055
|
+
].join("\n")
|
1056
|
+
puts center_text(info_msg)
|
1057
|
+
puts
|
1058
|
+
|
1059
|
+
choice = @prompt.select(
|
1060
|
+
@pastel.bright_white("What would you like to do?"),
|
1061
|
+
cycle: true,
|
1062
|
+
symbols: { marker: "▶" }
|
1063
|
+
) do |menu|
|
1064
|
+
menu.choice @pastel.yellow("📋 Copy run command"), :copy_command
|
1065
|
+
menu.choice @pastel.cyan("⬅️ Back to main menu"), :back
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
case choice
|
1069
|
+
when :copy_command
|
1070
|
+
copy_to_clipboard(result[:run_command])
|
1071
|
+
when :back
|
1072
|
+
break
|
1073
|
+
end
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def initialize_config_and_clients
|
1078
|
+
@config_initialized = true
|
1079
|
+
|
1080
|
+
# Check if configuration is properly set up
|
1081
|
+
unless Tng::Services::UserAppConfig.configured?
|
1082
|
+
missing = Tng::Services::UserAppConfig.missing_config
|
1083
|
+
ConfigurationDisplay.new(@pastel).display_missing_config(missing)
|
1084
|
+
exit 1
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
@http_client = Tng::HttpClient.new(
|
1088
|
+
Tng::Services::UserAppConfig.base_url,
|
1089
|
+
Tng::Services::UserAppConfig.api_key
|
1090
|
+
)
|
1091
|
+
@testng = Services::Testng.new(@http_client)
|
1092
|
+
end
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
cli = CLI.new
|
1096
|
+
cli.start
|