github.com/CharukaK/i18n4go@v0.6.0/integration/fixup/fixup_test.go (about)

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