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  }