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.
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 (full UI with search & selection):", "bundle exec tng"
51
- example " • Browse and search controllers/models", ""
52
- example " • Select specific files from filtered lists", ""
53
- example " • Choose test generation options", ""
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.red(" Failed to load Rails environment.")
128
- puts @pastel.yellow("Please ensure you're running this command from a Rails application directory.")
129
- puts @pastel.dim("Try running: bundle exec tng")
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
- choice = @prompt.select(
183
- center_text("Select an option:"),
184
- cycle: true,
185
- per_page: 5,
186
- symbols: { marker: "→" }
187
- ) do |menu|
188
- menu.choice "Generate tests", :tests
189
- menu.choice "User stats", :stats
190
- menu.choice "About", :about
191
- menu.choice "Exit", :exit, key: "q"
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 @pastel.cyan("⬅️ Back"), :back
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 :route
233
- generate_route_tests
227
+ when :other
228
+ generate_other_tests
234
229
  end
235
230
  end
236
231
 
237
232
  def generate_controller_tests
238
- header = @pastel.bright_white.bold("Scanning for controllers...")
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.green("✅"),
245
- error_mark: @pastel.red("❌")
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.green("Found #{controllers.length} controllers")
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.bright_white.bold("Scanning for models...")
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.green("✅"),
284
- error_mark: @pastel.red("❌")
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.green("Found #{models.length} models")
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.bright_white.bold("Scanning for services...")
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.green("✅"),
324
- error_mark: @pastel.red("❌")
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.green("Found #{services.length} services")
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 generate_test_for_controller(controller)
357
- header = @pastel.bright_white.bold("🎯 Generating tests for #{controller[:name]}")
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
- # Show controller analysis
361
- analysis_msg = @pastel.dim("Analyzing controller structure...")
362
- puts center_text(analysis_msg)
363
-
364
- # Center the progress bar
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
- step_msg = @pastel.dim("Parsing controller file...")
376
- puts center_text(step_msg)
377
- progress_bar.advance(1)
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
- step_msg = @pastel.dim("Extracting actions...")
381
- puts center_text(step_msg)
382
- progress_bar.advance(1)
383
- sleep(0.3)
364
+ {
365
+ name: namespaced_name,
366
+ path: file[:path],
367
+ type: file[:type]
368
+ }
369
+ end
384
370
 
385
- step_msg = @pastel.dim("Analyzing dependencies...")
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
- step_msg = @pastel.dim("Generating test code...")
391
- puts center_text(step_msg)
392
- progress_bar.advance(1)
393
- # test for the controller
394
- result = Tng::Services::TestGenerator.new(@http_client).run_for_controller(controller)
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
- step_msg = @pastel.dim("Writing test file...")
398
- puts center_text(step_msg)
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
- completion_msg = @pastel.green.bold("✅ Test generation completed!")
403
- puts center_text(completion_msg)
382
+ other_choice = flow_display.select_other_file(other_files)
383
+ return if other_choice == :back
404
384
 
405
- # Show post-generation menu if test was generated successfully
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.bright_white.bold("🔍 Analyzing methods for #{controller[:name]}")
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.bright_white.bold("🎯 Generating test for #{controller[:name]}##{method_info[:name]}")
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
- # Show method analysis
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.green("█"),
459
- incomplete: @pastel.dim("░"),
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.dim("Parsing method details...")
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.dim("Analyzing method dependencies...")
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.dim("Extracting method context...")
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.dim("Generating method test code...")
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.dim("Writing test file...")
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
- completion_msg = @pastel.green.bold("✅ Method test generation completed!")
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.dim("Press any key to continue...")))
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
- case test_option
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
- case test_option
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
- case test_option
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.bright_white.bold("🔍 Analyzing methods for #{model[:name]}")
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.bright_white.bold("🎯 Generating test for #{model[:name]}##{method_info[:name]}")
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
- # Show method analysis
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.green("█"),
665
- incomplete: @pastel.dim("░"),
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.dim("Parsing method details...")
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.dim("Analyzing method dependencies...")
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.dim("Generating method test code...")
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.dim("Writing test file...")
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
- completion_msg = @pastel.green.bold("✅ Method test generation completed!")
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.dim("Press any key to continue...")))
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.bright_white.bold("🎯 Generating test for #{service[:name]}##{method_info[:name]}")
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
- # Show method analysis
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.green("█"),
717
- incomplete: @pastel.dim("░"),
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.dim("Parsing method details...")
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.dim("Analyzing method dependencies...")
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.dim("Generating method test code...")
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.dim("Writing test file...")
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
- completion_msg = @pastel.green.bold("✅ Method test generation completed!")
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.dim("Press any key to continue...")))
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.bright_white.bold("🔍 Analyzing methods for #{service[:name]}")
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 generate_test_for_service(service)
778
- header = @pastel.bright_white.bold("🎯 Generating tests for #{service[:name]}")
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
- # Show service analysis
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.green("█"),
792
- incomplete: @pastel.dim("░"),
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.dim("Parsing service file...")
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.dim("Analyzing methods...")
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.dim("Analyzing dependencies...")
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
- sleep(0.3)
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.dim("Writing test file...")
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
- completion_msg = @pastel.green.bold("✅ Test generation completed!")
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.dim("Press any key to continue...")))
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
- # Only show status check in interactive mode
844
- puts @pastel.dim("🔍 Checking system status...") unless params[:type] && params[:file]
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.dim("Press any key to exit...")
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
- case type
914
- when "controller"
915
- run_direct_controller_generation
916
- when "model"
917
- run_direct_model_generation
918
- when "service"
919
- run_direct_service_generation
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
- if result && result[:file_path]
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.bright_white.bold("🎉 Test Generated Successfully!")
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.cyan("📁 File: #{result[:file_path]}"),
1060
- @pastel.yellow("🏃 Run: #{result[:run_command]}")
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
- # In direct mode, just show the success message and exit
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.bright_white.bold("🎉 Test Generated Successfully!")
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.cyan("📁 File: #{result[:file_path]}"),
1080
- @pastel.yellow("🏃 Run: #{result[:run_command]}")
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.bright_white("What would you like to do?"),
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.yellow("📋 Copy run command"), :copy_command
1091
- menu.choice @pastel.cyan("⬅️ Back to main menu"), :back
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