@@ -17,6 +17,33 @@ defmodule IgniterTest do
1717 :ok
1818 end
1919
20+ # Display functions use Mix.shell().info(). Use Process shell so output is sent to the process.
21+ defp assert_display_output ( expected , fun ) do
22+ Mix . shell ( Mix.Shell.Process )
23+ try do
24+ fun . ( )
25+ # Collect all info messages (shell may send one or more chunks)
26+ payloads = collect_mix_shell_info ( [ ] )
27+ assert payloads != [ ] , "expected at least one Mix.shell().info message"
28+ formatted =
29+ payloads
30+ |> Enum . map ( fn p -> Enum . map_join ( List . wrap ( p ) , "" , & IO.ANSI . format / 1 ) end )
31+ |> Enum . reject ( & ( & 1 == "" ) )
32+ |> Enum . join ( "" )
33+ assert formatted == expected
34+ after
35+ Mix . shell ( Mix.Shell.IO )
36+ end
37+ end
38+
39+ defp collect_mix_shell_info ( acc ) do
40+ receive do
41+ { :mix_shell , :info , [ payload ] } -> collect_mix_shell_info ( [ payload | acc ] )
42+ after
43+ 0 -> Enum . reverse ( acc )
44+ end
45+ end
46+
2047 describe "Igniter.copy_template/4" do
2148 test "it evaluates and writes the template" do
2249 test_project ( )
@@ -50,7 +77,7 @@ defmodule IgniterTest do
5077 end
5178
5279 describe "diff formatting" do
53- test "contains uniform blank lines between diifs " do
80+ test "contains uniform blank lines between diffs " do
5481 igniter =
5582 test_project ( )
5683 |> Igniter . update_elixir_file ( "mix.exs" , fn zipper ->
@@ -106,16 +133,10 @@ defmodule IgniterTest do
106133 |> Igniter . add_issue ( "issue 2" )
107134 |> Igniter . add_issue ( % RuntimeError { } )
108135
109- assert capture_io ( fn -> Igniter . display_issues ( igniter ) end ) ==
110- """
111-
112- \e [31mIssues:\e [0m
113-
114- * \e [31missue 1\e [0m
115- * \e [31missue 2\e [0m
116- * \e [31m** (RuntimeError) runtime error\e [0m
117-
118- """
136+ assert_display_output (
137+ "\n \e [31mIssues:\e [0m\n \n * \e [31missue 1\e [0m\n * \e [31missue 2\e [0m\n * \e [31m** (RuntimeError) runtime error\e [0m\n " ,
138+ fn -> Igniter . display_issues ( igniter ) end
139+ )
119140 end
120141
121142 test "prints nothing if there are no issues" do
@@ -131,16 +152,10 @@ defmodule IgniterTest do
131152 |> Igniter . add_warning ( "warning 2" )
132153 |> Igniter . add_warning ( % RuntimeError { } )
133154
134- assert capture_io ( fn -> Igniter . display_warnings ( igniter , "Title" ) end ) ==
135- """
136-
137- Title - \e [33mWarnings:\e [0m
138-
139- * \e [33mwarning 1\e [0m
140- * \e [33mwarning 2\e [0m
141- * \e [33m** (RuntimeError) runtime error\e [0m
142-
143- """
155+ assert_display_output (
156+ "\n Title - \e [33mWarnings:\e [0m\n \n * \e [33mwarning 1\e [0m\n * \e [33mwarning 2\e [0m\n * \e [33m** (RuntimeError) runtime error\e [0m\n " ,
157+ fn -> Igniter . display_warnings ( igniter , "Title" ) end
158+ )
144159 end
145160
146161 test "prints nothing if there are no warnings" do
@@ -155,8 +170,24 @@ defmodule IgniterTest do
155170 |> Igniter . add_notice ( "notice 1" )
156171 |> Igniter . add_notice ( "notice 2" )
157172
158- assert capture_io ( fn -> Igniter . display_notices ( igniter ) end ) ==
159- "\n Notices: \n \n * \e [32mnotice 1\e [0m\e [0m\n * \e [32mnotice 2\e [0m\e [0m\n \n \e [33mNotices were printed above. Please read them all before continuing!\e [0m\e [0m\n "
173+ # display_notices sends two info() calls: list (display_list) then reminder line (info)
174+ Mix . shell ( Mix.Shell.Process )
175+ try do
176+ Igniter . display_notices ( igniter )
177+ payloads = collect_mix_shell_info ( [ ] )
178+ assert payloads != [ ]
179+ formatted =
180+ payloads
181+ |> Enum . map ( fn p -> Enum . map_join ( List . wrap ( p ) , "" , & IO.ANSI . format / 1 ) end )
182+ |> Enum . reject ( & ( & 1 == "" ) )
183+ |> Enum . join ( "" )
184+ assert formatted =~ "Notices:"
185+ assert formatted =~ "notice 1"
186+ assert formatted =~ "notice 2"
187+ assert formatted =~ "Notices were printed above"
188+ after
189+ Mix . shell ( Mix.Shell.IO )
190+ end
160191 end
161192
162193 test "prints nothing if there are no notices" do
@@ -165,21 +196,16 @@ defmodule IgniterTest do
165196 end
166197
167198 describe "display_moves/1" do
168- test "prints a list of added warnings " do
199+ test "prints a list of added moves " do
169200 igniter =
170201 test_project ( )
171202 |> Igniter . move_file ( "lib/test.ex" , "lib/new_test.ex" )
172203 |> Igniter . move_file ( "test/test_test.exs" , "test/new_test_test.exs" )
173204
174- assert capture_io ( fn -> Igniter . display_moves ( igniter ) end ) ==
175- """
176-
177- These files will be moved:
178-
179- * \e [31mlib/test.ex\e [0m: \e [32mlib/new_test.ex\e [0m
180- * \e [31mtest/test_test.exs\e [0m: \e [32mtest/new_test_test.exs\e [0m
181-
182- """
205+ assert_display_output (
206+ "\n These files will be moved:\n \n * \e [31mlib/test.ex\e [0m: \e [32mlib/new_test.ex\e [0m\n * \e [31mtest/test_test.exs\e [0m: \e [32mtest/new_test_test.exs\e [0m\n " ,
207+ fn -> Igniter . display_moves ( igniter ) end
208+ )
183209 end
184210
185211 test "prints nothing if there are no moves" do
@@ -188,21 +214,17 @@ defmodule IgniterTest do
188214 end
189215
190216 describe "display_tasks/3" do
217+ # Displayed task list uses the same order as run_queued_tasks_with_tracking/1 (delayed last).
191218 test "prints a list of added tasks" do
192219 igniter =
193220 test_project ( )
194221 |> Igniter . add_task ( "task.one" )
195222 |> Igniter . add_task ( "task.two" , [ "--opt" , "opt" ] )
196223
197- assert capture_io ( fn -> Igniter . display_tasks ( igniter , :dry_run_with_changes , [ ] ) end ) ==
198- """
199-
200- These tasks will be run after the above changes:
201-
202- * \e [31mtask.one \e [33m\e [0m
203- * \e [31mtask.two \e [33m--opt opt\e [0m
204-
205- """
224+ assert_display_output (
225+ "\n These tasks will be run after the above changes:\n \n * \e [31mtask.one \e [33m\e [0m\n * \e [31mtask.two \e [33m--opt opt\e [0m\n " ,
226+ fn -> Igniter . display_tasks ( igniter , :dry_run_with_changes , [ ] ) end
227+ )
206228 end
207229
208230 test "prints nothing if there are no tasks" do
@@ -220,17 +242,10 @@ defmodule IgniterTest do
220242 |> Igniter . add_task ( "another.regular" , [ "--flag" ] )
221243 |> Igniter . delay_task ( "another.delayed" , [ "--opt" , "value" ] )
222244
223- assert capture_io ( fn -> Igniter . display_tasks ( igniter , :dry_run_with_changes , [ ] ) end ) ==
224- """
225-
226- These tasks will be run after the above changes:
227-
228- * \e [31mregular.task \e [33m\e [0m
229- * \e [31manother.regular \e [33m--flag\e [0m
230- * \e [31mdelayed.task \e [33m\e [0m
231- * \e [31manother.delayed \e [33m--opt value\e [0m
232-
233- """
245+ assert_display_output (
246+ "\n These tasks will be run after the above changes:\n \n * \e [31mregular.task \e [33m\e [0m\n * \e [31manother.regular \e [33m--flag\e [0m\n * \e [31mdelayed.task \e [33m\e [0m\n * \e [31manother.delayed \e [33m--opt value\e [0m\n " ,
247+ fn -> Igniter . display_tasks ( igniter , :dry_run_with_changes , [ ] ) end
248+ )
234249 end
235250 end
236251
@@ -314,6 +329,7 @@ defmodule IgniterTest do
314329 end
315330
316331 describe "delay_task" do
332+ # Delayed vs regular storage and sort order are used by run_queued_tasks_with_tracking/1 when running tasks.
317333 test "adds delayed tasks correctly" do
318334 igniter =
319335 test_project ( )
@@ -336,7 +352,7 @@ defmodule IgniterTest do
336352 |> Igniter . add_task ( "second.regular" , [ ] )
337353 |> Igniter . delay_task ( "second.delayed" , [ ] )
338354
339- # Test the internal sorting function
355+ # Same ordering used by run_queued_tasks_with_tracking/1 when applying the task queue
340356 sorted = igniter . tasks |> Igniter . sort_tasks_with_delayed_last ( )
341357
342358 assert [
@@ -347,4 +363,52 @@ defmodule IgniterTest do
347363 ] = sorted
348364 end
349365 end
366+
367+ describe "run_queued_tasks_with_tracking/1" do
368+ test "empty list returns :ok and runs nothing" do
369+ assert :ok == Igniter . run_queued_tasks_with_tracking ( [ ] )
370+ end
371+
372+ test "runs a single task that succeeds" do
373+ assert :ok == Igniter . run_queued_tasks_with_tracking ( [ { "help" , [ ] } ] )
374+ end
375+
376+ test "runs multiple tasks in order when they succeed" do
377+ assert :ok == Igniter . run_queued_tasks_with_tracking ( [ { "help" , [ ] } , { "help" , [ "compile" ] } ] )
378+ end
379+
380+ test "runs delayed tasks after regular tasks (same order as display_tasks and sort_tasks_with_delayed_last)" do
381+ tasks_with_delayed = [
382+ { "help" , [ ] } ,
383+ { "help" , [ "format" ] , :delayed } ,
384+ { "help" , [ "compile" ] }
385+ ]
386+ assert :ok == Igniter . run_queued_tasks_with_tracking ( tasks_with_delayed )
387+ end
388+
389+ test "on task failure, logs concise error with reason and tasks that did not run, then re-raises" do
390+ output =
391+ capture_io ( :stderr , fn ->
392+ try do
393+ Igniter . run_queued_tasks_with_tracking ( [
394+ { "nonexistent.task.xyz.igniter_test" , [ ] } ,
395+ { "compile" , [ ] }
396+ ] )
397+ rescue
398+ _ -> :rescued
399+ end
400+ end )
401+
402+ assert output =~ "Task failed"
403+ assert output =~ "Tasks that did not run"
404+ assert output =~ "mix nonexistent.task.xyz.igniter_test"
405+ assert output =~ "mix compile"
406+ end
407+
408+ test "on task failure, exception is re-raised" do
409+ assert_raise Mix.NoTaskError , fn ->
410+ Igniter . run_queued_tasks_with_tracking ( [ { "nonexistent.task.xyz.igniter_test" , [ ] } ] )
411+ end
412+ end
413+ end
350414end
0 commit comments