(i18n) install jest and add tests

We install Jest to test our i18n package.
We tests:
  - the extraction of the translations on the Desk app fo crowdin
  - the formatings of the translations from crowdin to the app
  - we check that all the translations are present in the app
We connect the tests to the CI.
This commit is contained in:
Anthony LC
2024-01-24 15:19:49 +01:00
committed by Anthony LC
parent 3d0824e023
commit 801cb98e15
9 changed files with 223 additions and 12 deletions

View File

@@ -0,0 +1,104 @@
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
describe('integration testing on i18n package', () => {
afterAll(() => {
fs.rmSync('./locales/tests', { recursive: true, force: true });
});
test('cmd extract-translation:desk', () => {
// To be sure the file is not here
fs.rmSync('./locales/desk/translations-crowdin.json', {
recursive: true,
force: true,
});
expect(
fs.existsSync('./locales/desk/translations-crowdin.json'),
).toBeFalsy();
// Generate the file
execSync('yarn extract-translation:desk');
expect(
fs.existsSync('./locales/desk/translations-crowdin.json'),
).toBeTruthy();
});
test('cmd format-deploy', () => {
// To be sure the tests folder is not here
fs.rmSync('./locales/tests', { recursive: true, force: true });
expect(fs.existsSync('./locales/tests')).toBeFalsy();
// Generate english json file
fs.mkdirSync('./locales/tests/en/', { recursive: true });
fs.writeFileSync(
'./locales/tests/en/translations.json',
JSON.stringify({ test: { message: 'My test' } }),
'utf8',
);
expect(fs.existsSync('./locales/tests/en/translations.json')).toBeTruthy();
fs.mkdirSync('./locales/tests/fr/', { recursive: true });
fs.writeFileSync(
'./locales/tests/fr/translations.json',
JSON.stringify({ test: { message: 'Mon test' } }),
'utf8',
);
expect(fs.existsSync('./locales/tests/fr/translations.json')).toBeTruthy();
// Execute format-deploy command
const output = './locales/tests/translations.json';
execSync(`node ./format-deploy.mjs --app=tests --output=${output}`);
const json = JSON.parse(fs.readFileSync(output, 'utf8'));
expect(json).toEqual({
en: {
translation: { test: 'My test' },
},
fr: {
translation: { test: 'Mon test' },
},
});
});
test('cmd format-deploy throws an error when translation file is not found', () => {
// To be sure the tests folder is not here
fs.rmSync('./locales/tests', { recursive: true, force: true });
expect(fs.existsSync('./locales/tests')).toBeFalsy();
// Generate english json file
fs.mkdirSync('./locales/tests/en/', { recursive: true });
// Execute format-deploy command
const output = './locales/tests/translations.json';
const cmd = () => {
execSync(`node ./format-deploy.mjs --app=tests --output=${output}`, {
stdio: 'pipe',
});
};
expect(cmd).toThrow(
`Error: File locales${path.sep}tests${path.sep}en${path.sep}translations.json not found!`,
);
});
test('cmd format-deploy throws an error when no translation to deploy', () => {
// To be sure the tests folder is not here
fs.rmSync('./locales/tests', { recursive: true, force: true });
expect(fs.existsSync('./locales/tests')).toBeFalsy();
// Generate english json file
fs.mkdirSync('./locales/tests/', { recursive: true });
// Execute format-deploy command
const output = './locales/tests/translations.json';
const cmd = () => {
execSync(`node ./format-deploy.mjs --app=tests --output=${output}`, {
stdio: 'pipe',
});
};
expect(cmd).toThrow('Error: No translation to deploy');
});
});

View File

@@ -0,0 +1,28 @@
import { execSync } from 'child_process';
import fs from 'fs';
describe('checks all the frontend translation are made', () => {
it('checks missing translation. If this test fails, go to https://crowdin.com/', () => {
// Extract the translations
execSync(
'yarn extract-translation:desk -c ./i18next-parser.config.jest.mjs',
);
const outputCrowdin = './locales/desk/translations-crowdin.json';
const jsonCrowdin = JSON.parse(fs.readFileSync(outputCrowdin, 'utf8'));
const listKeysCrowdin = Object.keys(jsonCrowdin).sort();
// Check the translations in the app Desk
const outputDesk = '../../apps/desk/src/i18n/translations.json';
const jsonDesk = JSON.parse(fs.readFileSync(outputDesk, 'utf8'));
// Our keys are in english, so we don't need to check the english translation
Object.keys(jsonDesk)
.filter((key) => key !== 'en')
.forEach((key) => {
const listKeysDesk = Object.keys(jsonDesk[key].translation).sort();
expect(
listKeysCrowdin.every((element) => listKeysDesk.includes(element)),
).toBeTruthy();
});
});
});

View File

@@ -28,7 +28,7 @@ fs.readdirSync(folderPath).map((language) => {
const pathTranslateFile = path.join(languagePath, path.sep, namefile);
if (!fs.existsSync(pathTranslateFile)) {
return;
throw new Error(`File ${pathTranslateFile} not found!`);
}
const json = JSON.parse(fs.readFileSync(pathTranslateFile, "utf8"));
@@ -43,6 +43,10 @@ fs.readdirSync(folderPath).map((language) => {
};
});
if (!Object.keys(jsonI18n).length) {
throw new Error(`No translation to deploy`);
}
// Write the file to the output
fs.writeFileSync(output, JSON.stringify(jsonI18n), "utf8");

View File

@@ -0,0 +1,7 @@
export default {
rootDir: "./",
testEnvironment: "node",
transform: {
"^.+\\.(ts)$": "ts-jest",
},
};

View File

@@ -6,10 +6,16 @@
"extract-translation": "yarn extract-translation:desk",
"extract-translation:desk": "yarn i18next ../../apps/desk/**/*.{ts,tsx} -c ./i18next-parser.config.mjs -o ./locales/desk/translations-crowdin.json",
"format-deploy": "yarn format-deploy:desk",
"format-deploy:desk": "node ./format-deploy.mjs --app=desk --output=../../apps/desk/src/i18n/translations.json"
"format-deploy:desk": "node ./format-deploy.mjs --app=desk --output=../../apps/desk/src/i18n/translations.json",
"test": "jest"
},
"dependencies": {
"@types/jest": "29.5.11",
"@types/node": "20.11.6",
"i18next-parser": "8.8.0",
"jest": "29.7.0",
"ts-jest": "29.1.2",
"typescript": "*",
"yargs": "17.7.2"
}
}

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
},
"include": [
"**/*.ts",
],
"exclude": ["node_modules"]
}