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