index.spec.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { getLanguageDictionary, getLanguagesDictionaries, registerLanguageDictionary } from 'handsontable/i18n/dictionariesManager';
  2. import { register as registerPhraseFormatter } from 'handsontable/i18n/phraseFormatters';
  3. import plPL from 'handsontable/i18n/languages/pl-PL';
  4. import * as allLanguages from 'handsontable/i18n/languages';
  5. import * as constants from 'handsontable/i18n/constants';
  6. import Handsontable from 'handsontable';
  7. import fs from 'fs';
  8. describe('i18n', () => {
  9. beforeAll(() => {
  10. // Note: please keep in mind that this language will be registered for all unit tests!
  11. // It's stored globally for already loaded Handsontable library.
  12. Handsontable.languages.registerLanguageDictionary(plPL);
  13. });
  14. const INSERT_ROW_ABOVE_DICTIONARY_KEY = 'ContextMenu:items.insertRowAbove';
  15. const INSERT_ROW_ABOVE_IN_POLISH_LANGUAGE = 'Wstaw wiersz powyżej';
  16. it('should extend Handsontable global by functions and constants for translation', () => {
  17. expect(Handsontable.languages.getLanguageDictionary).toBe(getLanguageDictionary);
  18. expect(Handsontable.languages.getLanguagesDictionaries).toBe(getLanguagesDictionaries);
  19. expect(Handsontable.languages.registerLanguageDictionary).toBe(registerLanguageDictionary);
  20. expect(Handsontable.languages.dictionaryKeys).toBe(constants);
  21. });
  22. it('should get translated phrase by `getTranslatedPhrase` function', () => {
  23. expect(Handsontable.languages.getTranslatedPhrase(plPL.languageCode, INSERT_ROW_ABOVE_DICTIONARY_KEY)).toEqual(plPL[INSERT_ROW_ABOVE_DICTIONARY_KEY]);
  24. expect(Handsontable.languages.getTranslatedPhrase(plPL.languageCode, INSERT_ROW_ABOVE_DICTIONARY_KEY)).toEqual(INSERT_ROW_ABOVE_IN_POLISH_LANGUAGE);
  25. });
  26. it('should get `null` when trying to translate phrase by passing language code of not registered language', () => {
  27. const notRegisteredLanguageCode = 'br-Za';
  28. expect(Handsontable.languages.getTranslatedPhrase(notRegisteredLanguageCode, INSERT_ROW_ABOVE_DICTIONARY_KEY)).toEqual(null);
  29. });
  30. it('should call formatters when `getTranslatedPhrase` function is called', () => {
  31. let formatterWasRun = false;
  32. registerPhraseFormatter('formatterChangingVariable', () => { formatterWasRun = true; });
  33. expect(formatterWasRun).toEqual(false);
  34. Handsontable.languages.getTranslatedPhrase(plPL.languageCode, INSERT_ROW_ABOVE_DICTIONARY_KEY);
  35. expect(formatterWasRun).toEqual(true);
  36. });
  37. describe('Collection of language packs', () => {
  38. // One of keys can be `__esModule` added by Babel. We separate it.
  39. const indexFileExportNames = Object.getOwnPropertyNames(allLanguages).filter(exportedKey => exportedKey !== '__esModule');
  40. const dictionaries = indexFileExportNames.map(exportName => allLanguages[exportName]);
  41. const languageCodes = dictionaries.map(dictionary => dictionary.languageCode);
  42. const keysGroupedByDictionary = dictionaries.map(dictionary => Object.keys(dictionary));
  43. const dictionariesKeys = [].concat([], ...keysGroupedByDictionary).filter(key => key !== 'languageCode');
  44. const valuesGroupedByDictionary = dictionaries.map(dictionary => Object.values(dictionary));
  45. const dictionariesValuesWithArrays = [].concat([], ...valuesGroupedByDictionary);
  46. const dictionariesValuesWithoutArrays = [].concat([], ...dictionariesValuesWithArrays);
  47. const filesInsideLanguageDictionary = fs.readdirSync('./src/i18n/languages');
  48. const languagePacks = filesInsideLanguageDictionary.filter(name => name !== 'index.js');
  49. describe('with every dictionary from `src/i18n/languages` folder', () => {
  50. // We take all dictionaries from export inside `src/i18n/languages/index.js` file.
  51. // We do not use dynamic import to take them from particular language files.
  52. // Workaround to stop next tests when below expectations fail. Without the conditions rest of tests is useless.
  53. // Jasmine problem described: https://github.com/jasmine/jasmine/issues/414
  54. expect(indexFileExportNames.length).toBeGreaterThan(0);
  55. expect(languageCodes.length).toBeGreaterThan(0);
  56. expect(indexFileExportNames.length).toEqual(languagePacks.length);
  57. it('should not contain two or more dictionaries with the same language code', () => {
  58. expect(languageCodes).toEqual(Array.from(new Set(languageCodes).values()));
  59. });
  60. describe('File `src/i18n/languages/index.js`', () => {
  61. it('should provide export names sorted alphabetically', () => {
  62. // Does ES6 introduce a well-defined order of enumeration for object properties?
  63. // https://stackoverflow.com/a/30919039
  64. expect(indexFileExportNames.length).toBeGreaterThan(0);
  65. expect(indexFileExportNames).toEqual(indexFileExportNames.slice().sort());
  66. });
  67. it('should provide export names corresponding to dictionaries file names', () => {
  68. // two lowercase letters, two uppercase letters, for example: `esPY`.
  69. expect(indexFileExportNames).toBeListFulfillingCondition(exportName =>
  70. languagePacks.map(languagePack => languagePack.replace('.js', '').replace('-', '')).includes(exportName));
  71. });
  72. });
  73. describe('Particular dictionary', () => {
  74. it('should contain language code of proper format', () => {
  75. // two lowercase letters, hyphen, two uppercase letters, for example: `es-PY`.
  76. expect(languageCodes).toBeListFulfillingCondition(languageCode => /^([a-z]{2})-([A-Z]{2})$/.test(languageCode));
  77. });
  78. it('should have file name corresponding to its language code', () => {
  79. // two lowercase letters, hyphen, two uppercase letters and JavaScript file extension, for example: `es-PY.js`.
  80. expect(languagePacks).toBeListFulfillingCondition(languagePack => languageCodes.includes(languagePack.replace('.js', '')));
  81. });
  82. it('should contain only predefined keys (checking for typo)', () => {
  83. // If you would like to extend any dictionary by a custom key, define also corresponding, exported constant
  84. // for it inside the `src/i18n/constants.js` file.
  85. const predefinedDictionaryKeys = Object.values(constants);
  86. expect(dictionariesKeys).toBeListFulfillingCondition(dictionaryKey => predefinedDictionaryKeys.includes(dictionaryKey));
  87. });
  88. it('should contain values defined as strings or arrays of strings, without unnecessary whitespace characters', () => {
  89. expect(dictionariesValuesWithArrays).toBeListFulfillingCondition(value => typeof value === 'string' || Array.isArray(value));
  90. expect(dictionariesValuesWithoutArrays).toBeListFulfillingCondition(value => typeof value === 'string');
  91. expect(dictionariesValuesWithoutArrays).toBeListFulfillingCondition(value => value === value.trim());
  92. expect(dictionariesValuesWithoutArrays).toBeListFulfillingCondition(value => / {2,}/.test(value) === false);
  93. });
  94. });
  95. });
  96. });
  97. });