github.com/Liam-Williams/i18n4go@v0.2.7-0.20201028180611-670cbaceaa6b/integration/fixup/fixup_test.go (about) 1 package fixup_test 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strings" 12 13 "github.com/Liam-Williams/i18n4go/common" 14 15 . "github.com/Liam-Williams/i18n4go/integration/test_helpers" 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 ) 19 20 var _ = Describe("fixup", func() { 21 var ( 22 fixturesPath string 23 cmd *exec.Cmd 24 stdinPipe io.WriteCloser 25 stdoutPipe io.ReadCloser 26 stdoutReader *bufio.Reader 27 curDir string 28 jsonFiles map[string][]byte 29 err error 30 ) 31 32 BeforeEach(func() { 33 curDir, err = os.Getwd() 34 if err != nil { 35 fmt.Println("Could not get working directory") 36 panic(err.Error()) 37 } 38 }) 39 40 AfterEach(func() { 41 // restore json files 42 for path, bytes := range jsonFiles { 43 err = ioutil.WriteFile(path, bytes, 0666) 44 if err != nil { 45 fmt.Println("Could not rewrite backup JSON files") 46 panic(err.Error()) 47 } 48 } 49 50 err = os.Chdir(curDir) 51 if err != nil { 52 fmt.Println("Could not change back to working directory") 53 panic(err.Error()) 54 } 55 }) 56 57 JustBeforeEach(func() { 58 err = os.Chdir(fixturesPath) 59 if err != nil { 60 fmt.Println("Could not change to fixtures directory") 61 panic(err.Error()) 62 } 63 64 // backup json files 65 jsonFiles, err = storeTranslationFiles(".") 66 if err != nil { 67 fmt.Println("Could not back up the JSON files.") 68 panic(err.Error()) 69 } 70 71 //session = Runi18n("-c", "fixup") 72 cmd = exec.Command(I18n4goExec, "-c", "fixup") 73 74 stdinPipe, err = cmd.StdinPipe() 75 if err != nil { 76 fmt.Println("Could not get the stdin pipe.") 77 panic(err.Error()) 78 } 79 80 stdoutPipe, err = cmd.StdoutPipe() 81 if err != nil { 82 fmt.Println("Could not get the stdout pipe.") 83 panic(err.Error()) 84 } 85 stdoutReader = bufio.NewReader(stdoutPipe) 86 87 _, err = cmd.StderrPipe() 88 if err != nil { 89 fmt.Println("Could not get the stderr pipe.") 90 panic(err.Error()) 91 } 92 93 err = cmd.Start() 94 if err != nil { 95 fmt.Println("Could not run fixup") 96 panic(err.Error()) 97 } 98 }) 99 100 Context("When there are no problems", func() { 101 BeforeEach(func() { 102 fixturesPath = filepath.Join("..", "..", "test_fixtures", "fixup", "allgood") 103 }) 104 105 It("returns 0 and prints a reassuring message", func() { 106 Ω(getNextOutputLine(stdoutReader)).Should(Equal("OK")) 107 108 exitCode := cmd.Wait() 109 Ω(exitCode).Should(BeNil()) 110 }) 111 112 Context("for adding", func() { 113 It("does not print empty add messages", func() { 114 Ω(getNextOutputLine(stdoutReader)).ShouldNot(ContainSubstring("Adding these strings")) 115 116 exitCode := cmd.Wait() 117 Ω(exitCode).Should(BeNil()) 118 }) 119 }) 120 121 Context("for deleting", func() { 122 It("does not print empty delete messages", func() { 123 Ω(getNextOutputLine(stdoutReader)).ShouldNot(ContainSubstring("Removing these strings")) 124 125 exitCode := cmd.Wait() 126 Ω(exitCode).Should(BeNil()) 127 }) 128 }) 129 130 Context("For updating", func() { 131 It("does not print empty new or update check message", func() { 132 Ω(getNextOutputLine(stdoutReader)).ShouldNot(ContainSubstring("new or updated string")) 133 134 exitCode := cmd.Wait() 135 Ω(exitCode).Should(BeNil()) 136 }) 137 }) 138 }) 139 140 Context("When there are brand new strings in the code that don't exist in en_US", func() { 141 BeforeEach(func() { 142 fixturesPath = filepath.Join("..", "..", "test_fixtures", "fixup", "notsogood", "add") 143 }) 144 145 It("adds strings to all the locales", func() { 146 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Adding these strings")) 147 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Heal the world")) 148 149 exitCode := cmd.Wait() 150 Ω(exitCode).Should(BeNil()) 151 152 file, err := ioutil.ReadFile(filepath.Join(".", "translations", "en_US.all.json")) 153 Ω(err).ShouldNot(HaveOccurred()) 154 Ω(file).Should(ContainSubstring("\"Heal the world\"")) 155 156 chineseFile, err := ioutil.ReadFile(filepath.Join(".", "translations", "zh_CN.all.json")) 157 Ω(err).ShouldNot(HaveOccurred()) 158 Ω(chineseFile).Should(ContainSubstring("\"Heal the world\"")) 159 }) 160 }) 161 162 Context("When there are old strings in the translations that don't exist in the code", func() { 163 BeforeEach(func() { 164 fixturesPath = filepath.Join("..", "..", "test_fixtures", "fixup", "notsogood", "delete") 165 }) 166 167 It("removes the strings from all the locales", func() { 168 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Removing these strings")) 169 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Heal the world")) 170 171 exitCode := cmd.Wait() 172 Ω(exitCode).Should(BeNil()) 173 174 file, err := ioutil.ReadFile(filepath.Join(".", "translations", "en_US.all.json")) 175 Ω(err).ShouldNot(HaveOccurred()) 176 Ω(file).ShouldNot(ContainSubstring("\"Heal the world\"")) 177 178 chineseFile, err := ioutil.ReadFile(filepath.Join(".", "translations", "zh_CN.all.json")) 179 Ω(err).ShouldNot(HaveOccurred()) 180 Ω(chineseFile).ShouldNot(ContainSubstring("\"Heal the world\"")) 181 }) 182 }) 183 184 Context("When a string has been updated or added in the code", func() { 185 BeforeEach(func() { 186 fixturesPath = filepath.Join("..", "..", "test_fixtures", "fixup", "notsogood", "update") 187 }) 188 189 It("cancels the interactive update when a user types exit", func() { 190 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Is the string \"I like apples.\" a new or updated string? [new/upd]")) 191 192 stdinPipe.Write([]byte("exit\n")) 193 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Canceling fixup")) 194 195 exitCode := cmd.Wait() 196 Ω(exitCode).Should(BeNil()) 197 }) 198 199 It("prompts the user again if they do not input a correct response", func() { 200 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Is the string \"I like apples.\" a new or updated string? [new/upd]")) 201 202 stdinPipe.Write([]byte("nope\n")) 203 204 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Invalid response")) 205 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Is the string \"I like apples.\" a new or updated string? [new/upd]")) 206 207 stdinPipe.Write([]byte("exit\n")) 208 209 exitCode := cmd.Wait() 210 Ω(exitCode).Should(BeNil()) 211 }) 212 213 Context("When the user says the translation was updated", func() { 214 JustBeforeEach(func() { 215 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Is the string \"I like apples.\" a new or updated string? [new/upd]")) 216 stdinPipe.Write([]byte("upd\n")) 217 stdinPipe.Write([]byte("1\n")) 218 }) 219 220 It("Updates the keys for all translation files", func() { 221 cmd.Wait() 222 223 translations, err := common.LoadI18nStringInfos(filepath.Join(".", "translations", "en_US.all.json")) 224 Ω(err).ShouldNot(HaveOccurred()) 225 mappedTranslations, err := common.CreateI18nStringInfoMap(translations) 226 Ω(err).ShouldNot(HaveOccurred()) 227 Ω(mappedTranslations["I like bananas."]).Should(Equal(common.I18nStringInfo{})) 228 Ω(mappedTranslations["I like apples."]).ShouldNot(Equal(common.I18nStringInfo{})) 229 230 translations, err = common.LoadI18nStringInfos(filepath.Join(".", "translations", "zh_CN.all.json")) 231 Ω(err).ShouldNot(HaveOccurred()) 232 mappedTranslations, err = common.CreateI18nStringInfoMap(translations) 233 Ω(err).ShouldNot(HaveOccurred()) 234 Ω(mappedTranslations["I like bananas."]).Should(Equal(common.I18nStringInfo{})) 235 Ω(mappedTranslations["I like apples."]).ShouldNot(Equal(common.I18nStringInfo{})) 236 }) 237 238 It("Updates all the translation", func() { 239 cmd.Wait() 240 241 translations, err := common.LoadI18nStringInfos(filepath.Join(".", "translations", "en_US.all.json")) 242 Ω(err).ShouldNot(HaveOccurred()) 243 mappedTranslations, err := common.CreateI18nStringInfoMap(translations) 244 Ω(err).ShouldNot(HaveOccurred()) 245 Ω(mappedTranslations["I like apples."].Translation).Should(Equal("I like apples.")) 246 }) 247 248 It("marks the foreign language translations as updated", func() { 249 cmd.Wait() 250 251 translations, err := common.LoadI18nStringInfos(filepath.Join(".", "translations", "zh_CN.all.json")) 252 Ω(err).ShouldNot(HaveOccurred()) 253 mappedTranslations, err := common.CreateI18nStringInfoMap(translations) 254 Ω(err).ShouldNot(HaveOccurred()) 255 256 Ω(mappedTranslations["I like apples."].Modified).Should(BeTrue()) 257 Ω(mappedTranslations["I like apples."].Translation).ShouldNot(Equal("I like apples.")) 258 }) 259 }) 260 261 Context("When the user says the translation is new", func() { 262 var ( 263 apple = common.I18nStringInfo{ID: "I like apples.", Translation: "I like apples.", Modified: false} 264 ) 265 266 JustBeforeEach(func() { 267 Ω(getNextOutputLine(stdoutReader)).Should(ContainSubstring("Is the string \"I like apples.\" a new or updated string? [new/upd]")) 268 stdinPipe.Write([]byte("new\n")) 269 }) 270 271 It("adds the new translation and deletes the old translation from all translation files", func() { 272 cmd.Wait() 273 274 translations, err := common.LoadI18nStringInfos(filepath.Join(".", "translations", "en_US.all.json")) 275 Ω(err).ShouldNot(HaveOccurred()) 276 mappedTranslations, err := common.CreateI18nStringInfoMap(translations) 277 Ω(err).ShouldNot(HaveOccurred()) 278 Ω(mappedTranslations["I like bananas."]).Should(Equal(common.I18nStringInfo{})) 279 Ω(mappedTranslations["I like apples."]).Should(Equal(apple)) 280 281 translations, err = common.LoadI18nStringInfos(filepath.Join(".", "translations", "zh_CN.all.json")) 282 Ω(err).ShouldNot(HaveOccurred()) 283 mappedTranslations, err = common.CreateI18nStringInfoMap(translations) 284 Ω(err).ShouldNot(HaveOccurred()) 285 Ω(mappedTranslations["I like bananas."]).Should(Equal(common.I18nStringInfo{})) 286 Ω(mappedTranslations["I like apples."]).Should(Equal(apple)) 287 }) 288 }) 289 }) 290 291 Context("When a foreign language is missing an english translation", func() { 292 BeforeEach(func() { 293 fixturesPath = filepath.Join("..", "..", "test_fixtures", "fixup", "notsogood", "missing_foreign_key") 294 }) 295 296 It("adds the extra translation", func() { 297 cmd.Wait() 298 299 translations, err := common.LoadI18nStringInfos(filepath.Join(".", "translations", "zh_CN.all.json")) 300 Ω(err).ShouldNot(HaveOccurred()) 301 mappedTranslations, err := common.CreateI18nStringInfoMap(translations) 302 Ω(err).ShouldNot(HaveOccurred()) 303 Ω(mappedTranslations["I'm the extra key"]).Should(Equal( 304 common.I18nStringInfo{ID: "I'm the extra key", Translation: "I'm the extra key", Modified: false}, 305 )) 306 }) 307 }) 308 309 Context("When a foreign language has an extra key", func() { 310 BeforeEach(func() { 311 fixturesPath = filepath.Join("..", "..", "test_fixtures", "fixup", "notsogood", "extra_foreign_key") 312 }) 313 314 It("removes the extra translation", func() { 315 cmd.Wait() 316 317 translations, err := common.LoadI18nStringInfos(filepath.Join(".", "translations", "zh_CN.all.json")) 318 Ω(err).ShouldNot(HaveOccurred()) 319 mappedTranslations, err := common.CreateI18nStringInfoMap(translations) 320 Ω(err).ShouldNot(HaveOccurred()) 321 Ω(mappedTranslations["I'm the extra key"]).Should(Equal(common.I18nStringInfo{})) 322 }) 323 }) 324 }) 325 326 func storeTranslationFiles(dir string) (files map[string][]byte, err error) { 327 files = make(map[string][]byte) 328 contents, _ := ioutil.ReadDir(dir) 329 330 for _, fileInfo := range contents { 331 if !fileInfo.IsDir() { 332 name := fileInfo.Name() 333 334 if strings.HasSuffix(name, ".all.json") { 335 path := filepath.Join(dir, fileInfo.Name()) 336 files[path], err = ioutil.ReadFile(path) 337 338 if err != nil { 339 return nil, err 340 } 341 } 342 } else { 343 newFiles, err := storeTranslationFiles(filepath.Join(dir, fileInfo.Name())) 344 if err != nil { 345 return nil, err 346 } 347 348 for path, bytes := range newFiles { 349 files[path] = bytes 350 } 351 } 352 } 353 354 return 355 } 356 357 func getNextOutputLine(reader *bufio.Reader) string { 358 line, _, err := reader.ReadLine() 359 if err != nil { 360 panic(err) 361 } 362 363 return string(line) 364 }