1Code.require_file("../../test_helper.exs", __DIR__)
2
3defmodule Mix.Tasks.NewTest do
4  use MixTest.Case
5
6  test "new" do
7    in_tmp("new", fn ->
8      Mix.Tasks.New.run(["hello_world"])
9
10      assert_file("hello_world/mix.exs", fn file ->
11        assert file =~ "app: :hello_world"
12        assert file =~ "version: \"0.1.0\""
13      end)
14
15      assert_file("hello_world/README.md", ~r/# HelloWorld\n/)
16      assert_file("hello_world/.gitignore")
17
18      assert_file("hello_world/lib/hello_world.ex", ~r/defmodule HelloWorld do/)
19      assert_file("hello_world/test/test_helper.exs", ~r/ExUnit.start()/)
20
21      assert_file("hello_world/test/hello_world_test.exs", fn file ->
22        assert file =~ ~r/defmodule HelloWorldTest do/
23        assert file =~ "assert HelloWorld.hello() == :world"
24      end)
25
26      assert_received {:mix_shell, :info, ["* creating mix.exs"]}
27      assert_received {:mix_shell, :info, ["* creating lib/hello_world.ex"]}
28
29      # Ensure formatting is setup and consistent.
30      File.cd!("hello_world", fn ->
31        Mix.Tasks.Format.run(["--check-formatted"])
32      end)
33    end)
34  end
35
36  test "new with --sup" do
37    in_tmp("new sup", fn ->
38      Mix.Tasks.New.run(["hello_world", "--sup"])
39
40      assert_file("hello_world/mix.exs", fn file ->
41        assert file =~ "app: :hello_world"
42        assert file =~ "version: \"0.1.0\""
43        assert file =~ "mod: {HelloWorld.Application, []}"
44      end)
45
46      assert_file("hello_world/README.md", ~r/# HelloWorld\n/)
47      assert_file("hello_world/.gitignore")
48
49      assert_file("hello_world/lib/hello_world.ex", fn file ->
50        assert file =~ "defmodule HelloWorld do"
51        assert file =~ "def hello do"
52      end)
53
54      assert_file("hello_world/lib/hello_world/application.ex", fn file ->
55        assert file =~ "defmodule HelloWorld.Application do"
56        assert file =~ "use Application"
57        assert file =~ "@impl true"
58        assert file =~ "Supervisor.start_link(children, opts)"
59      end)
60
61      assert_file("hello_world/test/test_helper.exs", ~r/ExUnit.start()/)
62      assert_file("hello_world/test/hello_world_test.exs", ~r/defmodule HelloWorldTest do/)
63
64      assert_received {:mix_shell, :info, ["* creating mix.exs"]}
65      assert_received {:mix_shell, :info, ["* creating lib/hello_world.ex"]}
66
67      # Ensure formatting is setup and consistent.
68      File.cd!("hello_world", fn ->
69        Mix.Tasks.Format.run(["--check-formatted"])
70      end)
71    end)
72  end
73
74  test "new with --module uses the module name also for naming the files in lib and test" do
75    in_tmp("new_with_module", fn ->
76      Mix.Tasks.New.run(["hello_world", "--module", "MyTestModule"])
77      assert_file("hello_world/lib/my_test_module.ex", ~r/defmodule MyTestModule do/)
78      assert_file("hello_world/test/my_test_module_test.exs", ~r/defmodule MyTestModuleTest do/)
79    end)
80  end
81
82  test "new with --app" do
83    in_tmp("new app", fn ->
84      Mix.Tasks.New.run(["HELLO_WORLD", "--app", "hello_world"])
85
86      assert_file("HELLO_WORLD/mix.exs", fn file ->
87        assert file =~ "app: :hello_world"
88        assert file =~ "version: \"0.1.0\""
89      end)
90
91      assert_file("HELLO_WORLD/README.md", ~r/# HelloWorld\n/)
92      assert_file("HELLO_WORLD/.gitignore")
93
94      assert_file("HELLO_WORLD/lib/hello_world.ex", ~r/defmodule HelloWorld do/)
95
96      assert_file("HELLO_WORLD/test/test_helper.exs", ~r/ExUnit.start()/)
97      assert_file("HELLO_WORLD/test/hello_world_test.exs", ~r/defmodule HelloWorldTest do/)
98
99      assert_received {:mix_shell, :info, ["* creating mix.exs"]}
100      assert_received {:mix_shell, :info, ["* creating lib/hello_world.ex"]}
101
102      # Ensure formatting is setup and consistent.
103      File.cd!("HELLO_WORLD", fn ->
104        Mix.Tasks.Format.run(["--check-formatted"])
105      end)
106    end)
107  end
108
109  test "new with --umbrella" do
110    in_tmp("new umbrella", fn ->
111      Mix.Tasks.New.run(["hello_world", "--umbrella"])
112
113      assert_file("hello_world/mix.exs", fn file ->
114        assert file =~ "apps_path: \"apps\""
115      end)
116
117      assert_file("hello_world/README.md", ~r/# HelloWorld\n/)
118      assert_file("hello_world/.gitignore")
119
120      assert_received {:mix_shell, :info, ["* creating mix.exs"]}
121
122      # Ensure formatting is setup and consistent.
123      File.cd!("hello_world", fn ->
124        Mix.Tasks.Format.run(["--check-formatted"])
125      end)
126    end)
127  end
128
129  test "new inside umbrella" do
130    in_fixture("umbrella_dep/deps/umbrella", fn ->
131      File.cd!("apps", fn ->
132        Mix.Tasks.New.run(["hello_world"])
133
134        assert_file("hello_world/mix.exs", fn file ->
135          assert file =~ "deps_path: \"../../deps\""
136          assert file =~ "lockfile: \"../../mix.lock\""
137        end)
138
139        # Ensure formatting is setup and consistent.
140        File.cd!("hello_world", fn ->
141          Mix.Tasks.Format.run(["--check-formatted"])
142        end)
143      end)
144    end)
145  end
146
147  test "new with dot" do
148    in_tmp("new_with_dot", fn ->
149      Mix.Tasks.New.run(["."])
150      assert_file("lib/new_with_dot.ex", ~r/defmodule NewWithDot do/)
151    end)
152  end
153
154  test "new with invalid args" do
155    in_tmp("new with an invalid application name", fn ->
156      assert_raise Mix.Error,
157                   ~r"Application name must start with a lowercase ASCII letter",
158                   fn ->
159                     Mix.Tasks.New.run(["007invalid"])
160                   end
161
162      assert_raise Mix.Error,
163                   ~r"Cannot use application name \"tools\" because it is already used by Erlang/OTP or Elixir",
164                   fn ->
165                     Mix.Tasks.New.run(["tools"])
166                   end
167
168      assert_raise Mix.Error,
169                   ~r"followed by lowercase ASCII letters, numbers, or underscores",
170                   fn ->
171                     Mix.Tasks.New.run(["invAlid"])
172                   end
173
174      assert_raise Mix.Error,
175                   ~r"followed by lowercase ASCII letters, numbers, or underscores",
176                   fn ->
177                     Mix.Tasks.New.run(["inválido"])
178                   end
179    end)
180
181    in_tmp("new with an invalid application name from the app option", fn ->
182      assert_raise Mix.Error, ~r"Application name must start with a lowercase ASCII letter", fn ->
183        Mix.Tasks.New.run(["valid", "--app", "007invalid"])
184      end
185    end)
186
187    in_tmp("new with an invalid module name from the module options", fn ->
188      assert_raise Mix.Error, ~r"Module name must be a valid Elixir alias", fn ->
189        Mix.Tasks.New.run(["valid", "--module", "not.valid"])
190      end
191    end)
192
193    in_tmp("new with an already taken module name from the module options", fn ->
194      assert_raise Mix.Error, ~r"Module name \w+ is already taken", fn ->
195        Mix.Tasks.New.run(["valid", "--module", "Mix"])
196      end
197    end)
198
199    in_tmp("new without a specified path", fn ->
200      assert_raise Mix.Error, "Expected PATH to be given, please use \"mix new PATH\"", fn ->
201        Mix.Tasks.New.run([])
202      end
203    end)
204  end
205
206  test "new with existent directory" do
207    in_tmp("new_with_existent_directory", fn ->
208      File.mkdir_p!("my_app")
209      send(self(), {:mix_shell_input, :yes?, false})
210
211      assert_raise Mix.Error, "Please select another directory for installation", fn ->
212        Mix.Tasks.New.run(["my_app"])
213      end
214    end)
215  end
216
217  defp assert_file(file) do
218    assert File.regular?(file), "Expected #{file} to exist, but does not"
219  end
220
221  defp assert_file(file, match) do
222    cond do
223      is_struct(match, Regex) ->
224        assert_file(file, &assert(&1 =~ match))
225
226      is_function(match, 1) ->
227        assert_file(file)
228        match.(File.read!(file))
229    end
230  end
231end
232