tng 0.2.9 ā 0.3.3
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/Rakefile +20 -43
- data/bin/tng +264 -558
- data/binaries/go-ui-darwin-amd64 +0 -0
- data/binaries/go-ui-darwin-arm64 +0 -0
- data/binaries/go-ui-linux-amd64 +0 -0
- data/binaries/go-ui-linux-arm64 +0 -0
- data/binaries/tng-darwin-arm64.bundle +0 -0
- data/binaries/tng-linux-arm64.so +0 -0
- data/binaries/tng-linux-x86_64.so +0 -0
- data/lib/generators/tng/install_generator.rb +7 -1
- data/lib/tng/api/http_client.rb +19 -0
- data/lib/tng/services/test_generator.rb +114 -81
- data/lib/tng/services/testng.rb +2 -8
- data/lib/tng/ui/go_ui_session.rb +406 -0
- data/lib/tng/utils.rb +3 -30
- data/lib/tng/version.rb +1 -1
- data/lib/tng.rb +34 -12
- data/tng.gemspec +4 -9
- metadata +59 -126
- data/binaries/tng.bundle +0 -0
- data/binaries/tng.so +0 -0
- data/lib/tng/ui/about_display.rb +0 -66
- data/lib/tng/ui/authentication_warning_display.rb +0 -122
- data/lib/tng/ui/configuration_display.rb +0 -52
- data/lib/tng/ui/controller_test_flow_display.rb +0 -79
- data/lib/tng/ui/display_banner.rb +0 -44
- data/lib/tng/ui/goodbye_display.rb +0 -41
- data/lib/tng/ui/model_test_flow_display.rb +0 -80
- data/lib/tng/ui/other_test_flow_display.rb +0 -78
- data/lib/tng/ui/service_test_flow_display.rb +0 -78
- data/lib/tng/ui/show_help.rb +0 -78
- data/lib/tng/ui/system_status_display.rb +0 -128
- data/lib/tng/ui/user_stats_display.rb +0 -160
data/bin/tng
CHANGED
|
@@ -11,30 +11,13 @@ require "tng/services/direct_generation"
|
|
|
11
11
|
require "tng/services/extract_methods"
|
|
12
12
|
require "tng/services/file_type_detector"
|
|
13
13
|
|
|
14
|
-
require "tty-prompt"
|
|
15
|
-
require "tty-spinner"
|
|
16
|
-
require "tty-box"
|
|
17
|
-
require "tty-table"
|
|
18
|
-
require "tty-progressbar"
|
|
19
14
|
require "tty-screen"
|
|
20
15
|
require "tty-option"
|
|
21
16
|
require "pastel"
|
|
22
|
-
require "tng/ui/show_help"
|
|
23
|
-
require "tng/ui/post_install_box"
|
|
24
17
|
require "tng/ui/theme"
|
|
25
|
-
require "tng/ui/system_status_display"
|
|
26
|
-
require "tng/ui/configuration_display"
|
|
27
|
-
require "tng/ui/authentication_warning_display"
|
|
28
|
-
require "tng/ui/display_banner"
|
|
29
|
-
require "tng/ui/user_stats_display"
|
|
30
|
-
require "tng/ui/about_display"
|
|
31
18
|
require "tng/services/testng"
|
|
32
|
-
require "tng/ui/
|
|
19
|
+
require "tng/ui/go_ui_session"
|
|
33
20
|
require "tng/api/http_client"
|
|
34
|
-
require "tng/ui/controller_test_flow_display"
|
|
35
|
-
require "tng/ui/model_test_flow_display"
|
|
36
|
-
require "tng/ui/service_test_flow_display"
|
|
37
|
-
require "tng/ui/other_test_flow_display"
|
|
38
21
|
require "tng/analyzers/controller"
|
|
39
22
|
require "tng/analyzers/model"
|
|
40
23
|
require "tng/analyzers/service"
|
|
@@ -81,8 +64,8 @@ class CLI
|
|
|
81
64
|
end
|
|
82
65
|
|
|
83
66
|
def initialize
|
|
84
|
-
@prompt = TTY::Prompt.new
|
|
85
67
|
@pastel = Pastel.new
|
|
68
|
+
@go_ui = Tng::UI::GoUISession.new
|
|
86
69
|
@terminal_width = begin
|
|
87
70
|
TTY::Screen.width
|
|
88
71
|
rescue StandardError
|
|
@@ -99,8 +82,7 @@ class CLI
|
|
|
99
82
|
parse(normalized_argv)
|
|
100
83
|
|
|
101
84
|
if params[:help]
|
|
102
|
-
|
|
103
|
-
puts help_display.render
|
|
85
|
+
@go_ui.show_help(Tng::VERSION)
|
|
104
86
|
return
|
|
105
87
|
end
|
|
106
88
|
|
|
@@ -121,8 +103,10 @@ class CLI
|
|
|
121
103
|
return unless check_configuration
|
|
122
104
|
|
|
123
105
|
check_system_status
|
|
106
|
+
return unless check_authentication
|
|
107
|
+
|
|
124
108
|
clear_screen
|
|
125
|
-
|
|
109
|
+
@go_ui.show_banner(Tng::VERSION)
|
|
126
110
|
main_menu
|
|
127
111
|
end
|
|
128
112
|
end
|
|
@@ -179,309 +163,193 @@ class CLI
|
|
|
179
163
|
end
|
|
180
164
|
|
|
181
165
|
def main_menu
|
|
182
|
-
|
|
183
|
-
clear_screen
|
|
184
|
-
|
|
185
|
-
puts DisplayBanner.new(@pastel, Tng::VERSION).render
|
|
166
|
+
@go_ui.start
|
|
186
167
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
center_text("Select an option:"),
|
|
190
|
-
cycle: true,
|
|
191
|
-
per_page: 5,
|
|
192
|
-
symbols: { marker: "ā" }
|
|
193
|
-
) do |menu|
|
|
194
|
-
menu.choice "Generate tests", :tests
|
|
195
|
-
menu.choice "User stats", :stats
|
|
196
|
-
menu.choice "About", :about
|
|
197
|
-
menu.choice "Exit", :exit, key: "q"
|
|
198
|
-
end
|
|
168
|
+
loop do
|
|
169
|
+
choice = @go_ui.show_menu
|
|
199
170
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
171
|
+
case choice
|
|
172
|
+
when "tests"
|
|
173
|
+
handle_test_generation
|
|
174
|
+
when "stats"
|
|
175
|
+
user_stats
|
|
176
|
+
when "about"
|
|
177
|
+
show_about
|
|
178
|
+
when "exit"
|
|
179
|
+
show_goodbye
|
|
180
|
+
break
|
|
181
|
+
else
|
|
182
|
+
break
|
|
203
183
|
end
|
|
204
|
-
|
|
205
|
-
break if choice == :exit
|
|
206
184
|
end
|
|
207
|
-
end
|
|
208
185
|
|
|
209
|
-
|
|
210
|
-
case choice
|
|
211
|
-
when :tests
|
|
212
|
-
handle_test_generation
|
|
213
|
-
when :stats
|
|
214
|
-
user_stats
|
|
215
|
-
when :about
|
|
216
|
-
show_about
|
|
217
|
-
when :exit
|
|
218
|
-
show_goodbye
|
|
219
|
-
end
|
|
186
|
+
@go_ui.stop
|
|
220
187
|
end
|
|
221
188
|
|
|
222
189
|
def handle_test_generation
|
|
223
|
-
choice = @
|
|
224
|
-
center_text("What would you like to test?"),
|
|
225
|
-
cycle: true,
|
|
226
|
-
per_page: 5,
|
|
227
|
-
symbols: { marker: "ā" }
|
|
228
|
-
) do |menu|
|
|
229
|
-
menu.choice "Controller", :controller
|
|
230
|
-
menu.choice "Model", :model
|
|
231
|
-
menu.choice "Service", :service
|
|
232
|
-
menu.choice "Other", :other
|
|
233
|
-
menu.choice @pastel.decorate("#{Tng::UI::Theme.icon(:back)} Back", Tng::UI::Theme.color(:info)), :back
|
|
234
|
-
end
|
|
190
|
+
choice = @go_ui.show_test_type_menu
|
|
235
191
|
|
|
236
192
|
case choice
|
|
237
|
-
when
|
|
193
|
+
when "controller"
|
|
238
194
|
generate_controller_tests
|
|
239
|
-
when
|
|
195
|
+
when "model"
|
|
240
196
|
generate_model_tests
|
|
241
|
-
when
|
|
197
|
+
when "service"
|
|
242
198
|
generate_service_tests
|
|
243
|
-
when
|
|
199
|
+
when "other"
|
|
244
200
|
generate_other_tests
|
|
245
201
|
end
|
|
246
202
|
end
|
|
247
203
|
|
|
248
204
|
def generate_controller_tests
|
|
249
|
-
|
|
250
|
-
puts center_text(header)
|
|
251
|
-
|
|
252
|
-
spinner = TTY::Spinner.new(
|
|
253
|
-
center_text("[:spinner] Analyzing controllers..."),
|
|
254
|
-
format: :dots,
|
|
255
|
-
success_mark: @pastel.decorate(Tng::UI::Theme.icon(:success), Tng::UI::Theme.color(:success)),
|
|
256
|
-
error_mark: @pastel.decorate(Tng::UI::Theme.icon(:error), Tng::UI::Theme.color(:error))
|
|
257
|
-
)
|
|
258
|
-
spinner.auto_spin
|
|
259
|
-
controllers = Tng::Analyzers::Controller.files_in_dir("app/controllers").map do |file|
|
|
260
|
-
relative_path = file[:path].gsub(%r{^.*app/controllers/}, "").gsub(".rb", "")
|
|
261
|
-
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
205
|
+
controllers = nil
|
|
262
206
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
end
|
|
207
|
+
@go_ui.show_spinner("Analyzing controllers...") do
|
|
208
|
+
controllers = Tng::Analyzers::Controller.files_in_dir("app/controllers").map do |file|
|
|
209
|
+
relative_path = file[:path].gsub(%r{^.*app/controllers/}, "").gsub(".rb", "")
|
|
210
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
268
211
|
|
|
269
|
-
|
|
212
|
+
{ name: namespaced_name, path: file[:path] }
|
|
213
|
+
end
|
|
214
|
+
{ success: true, message: "Found #{controllers.length} controllers" }
|
|
215
|
+
end
|
|
270
216
|
|
|
271
217
|
if controllers.empty?
|
|
272
|
-
|
|
273
|
-
flow_display.show_no_controllers_message
|
|
218
|
+
@go_ui.show_no_items("controllers")
|
|
274
219
|
return
|
|
275
220
|
end
|
|
276
221
|
|
|
277
|
-
|
|
278
|
-
|
|
222
|
+
items = controllers.map { |c| { name: c[:name], path: c[:path] } }
|
|
223
|
+
selected_name = @go_ui.show_list_view("Select Controller", items)
|
|
224
|
+
return if selected_name == "back"
|
|
279
225
|
|
|
280
|
-
controller_choice =
|
|
281
|
-
return
|
|
226
|
+
controller_choice = controllers.find { |c| c[:name] == selected_name }
|
|
227
|
+
return unless controller_choice
|
|
282
228
|
|
|
283
229
|
show_controller_test_options(controller_choice)
|
|
284
230
|
end
|
|
285
231
|
|
|
286
232
|
def generate_model_tests
|
|
287
|
-
|
|
288
|
-
puts center_text(header)
|
|
289
|
-
|
|
290
|
-
spinner = TTY::Spinner.new(
|
|
291
|
-
center_text("[:spinner] Analyzing models..."),
|
|
292
|
-
format: :dots,
|
|
293
|
-
success_mark: @pastel.decorate(Tng::UI::Theme.icon(:success), Tng::UI::Theme.color(:success)),
|
|
294
|
-
error_mark: @pastel.decorate(Tng::UI::Theme.icon(:error), Tng::UI::Theme.color(:error))
|
|
295
|
-
)
|
|
296
|
-
spinner.auto_spin
|
|
233
|
+
models = nil
|
|
297
234
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
235
|
+
@go_ui.show_spinner("Analyzing models...") do
|
|
236
|
+
models = Tng::Analyzers::Model.files_in_dir("app/models").map do |file|
|
|
237
|
+
relative_path = file[:path].gsub(%r{^.*app/models/}, "").gsub(".rb", "")
|
|
238
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
301
239
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
240
|
+
{ name: namespaced_name, path: file[:path] }
|
|
241
|
+
end
|
|
242
|
+
{ success: true, message: "Found #{models.length} models" }
|
|
306
243
|
end
|
|
307
244
|
|
|
308
|
-
flow_display = ModelTestFlowDisplay.new(@prompt, @pastel)
|
|
309
|
-
|
|
310
245
|
if models.empty?
|
|
311
|
-
|
|
312
|
-
flow_display.show_no_models_message
|
|
246
|
+
@go_ui.show_no_items("models")
|
|
313
247
|
return
|
|
314
248
|
end
|
|
315
249
|
|
|
316
|
-
|
|
317
|
-
|
|
250
|
+
items = models.map { |m| { name: m[:name], path: m[:path] } }
|
|
251
|
+
selected_name = @go_ui.show_list_view("Select Model", items)
|
|
252
|
+
return if selected_name == "back"
|
|
318
253
|
|
|
319
|
-
model_choice =
|
|
320
|
-
return
|
|
254
|
+
model_choice = models.find { |m| m[:name] == selected_name }
|
|
255
|
+
return unless model_choice
|
|
321
256
|
|
|
322
257
|
show_model_test_options(model_choice)
|
|
323
258
|
end
|
|
324
259
|
|
|
325
260
|
def generate_service_tests
|
|
326
|
-
|
|
327
|
-
puts center_text(header)
|
|
328
|
-
|
|
329
|
-
spinner = TTY::Spinner.new(
|
|
330
|
-
center_text("[:spinner] Analyzing services..."),
|
|
331
|
-
format: :dots,
|
|
332
|
-
success_mark: @pastel.decorate(Tng::UI::Theme.icon(:success), Tng::UI::Theme.color(:success)),
|
|
333
|
-
error_mark: @pastel.decorate(Tng::UI::Theme.icon(:error), Tng::UI::Theme.color(:error))
|
|
334
|
-
)
|
|
335
|
-
spinner.auto_spin
|
|
261
|
+
services = nil
|
|
336
262
|
|
|
337
|
-
services
|
|
338
|
-
|
|
339
|
-
|
|
263
|
+
@go_ui.show_spinner("Analyzing services...") do
|
|
264
|
+
services = Tng::Analyzers::Service.files_in_dir.map do |file|
|
|
265
|
+
relative_path = file[:path].gsub(%r{^.*app/services?/}, "").gsub(".rb", "")
|
|
266
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
340
267
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
268
|
+
{ name: namespaced_name, path: file[:path] }
|
|
269
|
+
end
|
|
270
|
+
{ success: true, message: "Found #{services.length} services" }
|
|
345
271
|
end
|
|
346
272
|
|
|
347
|
-
flow_display = ServiceTestFlowDisplay.new(@prompt, @pastel)
|
|
348
|
-
|
|
349
273
|
if services.empty?
|
|
350
|
-
|
|
351
|
-
flow_display.show_no_services_message
|
|
274
|
+
@go_ui.show_no_items("services")
|
|
352
275
|
return
|
|
353
276
|
end
|
|
354
277
|
|
|
355
|
-
|
|
356
|
-
|
|
278
|
+
items = services.map { |s| { name: s[:name], path: s[:path] } }
|
|
279
|
+
selected_name = @go_ui.show_list_view("Select Service", items)
|
|
280
|
+
return if selected_name == "back"
|
|
357
281
|
|
|
358
|
-
service_choice =
|
|
359
|
-
return
|
|
282
|
+
service_choice = services.find { |s| s[:name] == selected_name }
|
|
283
|
+
return unless service_choice
|
|
360
284
|
|
|
361
285
|
show_service_test_options(service_choice)
|
|
362
286
|
end
|
|
363
287
|
|
|
364
288
|
def generate_other_tests
|
|
365
|
-
|
|
366
|
-
puts center_text(header)
|
|
367
|
-
|
|
368
|
-
spinner = TTY::Spinner.new(
|
|
369
|
-
center_text("[:spinner] Analyzing other files..."),
|
|
370
|
-
format: :dots,
|
|
371
|
-
success_mark: @pastel.decorate(Tng::UI::Theme.icon(:success), Tng::UI::Theme.color(:success)),
|
|
372
|
-
error_mark: @pastel.decorate(Tng::UI::Theme.icon(:error), Tng::UI::Theme.color(:error))
|
|
373
|
-
)
|
|
374
|
-
spinner.auto_spin
|
|
289
|
+
other_files = nil
|
|
375
290
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
291
|
+
@go_ui.show_spinner("Analyzing other files...") do
|
|
292
|
+
other_files = Tng::Analyzers::Other.files_in_dir.map do |file|
|
|
293
|
+
relative_path = file[:relative_path].gsub(".rb", "")
|
|
294
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
379
295
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
type: file[:type]
|
|
384
|
-
}
|
|
296
|
+
{ name: namespaced_name, path: file[:path], type: file[:type] }
|
|
297
|
+
end
|
|
298
|
+
{ success: true, message: "Found #{other_files.length} other files" }
|
|
385
299
|
end
|
|
386
300
|
|
|
387
|
-
flow_display = OtherTestFlowDisplay.new(@prompt, @pastel)
|
|
388
|
-
|
|
389
301
|
if other_files.empty?
|
|
390
|
-
|
|
391
|
-
flow_display.show_no_other_files_message
|
|
302
|
+
@go_ui.show_no_items("other files")
|
|
392
303
|
return
|
|
393
304
|
end
|
|
394
305
|
|
|
395
|
-
|
|
396
|
-
|
|
306
|
+
items = other_files.map { |f| { name: f[:name], path: f[:path] } }
|
|
307
|
+
selected_name = @go_ui.show_list_view("Select File", items)
|
|
308
|
+
return if selected_name == "back"
|
|
397
309
|
|
|
398
|
-
other_choice =
|
|
399
|
-
return
|
|
310
|
+
other_choice = other_files.find { |f| f[:name] == selected_name }
|
|
311
|
+
return unless other_choice
|
|
400
312
|
|
|
401
313
|
show_other_test_options(other_choice)
|
|
402
314
|
end
|
|
403
315
|
|
|
404
316
|
def show_controller_method_selection(controller)
|
|
405
|
-
header = @pastel.decorate("#{Tng::UI::Theme.icon(:info)} Analyzing methods for #{controller[:name]}", Tng::UI::Theme.color(:primary))
|
|
406
|
-
puts center_text(header)
|
|
407
|
-
|
|
408
317
|
methods = extract_controller_methods(controller)
|
|
409
|
-
flow_display = ControllerTestFlowDisplay.new(@prompt, @pastel)
|
|
410
318
|
|
|
411
319
|
if methods.empty?
|
|
412
|
-
|
|
320
|
+
@go_ui.show_no_items("methods in #{controller[:name]}")
|
|
413
321
|
return
|
|
414
322
|
end
|
|
415
323
|
|
|
416
|
-
|
|
324
|
+
items = methods.map { |m| { name: m[:name], path: controller[:name] } }
|
|
325
|
+
selected_name = @go_ui.show_list_view("Select Method in #{controller[:name]}", items)
|
|
326
|
+
return if selected_name == "back"
|
|
417
327
|
|
|
418
|
-
|
|
328
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
329
|
+
return unless method_choice
|
|
419
330
|
|
|
420
331
|
generate_test_for_controller_method(controller, method_choice)
|
|
421
332
|
end
|
|
422
333
|
|
|
423
334
|
def generate_test_for_controller_method(controller, method_info)
|
|
424
|
-
|
|
425
|
-
"#{Tng::UI::Theme.icon(:marker)} Generating test for #{controller[:name]}##{method_info[:name]}", Tng::UI::Theme.color(:primary)
|
|
426
|
-
)
|
|
427
|
-
puts center_text(header)
|
|
428
|
-
|
|
429
|
-
analysis_msg = @pastel.decorate("Analyzing method structure...", Tng::UI::Theme.color(:muted))
|
|
430
|
-
puts center_text(analysis_msg)
|
|
431
|
-
|
|
432
|
-
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
|
433
|
-
progress_bar = TTY::ProgressBar.new(
|
|
434
|
-
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
|
435
|
-
total: 5,
|
|
436
|
-
bar_format: :box,
|
|
437
|
-
complete: @pastel.decorate("ā", Tng::UI::Theme.color(:success)),
|
|
438
|
-
incomplete: @pastel.decorate("ā", Tng::UI::Theme.color(:muted)),
|
|
439
|
-
width: 40
|
|
440
|
-
)
|
|
335
|
+
result = nil
|
|
441
336
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
step_msg = @pastel.decorate("Analyzing method dependencies...", Tng::UI::Theme.color(:muted))
|
|
448
|
-
puts center_text(step_msg)
|
|
449
|
-
progress_bar.advance(1)
|
|
450
|
-
sleep(0.3)
|
|
451
|
-
|
|
452
|
-
step_msg = @pastel.decorate("Extracting method context...", Tng::UI::Theme.color(:muted))
|
|
453
|
-
puts center_text(step_msg)
|
|
454
|
-
progress_bar.advance(1)
|
|
455
|
-
sleep(0.3)
|
|
456
|
-
|
|
457
|
-
step_msg = @pastel.decorate("Generating method test code...", Tng::UI::Theme.color(:muted))
|
|
458
|
-
puts center_text(step_msg)
|
|
459
|
-
progress_bar.advance(1)
|
|
460
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_controller_method(controller, method_info)
|
|
461
|
-
sleep(0.5)
|
|
462
|
-
|
|
463
|
-
step_msg = @pastel.decorate("Writing test file...", Tng::UI::Theme.color(:muted))
|
|
464
|
-
puts center_text(step_msg)
|
|
465
|
-
progress_bar.advance(1)
|
|
466
|
-
sleep(0.2)
|
|
467
|
-
|
|
468
|
-
# Show completion message with test count and timing
|
|
469
|
-
if result && result[:test_count]
|
|
470
|
-
count_msg = result[:test_count] == 1 ? "1 test" : "#{result[:test_count]} tests"
|
|
471
|
-
time_msg = result[:generation_time] ? " in #{Tng::Utils.format_generation_time(result[:generation_time])}" : ""
|
|
472
|
-
completion_msg = @pastel.decorate(
|
|
473
|
-
"#{Tng::UI::Theme.icon(:success)} Generated #{count_msg} for controller method#{time_msg}!", Tng::UI::Theme.color(:success)
|
|
474
|
-
)
|
|
475
|
-
else
|
|
476
|
-
completion_msg = @pastel.decorate("#{Tng::UI::Theme.icon(:success)} Method test generation completed!", Tng::UI::Theme.color(:success))
|
|
477
|
-
end
|
|
478
|
-
puts center_text(completion_msg)
|
|
337
|
+
@go_ui.show_progress("Generating test for #{controller[:name]}##{method_info[:name]}") do |progress|
|
|
338
|
+
progress.update("Parsing method details...", 20)
|
|
339
|
+
progress.update("Analyzing method dependencies...", 40)
|
|
340
|
+
progress.update("Extracting method context...", 60)
|
|
341
|
+
progress.update("Generating method test code...", 80)
|
|
479
342
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
343
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_controller_method(controller, method_info, progress: progress)
|
|
344
|
+
|
|
345
|
+
progress.update("Writing test file...", 100)
|
|
346
|
+
|
|
347
|
+
handle_generation_result(result)
|
|
484
348
|
end
|
|
349
|
+
|
|
350
|
+
return @go_ui.show_auth_error(result[:message] || "Generation failed") if result&.dig(:error)
|
|
351
|
+
|
|
352
|
+
show_post_generation_menu(result) if result && result[:file_path]
|
|
485
353
|
end
|
|
486
354
|
|
|
487
355
|
def show_controller_test_options(controller_choice)
|
|
@@ -503,157 +371,77 @@ class CLI
|
|
|
503
371
|
end
|
|
504
372
|
|
|
505
373
|
def show_model_method_selection(model)
|
|
506
|
-
header = @pastel.decorate("#{Tng::UI::Theme.icon(:info)} Analyzing methods for #{model[:name]}", Tng::UI::Theme.color(:primary))
|
|
507
|
-
puts center_text(header)
|
|
508
|
-
|
|
509
374
|
methods = extract_model_methods(model)
|
|
510
|
-
flow_display = ModelTestFlowDisplay.new(@prompt, @pastel)
|
|
511
375
|
|
|
512
376
|
if methods.empty?
|
|
513
|
-
|
|
377
|
+
@go_ui.show_no_items("methods in #{model[:name]}")
|
|
514
378
|
return
|
|
515
379
|
end
|
|
516
380
|
|
|
517
|
-
|
|
381
|
+
items = methods.map { |m| { name: m[:name], path: model[:name] } }
|
|
382
|
+
selected_name = @go_ui.show_list_view("Select Method in #{model[:name]}", items)
|
|
383
|
+
return if selected_name == "back"
|
|
518
384
|
|
|
519
|
-
|
|
385
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
386
|
+
return unless method_choice
|
|
520
387
|
|
|
521
388
|
generate_test_for_model_method(model, method_choice)
|
|
522
389
|
end
|
|
523
390
|
|
|
524
391
|
def generate_test_for_model_method(model, method_info)
|
|
525
|
-
|
|
526
|
-
"#{Tng::UI::Theme.icon(:marker)} Generating test for #{model[:name]}##{method_info[:name]}", Tng::UI::Theme.color(:primary)
|
|
527
|
-
)
|
|
528
|
-
puts center_text(header)
|
|
529
|
-
|
|
530
|
-
analysis_msg = @pastel.decorate("Analyzing method structure...", Tng::UI::Theme.color(:muted))
|
|
531
|
-
puts center_text(analysis_msg)
|
|
532
|
-
|
|
533
|
-
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
|
534
|
-
progress_bar = TTY::ProgressBar.new(
|
|
535
|
-
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
|
536
|
-
total: 4,
|
|
537
|
-
bar_format: :box,
|
|
538
|
-
complete: @pastel.decorate("ā", Tng::UI::Theme.color(:success)),
|
|
539
|
-
incomplete: @pastel.decorate("ā", Tng::UI::Theme.color(:muted)),
|
|
540
|
-
width: 40
|
|
541
|
-
)
|
|
392
|
+
result = nil
|
|
542
393
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
step_msg = @pastel.decorate("Analyzing method dependencies...", Tng::UI::Theme.color(:muted))
|
|
549
|
-
puts center_text(step_msg)
|
|
550
|
-
progress_bar.advance(1)
|
|
551
|
-
sleep(0.3)
|
|
552
|
-
|
|
553
|
-
step_msg = @pastel.decorate("Generating method test code...", Tng::UI::Theme.color(:muted))
|
|
554
|
-
puts center_text(step_msg)
|
|
555
|
-
progress_bar.advance(1)
|
|
556
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_model_method(model, method_info)
|
|
557
|
-
sleep(0.5)
|
|
558
|
-
|
|
559
|
-
step_msg = @pastel.decorate("Writing test file...", Tng::UI::Theme.color(:muted))
|
|
560
|
-
puts center_text(step_msg)
|
|
561
|
-
progress_bar.advance(1)
|
|
562
|
-
sleep(0.2)
|
|
563
|
-
|
|
564
|
-
# Show completion message with test count and timing
|
|
565
|
-
if result && result[:test_count]
|
|
566
|
-
count_msg = result[:test_count] == 1 ? "1 test" : "#{result[:test_count]} tests"
|
|
567
|
-
time_msg = result[:generation_time] ? " in #{Tng::Utils.format_generation_time(result[:generation_time])}" : ""
|
|
568
|
-
completion_msg = @pastel.decorate(
|
|
569
|
-
"#{Tng::UI::Theme.icon(:success)} Generated #{count_msg} for model method#{time_msg}!", Tng::UI::Theme.color(:success)
|
|
570
|
-
)
|
|
571
|
-
else
|
|
572
|
-
completion_msg = @pastel.decorate("#{Tng::UI::Theme.icon(:success)} Method test generation completed!", Tng::UI::Theme.color(:success))
|
|
573
|
-
end
|
|
574
|
-
puts center_text(completion_msg)
|
|
394
|
+
@go_ui.show_progress("Generating test for #{model[:name]}##{method_info[:name]}") do |progress|
|
|
395
|
+
progress.update("Parsing method details...", 25)
|
|
396
|
+
progress.update("Analyzing method dependencies...", 50)
|
|
397
|
+
progress.update("Generating method test code...", 75)
|
|
575
398
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
399
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_model_method(model, method_info, progress: progress)
|
|
400
|
+
|
|
401
|
+
progress.update("Writing test file...", 100)
|
|
402
|
+
|
|
403
|
+
handle_generation_result(result)
|
|
580
404
|
end
|
|
405
|
+
|
|
406
|
+
return @go_ui.show_auth_error(result[:message] || "Generation failed") if result&.dig(:error)
|
|
407
|
+
|
|
408
|
+
show_post_generation_menu(result) if result && result[:file_path]
|
|
581
409
|
end
|
|
582
410
|
|
|
583
411
|
def generate_test_for_service_method(service, method_info)
|
|
584
|
-
|
|
585
|
-
"#{Tng::UI::Theme.icon(:marker)} Generating test for #{service[:name]}##{method_info[:name]}", Tng::UI::Theme.color(:primary)
|
|
586
|
-
)
|
|
587
|
-
puts center_text(header)
|
|
588
|
-
|
|
589
|
-
analysis_msg = @pastel.decorate("Analyzing method structure...", Tng::UI::Theme.color(:muted))
|
|
590
|
-
puts center_text(analysis_msg)
|
|
591
|
-
|
|
592
|
-
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
|
593
|
-
progress_bar = TTY::ProgressBar.new(
|
|
594
|
-
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
|
595
|
-
total: 4,
|
|
596
|
-
bar_format: :box,
|
|
597
|
-
complete: @pastel.decorate("ā", Tng::UI::Theme.color(:success)),
|
|
598
|
-
incomplete: @pastel.decorate("ā", Tng::UI::Theme.color(:muted)),
|
|
599
|
-
width: 40
|
|
600
|
-
)
|
|
412
|
+
result = nil
|
|
601
413
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
step_msg = @pastel.decorate("Analyzing method dependencies...", Tng::UI::Theme.color(:muted))
|
|
608
|
-
puts center_text(step_msg)
|
|
609
|
-
progress_bar.advance(1)
|
|
610
|
-
sleep(0.3)
|
|
611
|
-
|
|
612
|
-
step_msg = @pastel.decorate("Generating method test code...", Tng::UI::Theme.color(:muted))
|
|
613
|
-
puts center_text(step_msg)
|
|
614
|
-
progress_bar.advance(1)
|
|
615
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_service_method(service, method_info)
|
|
616
|
-
sleep(0.5)
|
|
617
|
-
|
|
618
|
-
step_msg = @pastel.decorate("Writing test file...", Tng::UI::Theme.color(:muted))
|
|
619
|
-
puts center_text(step_msg)
|
|
620
|
-
progress_bar.advance(1)
|
|
621
|
-
sleep(0.2)
|
|
622
|
-
|
|
623
|
-
# Show completion message with test count and timing
|
|
624
|
-
if result && result[:test_count]
|
|
625
|
-
count_msg = result[:test_count] == 1 ? "1 test" : "#{result[:test_count]} tests"
|
|
626
|
-
time_msg = result[:generation_time] ? " in #{Tng::Utils.format_generation_time(result[:generation_time])}" : ""
|
|
627
|
-
completion_msg = @pastel.decorate(
|
|
628
|
-
"#{Tng::UI::Theme.icon(:success)} Generated #{count_msg} for service method#{time_msg}!", Tng::UI::Theme.color(:success)
|
|
629
|
-
)
|
|
630
|
-
else
|
|
631
|
-
completion_msg = @pastel.decorate("#{Tng::UI::Theme.icon(:success)} Method test generation completed!", Tng::UI::Theme.color(:success))
|
|
632
|
-
end
|
|
633
|
-
puts center_text(completion_msg)
|
|
414
|
+
@go_ui.show_progress("Generating test for #{service[:name]}##{method_info[:name]}") do |progress|
|
|
415
|
+
progress.update("Parsing method details...", 25)
|
|
416
|
+
progress.update("Analyzing method dependencies...", 50)
|
|
417
|
+
progress.update("Generating method test code...", 75)
|
|
634
418
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
419
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_service_method(service, method_info, progress: progress)
|
|
420
|
+
|
|
421
|
+
progress.update("Writing test file...", 100)
|
|
422
|
+
|
|
423
|
+
handle_generation_result(result)
|
|
639
424
|
end
|
|
425
|
+
|
|
426
|
+
return @go_ui.show_auth_error(result[:message] || "Generation failed") if result&.dig(:error)
|
|
427
|
+
|
|
428
|
+
show_post_generation_menu(result) if result && result[:file_path]
|
|
640
429
|
end
|
|
641
430
|
|
|
642
431
|
def show_service_method_selection(service)
|
|
643
|
-
header = @pastel.decorate("#{Tng::UI::Theme.icon(:info)} Analyzing methods for #{service[:name]}", Tng::UI::Theme.color(:primary))
|
|
644
|
-
puts center_text(header)
|
|
645
|
-
|
|
646
432
|
methods = extract_service_methods(service)
|
|
647
|
-
flow_display = ServiceTestFlowDisplay.new(@prompt, @pastel)
|
|
648
433
|
|
|
649
434
|
if methods.empty?
|
|
650
|
-
|
|
435
|
+
@go_ui.show_no_items("methods in #{service[:name]}")
|
|
651
436
|
return
|
|
652
437
|
end
|
|
653
438
|
|
|
654
|
-
|
|
439
|
+
items = methods.map { |m| { name: m[:name], path: service[:name] } }
|
|
440
|
+
selected_name = @go_ui.show_list_view("Select Method in #{service[:name]}", items)
|
|
441
|
+
return if selected_name == "back"
|
|
655
442
|
|
|
656
|
-
|
|
443
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
444
|
+
return unless method_choice
|
|
657
445
|
|
|
658
446
|
generate_test_for_service_method(service, method_choice)
|
|
659
447
|
end
|
|
@@ -665,87 +453,58 @@ class CLI
|
|
|
665
453
|
end
|
|
666
454
|
|
|
667
455
|
def show_other_method_selection(other_file)
|
|
668
|
-
header = @pastel.decorate("#{Tng::UI::Theme.icon(:info)} Analyzing methods for #{other_file[:name]}", Tng::UI::Theme.color(:primary))
|
|
669
|
-
puts center_text(header)
|
|
670
|
-
|
|
671
456
|
methods = extract_other_methods(other_file)
|
|
672
|
-
flow_display = OtherTestFlowDisplay.new(@prompt, @pastel)
|
|
673
457
|
|
|
674
458
|
if methods.empty?
|
|
675
|
-
|
|
459
|
+
@go_ui.show_no_items("methods in #{other_file[:name]}")
|
|
676
460
|
return
|
|
677
461
|
end
|
|
678
462
|
|
|
679
|
-
|
|
463
|
+
items = methods.map { |m| { name: m[:name], path: other_file[:name] } }
|
|
464
|
+
selected_name = @go_ui.show_list_view("Select Method in #{other_file[:name]}", items)
|
|
465
|
+
return if selected_name == "back"
|
|
680
466
|
|
|
681
|
-
|
|
467
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
468
|
+
return unless method_choice
|
|
682
469
|
|
|
683
470
|
generate_test_for_other_method(other_file, method_choice)
|
|
684
471
|
end
|
|
685
472
|
|
|
686
473
|
def generate_test_for_other_method(other_file, method_info)
|
|
687
|
-
|
|
688
|
-
"#{Tng::UI::Theme.icon(:marker)} Generating test for #{other_file[:name]}##{method_info[:name]}", Tng::UI::Theme.color(:primary)
|
|
689
|
-
)
|
|
690
|
-
puts center_text(header)
|
|
691
|
-
|
|
692
|
-
analysis_msg = @pastel.decorate("Analyzing method structure...", Tng::UI::Theme.color(:muted))
|
|
693
|
-
puts center_text(analysis_msg)
|
|
694
|
-
|
|
695
|
-
progress_padding = [(@terminal_width - 56) / 2, 0].max # Account for progress bar width
|
|
696
|
-
progress_bar = TTY::ProgressBar.new(
|
|
697
|
-
"#{" " * progress_padding}[:bar] :percent :current/:total",
|
|
698
|
-
total: 4,
|
|
699
|
-
bar_format: :box,
|
|
700
|
-
complete: @pastel.decorate("ā", Tng::UI::Theme.color(:success)),
|
|
701
|
-
incomplete: @pastel.decorate("ā", Tng::UI::Theme.color(:muted)),
|
|
702
|
-
width: 40
|
|
703
|
-
)
|
|
474
|
+
result = nil
|
|
704
475
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
step_msg = @pastel.decorate("Generating method test code...", Tng::UI::Theme.color(:muted))
|
|
716
|
-
puts center_text(step_msg)
|
|
717
|
-
progress_bar.advance(1)
|
|
718
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_other_method(other_file, method_info)
|
|
719
|
-
sleep(0.5)
|
|
720
|
-
|
|
721
|
-
step_msg = @pastel.decorate("Writing test file...", Tng::UI::Theme.color(:muted))
|
|
722
|
-
puts center_text(step_msg)
|
|
723
|
-
progress_bar.advance(1)
|
|
724
|
-
sleep(0.2)
|
|
725
|
-
|
|
726
|
-
# Show completion message with test count and timing
|
|
727
|
-
if result && result[:test_count]
|
|
728
|
-
count_msg = result[:test_count] == 1 ? "1 test" : "#{result[:test_count]} tests"
|
|
729
|
-
time_msg = result[:generation_time] ? " in #{Tng::Utils.format_generation_time(result[:generation_time])}" : ""
|
|
730
|
-
completion_msg = @pastel.decorate(
|
|
731
|
-
"#{Tng::UI::Theme.icon(:success)} Generated #{count_msg} for other method#{time_msg}!", Tng::UI::Theme.color(:success)
|
|
732
|
-
)
|
|
733
|
-
else
|
|
734
|
-
completion_msg = @pastel.decorate("#{Tng::UI::Theme.icon(:success)} Method test generation completed!", Tng::UI::Theme.color(:success))
|
|
476
|
+
@go_ui.show_progress("Generating test for #{other_file[:name]}##{method_info[:name]}") do |progress|
|
|
477
|
+
progress.update("Parsing method details...", 25)
|
|
478
|
+
progress.update("Analyzing method dependencies...", 50)
|
|
479
|
+
progress.update("Generating method test code...", 75)
|
|
480
|
+
|
|
481
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_other_method(other_file, method_info, progress: progress)
|
|
482
|
+
|
|
483
|
+
progress.update("Writing test file...", 100)
|
|
484
|
+
|
|
485
|
+
handle_generation_result(result)
|
|
735
486
|
end
|
|
736
|
-
puts center_text(completion_msg)
|
|
737
487
|
|
|
738
|
-
|
|
739
|
-
|
|
488
|
+
return @go_ui.show_auth_error(result[:message] || "Generation failed") if result&.dig(:error)
|
|
489
|
+
|
|
490
|
+
show_post_generation_menu(result) if result && result[:file_path]
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def handle_generation_result(result)
|
|
494
|
+
if result&.dig(:error)
|
|
495
|
+
{ message: result[:message] || "Generation failed", error: result[:error], result: result }
|
|
496
|
+
elsif result && result[:test_count]
|
|
497
|
+
count_msg = result[:test_count] == 1 ? "1 test" : "#{result[:test_count]} tests"
|
|
498
|
+
{ message: "Generated #{count_msg}!", result: result }
|
|
740
499
|
else
|
|
741
|
-
|
|
500
|
+
{ message: "Test generation completed!", result: result }
|
|
742
501
|
end
|
|
743
502
|
end
|
|
744
503
|
|
|
745
504
|
# STATS
|
|
746
505
|
def user_stats
|
|
747
506
|
stats_data = @testng.get_user_stats
|
|
748
|
-
|
|
507
|
+
@go_ui.show_stats(stats_data)
|
|
749
508
|
end
|
|
750
509
|
|
|
751
510
|
def check_system_status
|
|
@@ -755,13 +514,17 @@ class CLI
|
|
|
755
514
|
end
|
|
756
515
|
|
|
757
516
|
status = @testng.check_system_status
|
|
758
|
-
status_ok = SystemStatusDisplay.new(@pastel, params).display(status)
|
|
759
517
|
|
|
760
|
-
|
|
518
|
+
if status[:status] == :ok
|
|
519
|
+
unless params[:file]
|
|
520
|
+
puts @pastel.decorate("#{Tng::UI::Theme.icon(:success)} System operational",
|
|
521
|
+
Tng::UI::Theme.color(:success))
|
|
522
|
+
end
|
|
523
|
+
return
|
|
524
|
+
end
|
|
761
525
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
$stdin.getch
|
|
526
|
+
# Show error via Go UI
|
|
527
|
+
@go_ui.show_system_status(status)
|
|
765
528
|
exit(1)
|
|
766
529
|
end
|
|
767
530
|
|
|
@@ -770,28 +533,74 @@ class CLI
|
|
|
770
533
|
|
|
771
534
|
missing = Tng::Services::UserAppConfig.missing_config
|
|
772
535
|
|
|
773
|
-
|
|
536
|
+
@go_ui.show_config_missing(missing)
|
|
774
537
|
false
|
|
775
538
|
end
|
|
776
539
|
|
|
777
540
|
def check_authentication_configuration
|
|
778
|
-
|
|
779
|
-
auth_missing = auth_warning.display_if_missing
|
|
541
|
+
return true unless authentication_config_missing?
|
|
780
542
|
|
|
781
|
-
|
|
543
|
+
choice = @go_ui.show_auth_warning(build_auth_missing_summary)
|
|
544
|
+
|
|
545
|
+
# "continue" means user wants to proceed, "cancel" means abort
|
|
546
|
+
choice == "continue"
|
|
547
|
+
end
|
|
782
548
|
|
|
783
|
-
|
|
549
|
+
def authentication_config_missing?
|
|
550
|
+
return true if Tng.authentication_enabled && authentication_methods_empty?
|
|
551
|
+
return true if !authentication_methods_empty? && !authentication_methods_valid?
|
|
552
|
+
|
|
553
|
+
false
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def authentication_methods_empty?
|
|
557
|
+
methods = Tng.authentication_methods
|
|
558
|
+
methods.nil? || methods.empty?
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def authentication_methods_valid?
|
|
562
|
+
methods = Tng.authentication_methods
|
|
563
|
+
return false if methods.nil? || methods.empty?
|
|
564
|
+
|
|
565
|
+
methods.all? do |method|
|
|
566
|
+
method.is_a?(Hash) &&
|
|
567
|
+
method.key?(:method) && !method[:method].to_s.strip.empty? &&
|
|
568
|
+
method.key?(:file_location) && !method[:file_location].to_s.strip.empty? &&
|
|
569
|
+
method.key?(:auth_type) && !method[:auth_type].to_s.strip.empty?
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
def build_auth_missing_summary
|
|
574
|
+
issues = []
|
|
575
|
+
issues << "empty auth methods" if Tng.authentication_enabled && authentication_methods_empty?
|
|
576
|
+
issues << "invalid auth config" if !authentication_methods_empty? && !authentication_methods_valid?
|
|
577
|
+
issues << "auth disabled" unless Tng.authentication_enabled
|
|
578
|
+
issues.empty? ? "unknown issue" : issues.join(", ")
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def check_authentication
|
|
582
|
+
puts @pastel.decorate("#{Tng::UI::Theme.icon(:info)} Verifying authentication...",
|
|
583
|
+
Tng::UI::Theme.color(:muted))
|
|
584
|
+
|
|
585
|
+
result = @http_client.verify_auth
|
|
586
|
+
|
|
587
|
+
if result[:valid]
|
|
588
|
+
true
|
|
589
|
+
else
|
|
590
|
+
@go_ui.show_auth_error(result[:error])
|
|
591
|
+
false
|
|
592
|
+
end
|
|
784
593
|
end
|
|
785
594
|
|
|
786
595
|
# ABOUT
|
|
787
596
|
def show_about
|
|
788
|
-
|
|
597
|
+
@go_ui.show_about
|
|
789
598
|
end
|
|
790
599
|
|
|
791
600
|
# GOODBYE
|
|
792
601
|
def show_goodbye
|
|
793
602
|
clear_screen
|
|
794
|
-
|
|
603
|
+
@go_ui.show_goodbye
|
|
795
604
|
end
|
|
796
605
|
|
|
797
606
|
def run_direct_generation
|
|
@@ -800,8 +609,8 @@ class CLI
|
|
|
800
609
|
return unless check_authentication_configuration
|
|
801
610
|
|
|
802
611
|
status = @testng.check_system_status
|
|
803
|
-
unless
|
|
804
|
-
|
|
612
|
+
unless status[:status] == :ok
|
|
613
|
+
@go_ui.show_system_status(status)
|
|
805
614
|
return
|
|
806
615
|
end
|
|
807
616
|
|
|
@@ -819,160 +628,57 @@ class CLI
|
|
|
819
628
|
def show_post_generation_menu(result)
|
|
820
629
|
is_direct_mode = params[:file]
|
|
821
630
|
|
|
631
|
+
# Direct mode - just print info and exit
|
|
822
632
|
if is_direct_mode
|
|
823
|
-
puts
|
|
824
|
-
|
|
825
|
-
puts
|
|
826
|
-
puts
|
|
827
|
-
|
|
828
|
-
info_msg = [
|
|
829
|
-
@pastel.decorate("#{Tng::UI::Theme.icon(:config)} File: #{result[:file_path]}", Tng::UI::Theme.color(:info)),
|
|
830
|
-
@pastel.decorate("#{Tng::UI::Theme.icon(:marker)} Run: #{result[:run_command]}", Tng::UI::Theme.color(:secondary))
|
|
831
|
-
].join("\n")
|
|
832
|
-
puts center_text(info_msg)
|
|
833
|
-
puts
|
|
834
|
-
|
|
835
|
-
puts center_text(@pastel.decorate("#{Tng::UI::Theme.icon(:success)} Test generation completed successfully!", Tng::UI::Theme.color(:success)))
|
|
633
|
+
puts "\nš Test Generated Successfully!"
|
|
634
|
+
puts "š File: #{result[:file_path]}"
|
|
635
|
+
puts "ā¶ļø Run: #{result[:run_command]}"
|
|
636
|
+
puts "ā
Test generation completed successfully!"
|
|
836
637
|
return
|
|
837
638
|
end
|
|
838
639
|
|
|
839
|
-
#
|
|
640
|
+
# Interactive mode - show Go UI menu
|
|
840
641
|
loop do
|
|
841
|
-
|
|
842
|
-
header = @pastel.decorate("#{Tng::UI::Theme.icon(:rocket)} Test Generated Successfully!", Tng::UI::Theme.color(:primary))
|
|
843
|
-
puts center_text(header)
|
|
844
|
-
puts
|
|
845
|
-
|
|
846
|
-
info_msg = [
|
|
847
|
-
@pastel.decorate("#{Tng::UI::Theme.icon(:config)} File: #{result[:file_path]}", Tng::UI::Theme.color(:info)),
|
|
848
|
-
@pastel.decorate("#{Tng::UI::Theme.icon(:marker)} Run: #{result[:run_command]}", Tng::UI::Theme.color(:secondary))
|
|
849
|
-
].join("\n")
|
|
850
|
-
puts center_text(info_msg)
|
|
851
|
-
puts
|
|
852
|
-
|
|
853
|
-
choice = @prompt.select(
|
|
854
|
-
@pastel.decorate("What would you like to do?", Tng::UI::Theme.color(:primary)),
|
|
855
|
-
cycle: true,
|
|
856
|
-
symbols: { marker: Tng::UI::Theme.icon(:marker) }
|
|
857
|
-
) do |menu|
|
|
858
|
-
menu.choice @pastel.decorate("#{Tng::UI::Theme.icon(:run)} Run tests", Tng::UI::Theme.color(:success)),
|
|
859
|
-
:run_tests
|
|
860
|
-
menu.choice @pastel.decorate("#{Tng::UI::Theme.icon(:config)} Copy run command", Tng::UI::Theme.color(:warning)),
|
|
861
|
-
:copy_command
|
|
862
|
-
menu.choice @pastel.decorate("#{Tng::UI::Theme.icon(:back)} Back to main menu", Tng::UI::Theme.color(:info)),
|
|
863
|
-
:back
|
|
864
|
-
end
|
|
642
|
+
choice = @go_ui.show_post_generation_menu(result[:file_path], result[:run_command])
|
|
865
643
|
|
|
866
644
|
case choice
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
when :copy_command
|
|
645
|
+
|
|
646
|
+
when "copy_command"
|
|
870
647
|
copy_to_clipboard(result[:run_command])
|
|
871
|
-
when
|
|
648
|
+
when "back"
|
|
872
649
|
break
|
|
873
|
-
end
|
|
874
|
-
end
|
|
875
|
-
end
|
|
876
|
-
|
|
877
|
-
def run_and_show_test_results(command)
|
|
878
|
-
puts
|
|
879
|
-
header = @pastel.decorate("#{Tng::UI::Theme.icon(:run)} Running tests...", Tng::UI::Theme.color(:primary))
|
|
880
|
-
puts center_text(header)
|
|
881
|
-
|
|
882
|
-
begin
|
|
883
|
-
# Check if test file exists before running
|
|
884
|
-
test_file = extract_test_file_from_command(command)
|
|
885
|
-
unless test_file && File.exist?(test_file)
|
|
886
|
-
error_msg = @pastel.decorate("#{Tng::UI::Theme.icon(:error)} Test file not found!", Tng::UI::Theme.color(:error))
|
|
887
|
-
puts center_text(error_msg)
|
|
888
|
-
|
|
889
|
-
if test_file
|
|
890
|
-
file_msg = @pastel.decorate("File: #{test_file}", Tng::UI::Theme.color(:muted))
|
|
891
|
-
puts center_text(file_msg)
|
|
892
|
-
end
|
|
893
|
-
|
|
894
|
-
suggestion_msg = @pastel.decorate("The test file may have been moved or deleted.", Tng::UI::Theme.color(:warning))
|
|
895
|
-
puts center_text(suggestion_msg)
|
|
896
|
-
|
|
897
|
-
@prompt.keypress(center_text(@pastel.decorate("Press any key to continue...", Tng::UI::Theme.color(:muted))))
|
|
898
|
-
return
|
|
899
|
-
end
|
|
900
|
-
|
|
901
|
-
# Modify command to get cleaner output
|
|
902
|
-
if command.include?("rspec")
|
|
903
|
-
# Use JSON format for RSpec - much cleaner parsing
|
|
904
|
-
json_command = command.sub(/rspec/, "rspec --format json --out /dev/null")
|
|
905
|
-
output = `#{json_command} 2>&1`
|
|
906
|
-
exit_code = $?.exitstatus
|
|
907
|
-
|
|
908
|
-
# Check for common errors before parsing
|
|
909
|
-
return if handle_test_execution_errors(output, exit_code)
|
|
910
|
-
|
|
911
|
-
Tng::Utils.parse_rspec_json_results(output, exit_code, @pastel, @terminal_width)
|
|
912
650
|
else
|
|
913
|
-
|
|
914
|
-
output = `#{command} 2>&1`
|
|
915
|
-
exit_code = $?.exitstatus
|
|
916
|
-
|
|
917
|
-
# Check for common errors before parsing
|
|
918
|
-
return if handle_test_execution_errors(output, exit_code)
|
|
919
|
-
|
|
920
|
-
Tng::Utils.parse_minitest_results(output, exit_code, @pastel, @terminal_width)
|
|
651
|
+
break
|
|
921
652
|
end
|
|
922
|
-
rescue StandardError => e
|
|
923
|
-
error_msg = @pastel.decorate("#{Tng::UI::Theme.icon(:error)} Error running tests!", Tng::UI::Theme.color(:error))
|
|
924
|
-
puts center_text(error_msg)
|
|
925
|
-
|
|
926
|
-
details_msg = @pastel.decorate("#{e.message}", Tng::UI::Theme.color(:muted))
|
|
927
|
-
puts center_text(details_msg)
|
|
928
653
|
end
|
|
929
|
-
|
|
930
|
-
@prompt.keypress(center_text(@pastel.decorate("Press any key to continue...", Tng::UI::Theme.color(:muted))))
|
|
931
654
|
end
|
|
932
655
|
|
|
933
|
-
def handle_test_execution_errors(output, exit_code)
|
|
934
|
-
# Return true if we handled an error (caller should return early)
|
|
935
|
-
# Return false if no error was handled (caller should continue)
|
|
936
656
|
|
|
937
|
-
# If tests failed to run (non-zero exit code or common error patterns)
|
|
938
|
-
if !exit_code.zero? || output.include?("LoadError") || output.include?("cannot load such file") ||
|
|
939
|
-
(output.include?("bundler") && output.include?("Could not find")) ||
|
|
940
|
-
(output.include?("bin/rails") && (output.include?("require") || output.include?("load")))
|
|
941
657
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
@
|
|
946
|
-
|
|
658
|
+
def copy_to_clipboard(command)
|
|
659
|
+
if RUBY_PLATFORM.include?("darwin")
|
|
660
|
+
IO.popen("pbcopy", "w") { |io| io.print command }
|
|
661
|
+
@go_ui.show_clipboard_success(command)
|
|
662
|
+
elsif RUBY_PLATFORM.include?("linux")
|
|
663
|
+
begin
|
|
664
|
+
IO.popen("xclip -selection clipboard", "w") { |io| io.print command }
|
|
665
|
+
@go_ui.show_clipboard_success(command)
|
|
666
|
+
rescue Errno::ENOENT
|
|
667
|
+
puts "\nš Copy this command:\n#{command}\n"
|
|
668
|
+
end
|
|
669
|
+
else
|
|
670
|
+
puts "\nš Copy this command:\n#{command}\n"
|
|
947
671
|
end
|
|
948
|
-
|
|
949
|
-
false
|
|
950
672
|
end
|
|
951
673
|
|
|
952
|
-
def extract_test_file_from_command(command)
|
|
953
|
-
# Extract test file path from commands like:
|
|
954
|
-
# "bundle exec rails test test/controllers/foo_test.rb"
|
|
955
|
-
# "bundle exec rspec spec/controllers/foo_spec.rb"
|
|
956
674
|
|
|
957
|
-
if command.include?("rails test")
|
|
958
|
-
# For Rails test commands
|
|
959
|
-
match = command.match(/rails test\s+(.+?)(?:\s|$)/)
|
|
960
|
-
match[1] if match
|
|
961
|
-
elsif command.include?("rspec")
|
|
962
|
-
# For RSpec commands
|
|
963
|
-
match = command.match(/rspec\s+(.+?)(?:\s|$)/)
|
|
964
|
-
match[1] if match
|
|
965
|
-
else
|
|
966
|
-
nil
|
|
967
|
-
end
|
|
968
|
-
end
|
|
969
675
|
|
|
970
676
|
def initialize_config_and_clients
|
|
971
677
|
@config_initialized = true
|
|
972
678
|
|
|
973
679
|
unless Tng::Services::UserAppConfig.configured?
|
|
974
680
|
missing = Tng::Services::UserAppConfig.missing_config
|
|
975
|
-
|
|
681
|
+
@go_ui.show_config_missing(missing)
|
|
976
682
|
exit 1
|
|
977
683
|
end
|
|
978
684
|
|