commands_test.go (11921B)
1 // Copyright 2019 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package commands 15 16 import ( 17 "fmt" 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "testing" 22 23 "github.com/gohugoio/hugo/config" 24 25 "github.com/spf13/afero" 26 27 "github.com/gohugoio/hugo/hugofs" 28 29 "github.com/gohugoio/hugo/common/types" 30 31 "github.com/spf13/cobra" 32 33 qt "github.com/frankban/quicktest" 34 ) 35 36 func TestExecute(t *testing.T) { 37 c := qt.New(t) 38 39 createSite := func(c *qt.C) string { 40 dir := createSimpleTestSite(t, testSiteConfig{}) 41 return dir 42 } 43 44 c.Run("hugo", func(c *qt.C) { 45 dir := createSite(c) 46 resp := Execute([]string{"-s=" + dir}) 47 c.Assert(resp.Err, qt.IsNil) 48 result := resp.Result 49 c.Assert(len(result.Sites) == 1, qt.Equals, true) 50 c.Assert(len(result.Sites[0].RegularPages()) == 1, qt.Equals, true) 51 c.Assert(result.Sites[0].Info.Params()["myparam"], qt.Equals, "paramproduction") 52 }) 53 54 c.Run("hugo, set environment", func(c *qt.C) { 55 dir := createSite(c) 56 resp := Execute([]string{"-s=" + dir, "-e=staging"}) 57 c.Assert(resp.Err, qt.IsNil) 58 result := resp.Result 59 c.Assert(result.Sites[0].Info.Params()["myparam"], qt.Equals, "paramstaging") 60 }) 61 62 c.Run("convert toJSON", func(c *qt.C) { 63 dir := createSite(c) 64 output := filepath.Join(dir, "myjson") 65 resp := Execute([]string{"convert", "toJSON", "-s=" + dir, "-e=staging", "-o=" + output}) 66 c.Assert(resp.Err, qt.IsNil) 67 converted := readFileFrom(c, filepath.Join(output, "content", "p1.md")) 68 c.Assert(converted, qt.Equals, "{\n \"title\": \"P1\",\n \"weight\": 1\n}\n\nContent\n\n", qt.Commentf(converted)) 69 }) 70 71 c.Run("config, set environment", func(c *qt.C) { 72 dir := createSite(c) 73 out, err := captureStdout(func() error { 74 resp := Execute([]string{"config", "-s=" + dir, "-e=staging"}) 75 return resp.Err 76 }) 77 c.Assert(err, qt.IsNil) 78 c.Assert(out, qt.Contains, "params = map[myparam:paramstaging]", qt.Commentf(out)) 79 }) 80 81 c.Run("deploy, environment set", func(c *qt.C) { 82 dir := createSite(c) 83 resp := Execute([]string{"deploy", "-s=" + dir, "-e=staging", "--target=mydeployment", "--dryRun"}) 84 c.Assert(resp.Err, qt.Not(qt.IsNil)) 85 c.Assert(resp.Err.Error(), qt.Contains, `no driver registered for "hugocloud"`) 86 }) 87 88 c.Run("list", func(c *qt.C) { 89 dir := createSite(c) 90 out, err := captureStdout(func() error { 91 resp := Execute([]string{"list", "all", "-s=" + dir, "-e=staging"}) 92 return resp.Err 93 }) 94 c.Assert(err, qt.IsNil) 95 c.Assert(out, qt.Contains, "p1.md") 96 }) 97 98 c.Run("new theme", func(c *qt.C) { 99 dir := createSite(c) 100 themesDir := filepath.Join(dir, "mythemes") 101 resp := Execute([]string{"new", "theme", "mytheme", "-s=" + dir, "-e=staging", "--themesDir=" + themesDir}) 102 c.Assert(resp.Err, qt.IsNil) 103 themeTOML := readFileFrom(c, filepath.Join(themesDir, "mytheme", "theme.toml")) 104 c.Assert(themeTOML, qt.Contains, "name = \"Mytheme\"") 105 }) 106 107 c.Run("new site", func(c *qt.C) { 108 dir := createSite(c) 109 siteDir := filepath.Join(dir, "mysite") 110 resp := Execute([]string{"new", "site", siteDir, "-e=staging"}) 111 c.Assert(resp.Err, qt.IsNil) 112 config := readFileFrom(c, filepath.Join(siteDir, "config.toml")) 113 c.Assert(config, qt.Contains, "baseURL = 'http://example.org/'") 114 checkNewSiteInited(c, siteDir) 115 }) 116 } 117 118 func checkNewSiteInited(c *qt.C, basepath string) { 119 paths := []string{ 120 filepath.Join(basepath, "layouts"), 121 filepath.Join(basepath, "content"), 122 filepath.Join(basepath, "archetypes"), 123 filepath.Join(basepath, "static"), 124 filepath.Join(basepath, "data"), 125 filepath.Join(basepath, "config.toml"), 126 } 127 128 for _, path := range paths { 129 _, err := os.Stat(path) 130 c.Assert(err, qt.IsNil) 131 } 132 } 133 134 func readFileFrom(c *qt.C, filename string) string { 135 c.Helper() 136 filename = filepath.Clean(filename) 137 b, err := afero.ReadFile(hugofs.Os, filename) 138 c.Assert(err, qt.IsNil) 139 return string(b) 140 } 141 142 func TestFlags(t *testing.T) { 143 c := qt.New(t) 144 145 noOpRunE := func(cmd *cobra.Command, args []string) error { 146 return nil 147 } 148 149 tests := []struct { 150 name string 151 args []string 152 check func(c *qt.C, cmd *serverCmd) 153 }{ 154 { 155 // https://github.com/gohugoio/hugo/issues/7642 156 name: "ignoreVendorPaths", 157 args: []string{"server", "--ignoreVendorPaths=github.com/**"}, 158 check: func(c *qt.C, cmd *serverCmd) { 159 cfg := config.NewWithTestDefaults() 160 cmd.flagsToConfig(cfg) 161 c.Assert(cfg.Get("ignoreVendorPaths"), qt.Equals, "github.com/**") 162 }, 163 }, 164 { 165 name: "Persistent flags", 166 args: []string{ 167 "server", 168 "--config=myconfig.toml", 169 "--configDir=myconfigdir", 170 "--contentDir=mycontent", 171 "--disableKinds=page,home", 172 "--environment=testing", 173 "--configDir=myconfigdir", 174 "--layoutDir=mylayouts", 175 "--theme=mytheme", 176 "--gc", 177 "--themesDir=mythemes", 178 "--cleanDestinationDir", 179 "--navigateToChanged", 180 "--disableLiveReload", 181 "--noHTTPCache", 182 "--printI18nWarnings", 183 "--destination=/tmp/mydestination", 184 "-b=https://example.com/b/", 185 "--port=1366", 186 "--renderToDisk", 187 "--source=mysource", 188 "--printPathWarnings", 189 "--printUnusedTemplates", 190 }, 191 check: func(c *qt.C, sc *serverCmd) { 192 c.Assert(sc, qt.Not(qt.IsNil)) 193 c.Assert(sc.navigateToChanged, qt.Equals, true) 194 c.Assert(sc.disableLiveReload, qt.Equals, true) 195 c.Assert(sc.noHTTPCache, qt.Equals, true) 196 c.Assert(sc.renderToDisk, qt.Equals, true) 197 c.Assert(sc.serverPort, qt.Equals, 1366) 198 c.Assert(sc.environment, qt.Equals, "testing") 199 200 cfg := config.NewWithTestDefaults() 201 sc.flagsToConfig(cfg) 202 c.Assert(cfg.GetString("publishDir"), qt.Equals, "/tmp/mydestination") 203 c.Assert(cfg.GetString("contentDir"), qt.Equals, "mycontent") 204 c.Assert(cfg.GetString("layoutDir"), qt.Equals, "mylayouts") 205 c.Assert(cfg.GetStringSlice("theme"), qt.DeepEquals, []string{"mytheme"}) 206 c.Assert(cfg.GetString("themesDir"), qt.Equals, "mythemes") 207 c.Assert(cfg.GetString("baseURL"), qt.Equals, "https://example.com/b/") 208 209 c.Assert(cfg.Get("disableKinds"), qt.DeepEquals, []string{"page", "home"}) 210 211 c.Assert(cfg.GetBool("gc"), qt.Equals, true) 212 213 // The flag is named printPathWarnings 214 c.Assert(cfg.GetBool("logPathWarnings"), qt.Equals, true) 215 216 // The flag is named printI18nWarnings 217 c.Assert(cfg.GetBool("logI18nWarnings"), qt.Equals, true) 218 }, 219 }, 220 } 221 222 for _, test := range tests { 223 c.Run(test.name, func(c *qt.C) { 224 b := newCommandsBuilder() 225 root := b.addAll().build() 226 227 for _, cmd := range b.commands { 228 if cmd.getCommand() == nil { 229 continue 230 } 231 // We are only intereseted in the flag handling here. 232 cmd.getCommand().RunE = noOpRunE 233 } 234 rootCmd := root.getCommand() 235 rootCmd.SetArgs(test.args) 236 c.Assert(rootCmd.Execute(), qt.IsNil) 237 test.check(c, b.commands[0].(*serverCmd)) 238 }) 239 } 240 } 241 242 func TestCommandsExecute(t *testing.T) { 243 c := qt.New(t) 244 245 dir := createSimpleTestSite(t, testSiteConfig{}) 246 dirOut := t.TempDir() 247 248 sourceFlag := fmt.Sprintf("-s=%s", dir) 249 250 tests := []struct { 251 commands []string 252 flags []string 253 expectErrToContain string 254 }{ 255 // TODO(bep) permission issue on my OSX? "operation not permitted" {[]string{"check", "ulimit"}, nil, false}, 256 {[]string{"env"}, nil, ""}, 257 {[]string{"version"}, nil, ""}, 258 // no args = hugo build 259 {nil, []string{sourceFlag}, ""}, 260 {nil, []string{sourceFlag, "--renderToMemory"}, ""}, 261 {[]string{"completion", "bash"}, nil, ""}, 262 {[]string{"completion", "fish"}, nil, ""}, 263 {[]string{"completion", "powershell"}, nil, ""}, 264 {[]string{"completion", "zsh"}, nil, ""}, 265 {[]string{"config"}, []string{sourceFlag}, ""}, 266 {[]string{"convert", "toTOML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "toml")}, ""}, 267 {[]string{"convert", "toYAML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "yaml")}, ""}, 268 {[]string{"convert", "toJSON"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "json")}, ""}, 269 {[]string{"gen", "chromastyles"}, []string{"--style=manni"}, ""}, 270 {[]string{"gen", "doc"}, []string{"--dir=" + filepath.Join(dirOut, "doc")}, ""}, 271 {[]string{"gen", "man"}, []string{"--dir=" + filepath.Join(dirOut, "man")}, ""}, 272 {[]string{"list", "drafts"}, []string{sourceFlag}, ""}, 273 {[]string{"list", "expired"}, []string{sourceFlag}, ""}, 274 {[]string{"list", "future"}, []string{sourceFlag}, ""}, 275 {[]string{"new", "new-page.md"}, []string{sourceFlag}, ""}, 276 {[]string{"new", "site", filepath.Join(dirOut, "new-site")}, nil, ""}, 277 {[]string{"unknowncommand"}, nil, "unknown command"}, 278 // TODO(bep) cli refactor fix https://github.com/gohugoio/hugo/issues/4450 279 //{[]string{"new", "theme", filepath.Join(dirOut, "new-theme")}, nil,false}, 280 } 281 282 for _, test := range tests { 283 name := "hugo" 284 if len(test.commands) > 0 { 285 name = test.commands[0] 286 } 287 c.Run(name, func(c *qt.C) { 288 b := newCommandsBuilder().addAll().build() 289 hugoCmd := b.getCommand() 290 test.flags = append(test.flags, "--quiet") 291 hugoCmd.SetArgs(append(test.commands, test.flags...)) 292 293 // TODO(bep) capture output and add some simple asserts 294 // TODO(bep) misspelled subcommands does not return an error. We should investigate this 295 // but before that, check for "Error: unknown command". 296 297 _, err := hugoCmd.ExecuteC() 298 if test.expectErrToContain != "" { 299 c.Assert(err, qt.Not(qt.IsNil)) 300 c.Assert(err.Error(), qt.Contains, test.expectErrToContain) 301 } else { 302 c.Assert(err, qt.IsNil) 303 } 304 305 // Assert that we have not left any development debug artifacts in 306 // the code. 307 if b.c != nil { 308 _, ok := b.c.publishDirFs.(types.DevMarker) 309 c.Assert(ok, qt.Equals, false) 310 } 311 }) 312 313 } 314 } 315 316 type testSiteConfig struct { 317 configTOML string 318 contentDir string 319 } 320 321 func createSimpleTestSite(t testing.TB, cfg testSiteConfig) string { 322 dir := t.TempDir() 323 324 cfgStr := ` 325 326 baseURL = "https://example.org" 327 title = "Hugo Commands" 328 329 330 ` 331 332 contentDir := "content" 333 334 if cfg.configTOML != "" { 335 cfgStr = cfg.configTOML 336 } 337 if cfg.contentDir != "" { 338 contentDir = cfg.contentDir 339 } 340 341 os.MkdirAll(filepath.Join(dir, "public"), 0777) 342 343 // Just the basic. These are for CLI tests, not site testing. 344 writeFile(t, filepath.Join(dir, "config.toml"), cfgStr) 345 writeFile(t, filepath.Join(dir, "config", "staging", "params.toml"), `myparam="paramstaging"`) 346 writeFile(t, filepath.Join(dir, "config", "staging", "deployment.toml"), ` 347 [[targets]] 348 name = "mydeployment" 349 URL = "hugocloud://hugotestbucket" 350 `) 351 352 writeFile(t, filepath.Join(dir, "config", "testing", "params.toml"), `myparam="paramtesting"`) 353 writeFile(t, filepath.Join(dir, "config", "production", "params.toml"), `myparam="paramproduction"`) 354 355 writeFile(t, filepath.Join(dir, "static", "myfile.txt"), `Hello World!`) 356 357 writeFile(t, filepath.Join(dir, contentDir, "p1.md"), ` 358 --- 359 title: "P1" 360 weight: 1 361 --- 362 363 Content 364 365 `) 366 367 writeFile(t, filepath.Join(dir, "layouts", "_default", "single.html"), ` 368 369 Single: {{ .Title }} 370 371 `) 372 373 writeFile(t, filepath.Join(dir, "layouts", "_default", "list.html"), ` 374 375 List: {{ .Title }} 376 Environment: {{ hugo.Environment }} 377 378 For issue 9788: 379 {{ $foo :="abc" | resources.FromString "foo.css" | minify | resources.PostProcess }} 380 PostProcess: {{ $foo.RelPermalink }} 381 382 `) 383 384 return dir 385 } 386 387 func writeFile(t testing.TB, filename, content string) { 388 must(t, os.MkdirAll(filepath.Dir(filename), os.FileMode(0755))) 389 must(t, ioutil.WriteFile(filename, []byte(content), os.FileMode(0755))) 390 } 391 392 func must(t testing.TB, err error) { 393 if err != nil { 394 t.Fatal(err) 395 } 396 }