From 5bbfe6916c591a83f353ea6aa274614002e33556 Mon Sep 17 00:00:00 2001 From: rafaelgssa Date: Sat, 18 Jul 2020 21:07:18 -0300 Subject: [PATCH 1/4] Implement option to check for duplicate values --- src/identical-keys.js | 12 +++++++- src/util/compare-translations-structure.js | 32 ++++++++++++++++++---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/identical-keys.js b/src/identical-keys.js index f941b99..9558895 100644 --- a/src/identical-keys.js +++ b/src/identical-keys.js @@ -34,6 +34,11 @@ const getKeyStructureFromMap = (filePathMap, sourceFilePath) => { If it's an object , then it should have a mapping b/w file names and what key structure file to require. + + checkDuplicateValues = boolean + + If true, the values will also be checked for duplicates, + in comparison to the file specified in filePath. } */ @@ -159,10 +164,12 @@ const identicalKeys = (context, source, sourceFilePath) => { return errors; } + const { checkDuplicateValues = false } = comparisonOptions; const diffString = compareTranslationsStructure( settings, keyStructure, - currentTranslations + currentTranslations, + checkDuplicateValues ); if (noDifferenceRegex.test(diffString.trim())) { @@ -196,6 +203,9 @@ module.exports = { properties: { filePath: { type: ['string', 'object'] + }, + checkDuplicateValues: { + type: 'boolean' } }, type: 'object' diff --git a/src/util/compare-translations-structure.js b/src/util/compare-translations-structure.js index c4b5a68..bebfd89 100644 --- a/src/util/compare-translations-structure.js +++ b/src/util/compare-translations-structure.js @@ -1,4 +1,5 @@ const set = require('lodash.set'); +const get = require('lodash.get'); const diff = require('jest-diff'); const deepForOwn = require('./deep-for-own'); @@ -7,11 +8,18 @@ const DIFF_OPTIONS = { contextLines: 1 }; +const noDifferenceRegex = /Compared\s+values\s+have\s+no\s+visual\s+difference/i; + // we don't care what the actual values are. // lodash.set will automatically convert a previous string value // into an object, if the current path states that a key is nested inside. // reminder, deepForOwn goes from the root level to the deepest level (preorder) -const compareTranslationsStructure = (settings, translationsA, translationsB) => { +const compareTranslationsStructure = ( + settings, + translationsA, + translationsB, + checkDuplicateValues +) => { const augmentedTranslationsA = {}; const augmentedTranslationsB = {}; @@ -21,13 +29,27 @@ const compareTranslationsStructure = (settings, translationsA, translationsB) => ignorePaths }; - deepForOwn(translationsA, (value, key, path) => { + const duplicateTranslations = {}; + + deepForOwn(translationsA, (valueA, key, path) => { set(augmentedTranslationsA, path, 'Message'); }, opts); - deepForOwn(translationsB, (value, key, path) => { - set(augmentedTranslationsB, path, 'Message'); + deepForOwn(translationsB, (valueB, key, path) => { + let newValue = 'Message'; + if (checkDuplicateValues) { + set(duplicateTranslations, path, newValue); + const valueA = get(translationsA, path); + if (valueA === valueB) { + newValue = valueB; + } + } + set(augmentedTranslationsB, path, newValue); }, opts); - return diff(augmentedTranslationsA, augmentedTranslationsB, DIFF_OPTIONS); + const diffString = diff(augmentedTranslationsA, augmentedTranslationsB, DIFF_OPTIONS); + if (checkDuplicateValues && noDifferenceRegex.test(diffString.trim())) { + return diff(augmentedTranslationsB, duplicateTranslations, DIFF_OPTIONS); + } + return diffString; }; module.exports = compareTranslationsStructure; From 28bc491c7ce1f7ebf3887cbd960261d96ba461b9 Mon Sep 17 00:00:00 2001 From: rafaelgssa Date: Sat, 18 Jul 2020 21:45:55 -0300 Subject: [PATCH 2/4] Add tests --- src/__snapshots__/identical-keys.test.js.snap | 18 +++++++ src/identical-keys.test.js | 48 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/__snapshots__/identical-keys.test.js.snap b/src/__snapshots__/identical-keys.test.js.snap index ab1961f..4521ad9 100644 --- a/src/__snapshots__/identical-keys.test.js.snap +++ b/src/__snapshots__/identical-keys.test.js.snap @@ -20,6 +20,24 @@ exports[`Snapshot Tests for Invalid Code map of comparison files - structure mis }" `; +exports[`Snapshot Tests for Invalid Code single comparison file - duplicate values 1`] = ` +" +- Expected ++ Received + +@@ -2,7 +2,7 @@ + \\"translationLevelOne\\": Object { +- \\"translationKeyA\\": \\"Message\\", ++ \\"translationKeyA\\": \\"value a\\", + \\"translationLevelTwo\\": Object { +- \\"translationKeyB\\": \\"Message\\", ++ \\"translationKeyB\\": \\"value b\\", + \\"translationsLevelThree\\": Object { +- \\"translationKeyC\\": \\"Message\\", ++ \\"translationKeyC\\": \\"value c\\", + }," +`; + exports[`Snapshot Tests for Invalid Code single comparison file - structure mismatch 1`] = ` " - Expected diff --git a/src/identical-keys.test.js b/src/identical-keys.test.js index 7c48a0d..73f830a 100644 --- a/src/identical-keys.test.js +++ b/src/identical-keys.test.js @@ -94,6 +94,29 @@ ruleTester.run('identical-keys', rule, { ], filename: 'file.json' }, + // single file path to compare with, while checking for duplicate values + { + code: ` + /*{ + "translationLevelOne": { + "translationKeyA": "translated value a", + "translationLevelTwo": { + "translationKeyB": "translated value b", + "translationsLevelThree": { + "translationKeyC": "translated value c" + } + } + } + }*//*path/to/file.json*/ + `, + options: [ + { + filePath: 'path/to/compare-file-a.json', + checkDuplicateValues: true + } + ], + filename: 'file.json' + }, // mapping to match which file we should use to compare structure { code: ` @@ -293,6 +316,31 @@ describe('Snapshot Tests for Invalid Code', () => { }); expect(strip(errors[0].message)).toMatchSnapshot(); }); + test('single comparison file - duplicate values', () => { + const errors = run({ + code: ` + /*{ + "translationLevelOne": { + "translationKeyA": "value a", + "translationLevelTwo": { + "translationKeyB": "value b", + "translationsLevelThree": { + "translationKeyC": "value c" + } + } + } + }*//*/path/to/invalid-file.json*/ + `, + options: [ + { + filePath: 'path/to/compare-file-a.json', + checkDuplicateValues: true + } + ], + filename: 'file.json' + }); + expect(strip(errors[0].message)).toMatchSnapshot(); + }); test('map of comparison files - structure mismatch', () => { const errors = run({ code: ` From 8b6fd850ea2b52bf3f1d03e61e87373716ec4d6a Mon Sep 17 00:00:00 2001 From: rafaelgssa Date: Sat, 18 Jul 2020 21:53:59 -0300 Subject: [PATCH 3/4] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7095a0b..8669528 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,8 @@ Check out the [Examples](examples/) folder to see different use cases. }; ``` + - `checkDuplicateValues`: Boolean (Optional). Default value: `false`. If true, the values will also be checked for duplicates, in comparison to the file specified in `filePath`. + Output from the slightly advanced [identical keys](/examples/multiple-keys-per-locale) example where some keys from the reference translation file (`en-US`) were not found during comparison. ![](assets/identical-keys-error-screenshot-2018-04-30.png) From 8e6410c3e5e27984f86ea8f71f31805a6bda74d4 Mon Sep 17 00:00:00 2001 From: rafaelgssa Date: Sun, 19 Jul 2020 00:34:47 -0300 Subject: [PATCH 4/4] Do not check source file --- src/identical-keys.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/identical-keys.js b/src/identical-keys.js index 9558895..f069937 100644 --- a/src/identical-keys.js +++ b/src/identical-keys.js @@ -165,11 +165,12 @@ const identicalKeys = (context, source, sourceFilePath) => { } const { checkDuplicateValues = false } = comparisonOptions; + const isSourceFile = sourceFilePath === comparisonOptions.filePath; const diffString = compareTranslationsStructure( settings, keyStructure, currentTranslations, - checkDuplicateValues + checkDuplicateValues && !isSourceFile ); if (noDifferenceRegex.test(diffString.trim())) {