fix(tests): add validation of comment translations (#41537)
This commit is contained in:
committed by
GitHub
parent
654d66186e
commit
0d3158d4f4
16
curriculum/package-lock.json
generated
16
curriculum/package-lock.json
generated
@ -38,10 +38,12 @@
|
||||
"puppeteer": "^8.0.0",
|
||||
"readdirp": "^3.5.0",
|
||||
"rehype": "^11.0.0",
|
||||
"rework-visit": "^1.0.0",
|
||||
"string-similarity": "^4.0.2",
|
||||
"unist-util-visit": "^2.0.3",
|
||||
"vfile": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@ -8090,12 +8092,6 @@
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/rework-visit": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz",
|
||||
"integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
@ -16669,12 +16665,6 @@
|
||||
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
|
||||
"dev": true
|
||||
},
|
||||
"rework-visit": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz",
|
||||
"integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
|
@ -55,7 +55,6 @@
|
||||
"puppeteer": "^8.0.0",
|
||||
"readdirp": "^3.5.0",
|
||||
"rehype": "^11.0.0",
|
||||
"rework-visit": "^1.0.0",
|
||||
"string-similarity": "^4.0.2",
|
||||
"unist-util-visit": "^2.0.3",
|
||||
"vfile": "^4.2.0"
|
||||
|
@ -27,6 +27,8 @@ const schema = Joi.object()
|
||||
challengeOrder: Joi.number(),
|
||||
challengeType: Joi.number().min(0).max(11).required(),
|
||||
checksum: Joi.number(),
|
||||
// __commentCounts is only used to test the comment replacement
|
||||
__commentCounts: Joi.object(),
|
||||
// TODO: require this only for normal challenges, not certs
|
||||
dashedName: Joi.string().regex(slugRE),
|
||||
description: Joi.when('challengeType', {
|
||||
|
@ -24,7 +24,7 @@ const {
|
||||
|
||||
const { assert, AssertionError } = require('chai');
|
||||
const Mocha = require('mocha');
|
||||
const { flatten, isEmpty, cloneDeep } = require('lodash');
|
||||
const { flatten, isEmpty, cloneDeep, isEqual } = require('lodash');
|
||||
const { getLines } = require('../../utils/get-lines');
|
||||
|
||||
const jsdom = require('jsdom');
|
||||
@ -62,12 +62,14 @@ const TRANSLATABLE_COMMENTS = getTranslatableComments(
|
||||
// the config files are created during the build, but not before linting
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
const testEvaluator = require('../../config/client/test-evaluator').filename;
|
||||
const { inspect } = require('util');
|
||||
|
||||
const commentExtractors = {
|
||||
html: require('./utils/extract-html-comments'),
|
||||
js: require('./utils/extract-js-comments'),
|
||||
jsx: require('./utils/extract-jsx-comments'),
|
||||
css: require('./utils/extract-css-comments')
|
||||
css: require('./utils/extract-css-comments'),
|
||||
scriptJs: require('./utils/extract-script-js-comments')
|
||||
};
|
||||
|
||||
// rethrow unhandled rejections to make sure the tests exit with -1
|
||||
@ -329,7 +331,7 @@ function populateTestsForLang({ lang, challenges, meta }) {
|
||||
|
||||
// We get all the actual comments using the appropriate parsers
|
||||
if (file.ext === 'html') {
|
||||
const commentTypes = ['css', 'html'];
|
||||
const commentTypes = ['css', 'html', 'scriptJs'];
|
||||
for (let type of commentTypes) {
|
||||
const newComments = commentExtractors[type](file.contents);
|
||||
for (const [key, value] of Object.entries(newComments)) {
|
||||
@ -342,17 +344,22 @@ function populateTestsForLang({ lang, challenges, meta }) {
|
||||
comments = commentExtractors[file.ext](file.contents);
|
||||
}
|
||||
|
||||
// Then we compare the number of times a given comment appears
|
||||
// (count) with the number of times the text within it appears
|
||||
// (commentTextCount)
|
||||
for (const [comment, count] of Object.entries(comments)) {
|
||||
const commentTextCount =
|
||||
file.contents.split(comment).length - 1;
|
||||
if (commentTextCount !== count)
|
||||
throw Error(
|
||||
`Translated comment text, ${comment}, should only appear inside comments`
|
||||
);
|
||||
}
|
||||
// Then we compare the number of times each comment appears in the
|
||||
// translated text (commentMap) with the number of replacements
|
||||
// made during translation (challenge.__commentCounts). If they
|
||||
// differ, the translation must have gone wrong
|
||||
|
||||
const commentMap = new Map(Object.entries(comments));
|
||||
|
||||
if (isEmpty(challenge.__commentCounts) && isEmpty(commentMap))
|
||||
return;
|
||||
|
||||
if (!isEqual(commentMap, challenge.__commentCounts))
|
||||
throw Error(`Mismatch in ${challenge.title}. Replaced comments:
|
||||
${inspect(challenge.__commentCounts)}
|
||||
Comments in translated text:
|
||||
${inspect(commentMap)}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -19,6 +19,39 @@ Some text
|
||||
</body>
|
||||
`;
|
||||
|
||||
const outsideDeclarations = `
|
||||
<style>
|
||||
.body {
|
||||
color: red;
|
||||
/* comment 1 */
|
||||
}
|
||||
|
||||
|
||||
/* comment 1 */
|
||||
/* comment 2 */
|
||||
}
|
||||
|
||||
</style>
|
||||
`;
|
||||
|
||||
const mediaQuery = `
|
||||
<style>
|
||||
.body {
|
||||
color: red;
|
||||
/* comment 1 */
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 350px) {
|
||||
:root {
|
||||
/* comment 2 */
|
||||
|
||||
/* comment 2 */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
// NOTE: this is a bit finicky. If the css is, say, missing a semi-colon,
|
||||
// nearby comments may be missed.
|
||||
describe('extractCSSComments', () => {
|
||||
@ -29,4 +62,18 @@ describe('extractCSSComments', () => {
|
||||
};
|
||||
expect(extractCSSComments(someHTMLWithCSS)).toEqual(commentCounts);
|
||||
});
|
||||
it('should catch comments outside of declarations', () => {
|
||||
const commentCounts = {
|
||||
'comment 1': 2,
|
||||
'comment 2': 1
|
||||
};
|
||||
expect(extractCSSComments(outsideDeclarations)).toEqual(commentCounts);
|
||||
});
|
||||
it('should catch comments inside of media queries', () => {
|
||||
const commentCounts = {
|
||||
'comment 1': 1,
|
||||
'comment 2': 2
|
||||
};
|
||||
expect(extractCSSComments(mediaQuery)).toEqual(commentCounts);
|
||||
});
|
||||
});
|
||||
|
12
curriculum/test/utils/extract-script-js-comments.js
Normal file
12
curriculum/test/utils/extract-script-js-comments.js
Normal file
@ -0,0 +1,12 @@
|
||||
var rehype = require('rehype');
|
||||
var vfile = require('vfile');
|
||||
|
||||
var getComments = require('./plugins/get-script-js-comments');
|
||||
|
||||
const processor = rehype().use(getComments);
|
||||
|
||||
function extractComments(html) {
|
||||
return processor.processSync(vfile(html)).data;
|
||||
}
|
||||
|
||||
module.exports = extractComments;
|
77
curriculum/test/utils/extract-script-js-comments.test.js
Normal file
77
curriculum/test/utils/extract-script-js-comments.test.js
Normal file
@ -0,0 +1,77 @@
|
||||
/* global expect */
|
||||
const extractScriptJSComments = require('./extract-script-js-comments');
|
||||
|
||||
const inlineComments = `<body>
|
||||
Some text
|
||||
<script>
|
||||
|
||||
// comment 1
|
||||
|
||||
var x = 'y';
|
||||
|
||||
// comment 2
|
||||
|
||||
// comment 1
|
||||
|
||||
</script>
|
||||
</body>
|
||||
`;
|
||||
|
||||
const multilineComments = `<body>
|
||||
Some text
|
||||
<script>
|
||||
|
||||
/*
|
||||
comment 1
|
||||
*/
|
||||
var x = 'y';
|
||||
|
||||
/* comment 2 */
|
||||
|
||||
/* comment 1 */
|
||||
|
||||
</script>
|
||||
</body>
|
||||
`;
|
||||
|
||||
const outsideScript = `<body>
|
||||
Some text
|
||||
<script>
|
||||
|
||||
// comment 1
|
||||
|
||||
var x = 'y';
|
||||
|
||||
// comment 2
|
||||
|
||||
// comment 1
|
||||
|
||||
</script>
|
||||
|
||||
// comment 2
|
||||
</body>
|
||||
`;
|
||||
|
||||
describe('extractScriptJSComments', () => {
|
||||
it('should catch inline comments', () => {
|
||||
const commentCounts = {
|
||||
'comment 1': 2,
|
||||
'comment 2': 1
|
||||
};
|
||||
expect(extractScriptJSComments(inlineComments)).toEqual(commentCounts);
|
||||
});
|
||||
it('should catch multiline comments', () => {
|
||||
const commentCounts = {
|
||||
'comment 1': 2,
|
||||
'comment 2': 1
|
||||
};
|
||||
expect(extractScriptJSComments(multilineComments)).toEqual(commentCounts);
|
||||
});
|
||||
it('should ignore comments outside script tags', () => {
|
||||
const commentCounts = {
|
||||
'comment 1': 2,
|
||||
'comment 2': 1
|
||||
};
|
||||
expect(extractScriptJSComments(outsideScript)).toEqual(commentCounts);
|
||||
});
|
||||
});
|
@ -1,9 +1,28 @@
|
||||
const { isEmpty } = require('lodash');
|
||||
const visit = require('unist-util-visit');
|
||||
var css = require('css');
|
||||
var visitCss = require('rework-visit');
|
||||
const { commentToData } = require('../comment-to-data');
|
||||
|
||||
function visitComments(node, cb) {
|
||||
node.rules.forEach(rule => {
|
||||
if (rule.type === 'rule') {
|
||||
visitDeclarations(rule.declarations, cb);
|
||||
} else if (rule.type === 'comment') {
|
||||
cb(rule.comment);
|
||||
} else if (rule.type === 'media') {
|
||||
visitComments(rule, cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function visitDeclarations(declarations, cb) {
|
||||
declarations.forEach(dec => {
|
||||
if (dec.type === 'comment') {
|
||||
cb(dec.comment);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function plugin() {
|
||||
return transformer;
|
||||
|
||||
@ -16,12 +35,9 @@ function plugin() {
|
||||
}
|
||||
function cssVisitor(node) {
|
||||
const ast = css.parse(node.value);
|
||||
visitCss(ast.stylesheet, dec => {
|
||||
let comments = dec
|
||||
.filter(({ type }) => type === 'comment')
|
||||
.map(({ comment }) => comment.trim());
|
||||
comments.forEach(comment => commentToData(file, comment));
|
||||
});
|
||||
visitComments(ast.stylesheet, comment =>
|
||||
commentToData(file, comment.trim())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
curriculum/test/utils/plugins/get-script-js-comments.js
Normal file
29
curriculum/test/utils/plugins/get-script-js-comments.js
Normal file
@ -0,0 +1,29 @@
|
||||
const { isEmpty } = require('lodash');
|
||||
const visit = require('unist-util-visit');
|
||||
const acorn = require('acorn');
|
||||
const { commentToData } = require('../comment-to-data');
|
||||
|
||||
const parser = acorn.Parser;
|
||||
|
||||
function plugin() {
|
||||
return transformer;
|
||||
|
||||
function transformer(tree, file) {
|
||||
if (isEmpty(file.data)) file.data = {};
|
||||
visit(tree, { type: 'element', tagName: 'script' }, scriptVisitor);
|
||||
|
||||
function scriptVisitor(node) {
|
||||
visit(node, 'text', jsVisitor);
|
||||
}
|
||||
function jsVisitor(node) {
|
||||
let comments = [];
|
||||
parser.parse(node.value, { onComment: comments, ecmaVersion: 2020 });
|
||||
|
||||
comments
|
||||
.map(({ value }) => value.trim())
|
||||
.forEach(comment => commentToData(file, comment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = plugin;
|
@ -1,92 +1,3 @@
|
||||
const ENGLISH_CERTIFICATE = {
|
||||
id: '561add10cb82ac38a17513bc',
|
||||
title: 'Responsive Web Design Certificate',
|
||||
challengeType: 7,
|
||||
isPrivate: true,
|
||||
tests: [
|
||||
{ id: 'bd7158d8c442eddfaeb5bd18', title: 'Build a Tribute Page' },
|
||||
{ id: '587d78af367417b2b2512b03', title: 'Build a Survey Form' },
|
||||
{
|
||||
id: '587d78af367417b2b2512b04',
|
||||
title: 'Build a Product Landing Page'
|
||||
},
|
||||
{
|
||||
id: '587d78b0367417b2b2512b05',
|
||||
title: 'Build a Technical Documentation Page'
|
||||
},
|
||||
{
|
||||
id: 'bd7158d8c242eddfaeb5bd13',
|
||||
title: 'Build a Personal Portfolio Webpage'
|
||||
}
|
||||
],
|
||||
solutions: ['// solution required\n'],
|
||||
description: '',
|
||||
instructions: '',
|
||||
files: []
|
||||
};
|
||||
|
||||
const ENGLISH_CHALLENGE = {
|
||||
id: 'id',
|
||||
title: 'Title',
|
||||
challengeType: 0,
|
||||
videoUrl: 'https://scrimba.com/',
|
||||
forumTopicId: 12345,
|
||||
tests: [
|
||||
{
|
||||
text: 'Test text',
|
||||
testString: 'assertions'
|
||||
},
|
||||
{
|
||||
text: 'Test text2',
|
||||
testString: 'assertions2'
|
||||
}
|
||||
],
|
||||
solutions: ['solution html string'],
|
||||
description: 'description html string',
|
||||
instructions: 'instructions html string',
|
||||
files: [
|
||||
{
|
||||
key: 'indexhtml',
|
||||
ext: 'html',
|
||||
name: 'index',
|
||||
contents: 'seed html string',
|
||||
head: 'head string',
|
||||
tail: 'tail string'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const ENGLISH_CHALLENGE_TWO_SOLUTIONS = {
|
||||
id: 'id',
|
||||
title: 'Title',
|
||||
challengeType: 0,
|
||||
videoUrl: 'https://scrimba.com/',
|
||||
forumTopicId: 12345,
|
||||
tests: [
|
||||
{
|
||||
text: 'Test text',
|
||||
testString: 'assertions'
|
||||
},
|
||||
{
|
||||
text: 'Test text2',
|
||||
testString: 'assertions2'
|
||||
}
|
||||
],
|
||||
solutions: ['solution html string', 'second solution html string'],
|
||||
description: 'description html string',
|
||||
instructions: 'instructions html string',
|
||||
files: [
|
||||
{
|
||||
key: 'indexhtml',
|
||||
ext: 'html',
|
||||
name: 'index',
|
||||
contents: 'seed html string',
|
||||
head: '',
|
||||
tail: ''
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const ENGLISH_CHALLENGE_NO_FILES = {
|
||||
id: 'id',
|
||||
title: 'Title',
|
||||
@ -109,151 +20,4 @@ const ENGLISH_CHALLENGE_NO_FILES = {
|
||||
files: []
|
||||
};
|
||||
|
||||
const ENGLISH_VIDEO_CHALLENGE = {
|
||||
id: 'id',
|
||||
title: 'Title',
|
||||
challengeType: 0,
|
||||
videoId: 'abc123',
|
||||
forumTopicId: 12345,
|
||||
question: 'english question',
|
||||
description: 'description html string',
|
||||
instructions: 'instructions html string'
|
||||
};
|
||||
|
||||
const TRANSLATED_CERTIFICATE = {
|
||||
id: '561add10cb82ac38a17513bc',
|
||||
title: '响应式网页设计证书',
|
||||
challengeType: 7,
|
||||
isPrivate: true,
|
||||
videoUrl: '',
|
||||
tests: [
|
||||
{ id: 'bd7158d8c442eddfaeb5bd18', title: 'Build a Tribute Page' },
|
||||
{ id: '587d78af367417b2b2512b03', title: 'Build a Survey Form' },
|
||||
{
|
||||
id: '587d78af367417b2b2512b04',
|
||||
title: 'Build a Product Landing Page'
|
||||
},
|
||||
{
|
||||
id: '587d78b0367417b2b2512b05',
|
||||
title: 'Build a Technical Documentation Page'
|
||||
},
|
||||
{
|
||||
id: 'bd7158d8c242eddfaeb5bd13',
|
||||
title: 'Build a Personal Portfolio Webpage'
|
||||
}
|
||||
],
|
||||
solutions: ['// solution required\n'],
|
||||
description: '',
|
||||
instructions: '',
|
||||
files: []
|
||||
};
|
||||
|
||||
const TRANSLATED_CHALLENGE = {
|
||||
id: 'id',
|
||||
title: 'Translated title',
|
||||
challengeType: 0,
|
||||
videoUrl: 'https://scrimba.com/',
|
||||
forumTopicId: 9876,
|
||||
tests: [
|
||||
{
|
||||
text: 'Translated test text',
|
||||
testString: 'Translated assertions, should be ignored'
|
||||
},
|
||||
{
|
||||
text: 'Translated test text2',
|
||||
testString: 'Translated assertions, should be ignored2'
|
||||
}
|
||||
],
|
||||
solutions: ['Translated solution html string, should be ignored'],
|
||||
description: 'Translated description html string',
|
||||
instructions: 'Translated instructions html string',
|
||||
files: [
|
||||
{
|
||||
key: 'indexhtml',
|
||||
ext: 'html',
|
||||
name: 'index',
|
||||
contents: 'Translated seed html string, should be ignored',
|
||||
head: 'Translated head string, should be ignored',
|
||||
tail: 'Translated tail string, should be ignored'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const TRANSLATED_CHALLENGE_NO_TITLE = {
|
||||
id: 'id',
|
||||
challengeType: 0,
|
||||
videoUrl: 'https://scrimba.com/',
|
||||
forumTopicId: 9876,
|
||||
tests: [
|
||||
{
|
||||
text: 'Translated test text',
|
||||
testString: 'Translated assertions, should be ignored'
|
||||
},
|
||||
{
|
||||
text: 'Translated test text2',
|
||||
testString: 'Translated assertions, should be ignored2'
|
||||
}
|
||||
],
|
||||
solutions: ['Translated solution html string, should be ignored'],
|
||||
description: 'Translated description html string',
|
||||
instructions: 'Translated instructions html string',
|
||||
files: [
|
||||
{
|
||||
key: 'indexhtml',
|
||||
ext: 'html',
|
||||
name: 'index',
|
||||
contents: 'Translated seed html string, should be ignored',
|
||||
head: 'Translated head string, should be ignored',
|
||||
tail: 'Translated tail string, should be ignored'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const TRANSLATED_VIDEO_CHALLENGE = {
|
||||
id: 'id',
|
||||
title: 'Title',
|
||||
challengeType: 0,
|
||||
videoId: 'abc123',
|
||||
forumTopicId: 12345,
|
||||
question: 'translated question',
|
||||
description: 'translated description html string',
|
||||
instructions: 'translated instructions html string'
|
||||
};
|
||||
|
||||
const WRONG_NUM_TESTS_CHALLENGE = {
|
||||
id: 'id',
|
||||
title: 'Translated title',
|
||||
challengeType: 0,
|
||||
videoUrl: 'https://scrimba.com/',
|
||||
forumTopicId: 12345,
|
||||
tests: [
|
||||
{
|
||||
text: 'Translated test text',
|
||||
testString: 'Translated assertions, should be ignored'
|
||||
}
|
||||
],
|
||||
solutions: ['Translated solution html string, should be ignored'],
|
||||
description: 'Translated description html string',
|
||||
instructions: 'Translated instructions html string',
|
||||
files: [
|
||||
{
|
||||
key: 'indexhtml',
|
||||
ext: 'html',
|
||||
name: 'index',
|
||||
contents: 'Translated seed html string, should be ignored',
|
||||
head: '',
|
||||
tail: ''
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
exports.ENGLISH_CERTIFICATE = ENGLISH_CERTIFICATE;
|
||||
exports.ENGLISH_CHALLENGE = ENGLISH_CHALLENGE;
|
||||
exports.ENGLISH_CHALLENGE_TWO_SOLUTIONS = ENGLISH_CHALLENGE_TWO_SOLUTIONS;
|
||||
exports.ENGLISH_CHALLENGE_NO_FILES = ENGLISH_CHALLENGE_NO_FILES;
|
||||
exports.ENGLISH_VIDEO_CHALLENGE = ENGLISH_VIDEO_CHALLENGE;
|
||||
exports.TRANSLATED_CERTIFICATE = TRANSLATED_CERTIFICATE;
|
||||
exports.TRANSLATED_CHALLENGE = TRANSLATED_CHALLENGE;
|
||||
exports.TRANSLATED_CHALLENGE_NO_TITLE = TRANSLATED_CHALLENGE_NO_TITLE;
|
||||
exports.TRANSLATED_VIDEO_CHALLENGE = TRANSLATED_VIDEO_CHALLENGE;
|
||||
exports.WRONG_NUM_TESTS_CHALLENGE = WRONG_NUM_TESTS_CHALLENGE;
|
||||
|
@ -1,16 +1,17 @@
|
||||
const { isEmpty, cloneDeep } = require('lodash');
|
||||
const { cloneDeep } = require('lodash');
|
||||
|
||||
exports.translateComments = (text, lang, dict, codeLang) => {
|
||||
const knownComments = Object.keys(dict);
|
||||
const config = { knownComments, dict, lang };
|
||||
const input = { text, commentCounts: new Map() };
|
||||
switch (codeLang) {
|
||||
case 'js':
|
||||
case 'jsx':
|
||||
return transMultiline(transInline(text, config), config);
|
||||
return transMultiline(transInline(input, config), config);
|
||||
case 'html':
|
||||
return transScript(transHTML(transCSS(text, config), config), config);
|
||||
return transScript(transHTML(transCSS(input, config), config), config);
|
||||
default:
|
||||
return text;
|
||||
return input;
|
||||
}
|
||||
};
|
||||
|
||||
@ -21,125 +22,91 @@ exports.translateCommentsInChallenge = (challenge, lang, dict) => {
|
||||
} else {
|
||||
Object.keys(challClone.files).forEach(key => {
|
||||
if (challClone.files[key].contents) {
|
||||
challClone.files[key].contents = this.translateComments(
|
||||
let { text, commentCounts } = this.translateComments(
|
||||
challenge.files[key].contents,
|
||||
lang,
|
||||
dict,
|
||||
challClone.files[key].ext
|
||||
);
|
||||
challClone.__commentCounts = commentCounts;
|
||||
challClone.files[key].contents = text;
|
||||
}
|
||||
});
|
||||
}
|
||||
return challClone;
|
||||
};
|
||||
|
||||
exports.mergeChallenges = (engChal, transChal) => {
|
||||
const hasTests =
|
||||
(engChal.tests && transChal.tests) ||
|
||||
(engChal.question && transChal.question);
|
||||
const challenge = {
|
||||
...engChal,
|
||||
description: transChal.description,
|
||||
instructions: transChal.instructions,
|
||||
originalTitle: engChal.title,
|
||||
// TODO: throw in production?
|
||||
title: isEmpty(transChal.title) ? engChal.title : transChal.title,
|
||||
forumTopicId: transChal.forumTopicId
|
||||
};
|
||||
if (!hasTests)
|
||||
throw Error(
|
||||
`Both challenges must have tests or questions.
|
||||
title: ${engChal.title}
|
||||
translated title: ${transChal.title}`
|
||||
);
|
||||
// TODO: this should break the build when we go to production, but
|
||||
// not for testing.
|
||||
if (transChal.tests && transChal.tests.length !== engChal.tests.length) {
|
||||
console.error(
|
||||
`Challenges in both languages must have the same number of tests.
|
||||
title: ${engChal.title}
|
||||
translated title: ${transChal.title}`
|
||||
);
|
||||
return challenge;
|
||||
}
|
||||
|
||||
// throw Error(
|
||||
// `Challenges in both languages must have the same number of tests.
|
||||
// title: ${engChal.title}
|
||||
// translated title: ${transChal.title}`
|
||||
// );
|
||||
|
||||
if (transChal.tests) {
|
||||
const translatedTests =
|
||||
engChal.challengeType === 7
|
||||
? transChal.tests.map(({ title }, i) => ({
|
||||
title,
|
||||
id: engChal.tests[i].id
|
||||
}))
|
||||
: transChal.tests.map(({ text }, i) => ({
|
||||
text,
|
||||
testString: engChal.tests[i].testString
|
||||
}));
|
||||
challenge.tests = translatedTests;
|
||||
} else {
|
||||
challenge.question = transChal.question;
|
||||
}
|
||||
|
||||
// certificates do not have forumTopicIds
|
||||
if (challenge.challengeType === 7) delete challenge.forumTopicId;
|
||||
return challenge;
|
||||
};
|
||||
|
||||
// bare urls could be interpreted as comments, so we have to lookbehind for
|
||||
// http:// or https://
|
||||
function transInline(text, config) {
|
||||
return translateGeneric(text, config, '((?<!https?:)//\\s*)', '(\\s*$)');
|
||||
function transInline(input, config) {
|
||||
return translateGeneric(input, config, '((?<!https?:)//\\s*)', '(\\s*$)');
|
||||
}
|
||||
|
||||
function transMultiline(text, config) {
|
||||
return translateGeneric(text, config, '(/\\*\\s*)', '(\\s*\\*/)');
|
||||
function transMultiline(input, config) {
|
||||
return translateGeneric(input, config, '(/\\*\\s*)', '(\\s*\\*/)');
|
||||
}
|
||||
|
||||
// CSS has to be handled separately since it is looking for comments inside tags
|
||||
function transCSS(text, config) {
|
||||
function transCSS({ text, commentCounts }, config) {
|
||||
const regex = /<style>.*?<\/style>/gms;
|
||||
const matches = text.matchAll(regex);
|
||||
|
||||
for (const [match] of matches) {
|
||||
text = text.replace(match, transMultiline(match, config));
|
||||
let { text: styleText } = transMultiline(
|
||||
{ text: match, commentCounts },
|
||||
config
|
||||
);
|
||||
text = text.replace(match, styleText);
|
||||
}
|
||||
return text;
|
||||
return { text, commentCounts };
|
||||
}
|
||||
|
||||
function transScript(text, config) {
|
||||
function transScript({ text, commentCounts }, config) {
|
||||
const regex = /<script>.*?<\/script>/gms;
|
||||
const matches = text.matchAll(regex);
|
||||
|
||||
for (const [match] of matches) {
|
||||
text = text.replace(
|
||||
match,
|
||||
transMultiline(transInline(match, config), config)
|
||||
let { text: scriptText } = transMultiline(
|
||||
transInline({ text: match, commentCounts }, config),
|
||||
config
|
||||
);
|
||||
text = text.replace(match, scriptText);
|
||||
}
|
||||
return text;
|
||||
return { text, commentCounts };
|
||||
}
|
||||
|
||||
function transHTML(text, config) {
|
||||
return translateGeneric(text, config, '(<!--\\s*)', '(\\s*-->)');
|
||||
function transHTML(input, config) {
|
||||
return translateGeneric(input, config, '(<!--\\s*)', '(\\s*-->)');
|
||||
}
|
||||
|
||||
function translateGeneric(text, config, regexBefore, regexAfter) {
|
||||
function updateCounts(map, key) {
|
||||
if (map.has(key)) {
|
||||
map.set(key, map.get(key) + 1);
|
||||
} else {
|
||||
map.set(key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function translateGeneric(
|
||||
{ text, commentCounts },
|
||||
config,
|
||||
regexBefore,
|
||||
regexAfter
|
||||
) {
|
||||
const { knownComments, dict, lang } = config;
|
||||
const regex = new RegExp(regexBefore + '(.*?)' + regexAfter, 'gms');
|
||||
const matches = text.matchAll(regex);
|
||||
|
||||
for (const [match, before, comment, after] of matches) {
|
||||
if (knownComments.includes(comment)) {
|
||||
updateCounts(commentCounts, dict[comment][lang]);
|
||||
text = text.replace(match, `${before}${dict[comment][lang]}${after}`);
|
||||
} else if (comment.trim()) {
|
||||
throw `${comment} does not appear in the comment dictionary`;
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
return { text, commentCounts };
|
||||
}
|
||||
|
||||
exports.translateGeneric = translateGeneric;
|
||||
|
@ -1,48 +1,14 @@
|
||||
/* global expect jest */
|
||||
const {
|
||||
mergeChallenges,
|
||||
translateComments,
|
||||
translateCommentsInChallenge
|
||||
translateCommentsInChallenge,
|
||||
translateGeneric
|
||||
} = require('.');
|
||||
const {
|
||||
ENGLISH_CERTIFICATE,
|
||||
ENGLISH_CHALLENGE,
|
||||
ENGLISH_CHALLENGE_NO_FILES,
|
||||
ENGLISH_CHALLENGE_TWO_SOLUTIONS,
|
||||
ENGLISH_VIDEO_CHALLENGE,
|
||||
TRANSLATED_CERTIFICATE,
|
||||
TRANSLATED_CHALLENGE,
|
||||
TRANSLATED_CHALLENGE_NO_TITLE,
|
||||
TRANSLATED_VIDEO_CHALLENGE
|
||||
// WRONG_NUM_TESTS_CHALLENGE
|
||||
ENGLISH_CHALLENGE_NO_FILES
|
||||
} = require('./__fixtures__/challenge-objects');
|
||||
const { SIMPLE_TRANSLATION } = require('./__mocks__/mock-comments');
|
||||
|
||||
const COMBINED_CHALLENGE = mergeChallenges(
|
||||
ENGLISH_CHALLENGE,
|
||||
TRANSLATED_CHALLENGE
|
||||
);
|
||||
|
||||
const COMBINED_CHALLENGE_NO_TITLE = mergeChallenges(
|
||||
ENGLISH_CHALLENGE,
|
||||
TRANSLATED_CHALLENGE_NO_TITLE
|
||||
);
|
||||
|
||||
const COMBINED_CHALLENGE_TWO_SOLUTIONS = mergeChallenges(
|
||||
ENGLISH_CHALLENGE_TWO_SOLUTIONS,
|
||||
TRANSLATED_CHALLENGE
|
||||
);
|
||||
|
||||
const COMBINED_CERTIFICATE = mergeChallenges(
|
||||
ENGLISH_CERTIFICATE,
|
||||
TRANSLATED_CERTIFICATE
|
||||
);
|
||||
|
||||
const COMBINED_VIDEO_CHALLENGE = mergeChallenges(
|
||||
ENGLISH_VIDEO_CHALLENGE,
|
||||
TRANSLATED_VIDEO_CHALLENGE
|
||||
);
|
||||
|
||||
let logSpy;
|
||||
|
||||
describe('translation parser', () => {
|
||||
@ -52,111 +18,51 @@ describe('translation parser', () => {
|
||||
afterEach(() => {
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
describe('mergeChallenges', () => {
|
||||
it('takes the description from the second challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.description).toBe(
|
||||
TRANSLATED_CHALLENGE.description
|
||||
);
|
||||
});
|
||||
it('takes the head and tail from the first challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.files[0].head).toBe(
|
||||
ENGLISH_CHALLENGE.files[0].head
|
||||
);
|
||||
expect(COMBINED_CHALLENGE.files[0].tail).toBe(
|
||||
ENGLISH_CHALLENGE.files[0].tail
|
||||
);
|
||||
});
|
||||
it('takes the instructions from the second challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.instructions).toBe(
|
||||
TRANSLATED_CHALLENGE.instructions
|
||||
);
|
||||
});
|
||||
it('takes the seed from the first challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.files[0].contents).toBe(
|
||||
ENGLISH_CHALLENGE.files[0].contents
|
||||
);
|
||||
});
|
||||
it('takes the solution from the first challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.solutions[0]).toBe(
|
||||
ENGLISH_CHALLENGE.solutions[0]
|
||||
);
|
||||
});
|
||||
it('takes multiple solutions', () => {
|
||||
expect(COMBINED_CHALLENGE_TWO_SOLUTIONS.solutions).toEqual(
|
||||
ENGLISH_CHALLENGE_TWO_SOLUTIONS.solutions
|
||||
);
|
||||
});
|
||||
it('takes the testStrings from the first challenge', () => {
|
||||
const actualStrings = COMBINED_CHALLENGE.tests.map(
|
||||
({ testString }) => testString
|
||||
);
|
||||
const expectedStrings = ENGLISH_CHALLENGE.tests.map(
|
||||
({ testString }) => testString
|
||||
);
|
||||
for (let i = 0; i < actualStrings.length; i++) {
|
||||
expect(actualStrings[i]).toBe(expectedStrings[i]);
|
||||
}
|
||||
});
|
||||
it('takes the test text from the second challenge', () => {
|
||||
const actualStrings = COMBINED_CHALLENGE.tests.map(({ text }) => text);
|
||||
const expectedStrings = TRANSLATED_CHALLENGE.tests.map(
|
||||
({ text }) => text
|
||||
);
|
||||
for (let i = 0; i < actualStrings.length; i++) {
|
||||
expect(actualStrings[i]).toBe(expectedStrings[i]);
|
||||
}
|
||||
});
|
||||
it('takes the title from the second challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.title).toBe(TRANSLATED_CHALLENGE.title);
|
||||
});
|
||||
|
||||
// TODO: throw in production?
|
||||
it("takes the first challenge's title if the second is missing", () => {
|
||||
expect(COMBINED_CHALLENGE_NO_TITLE.title).toBe(ENGLISH_CHALLENGE.title);
|
||||
});
|
||||
|
||||
// 'originalTitle' is just used to create the dashedName (which must be
|
||||
// the same in both challenges, but only gets added after parsing)
|
||||
it('creates originalTitle from the first challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.originalTitle).toBe(ENGLISH_CHALLENGE.title);
|
||||
});
|
||||
// TODO: reinstate this after alpha testing.
|
||||
// it('throws an error if the numbers of tests do not match', () => {
|
||||
// expect(() =>
|
||||
// mergeChallenges(ENGLISH_CHALLENGE, WRONG_NUM_TESTS_CHALLENGE)
|
||||
// ).toThrow();
|
||||
// });
|
||||
it('takes the forum id from the second challenge', () => {
|
||||
expect(COMBINED_CHALLENGE.forumTopicId).toBe(
|
||||
TRANSLATED_CHALLENGE.forumTopicId
|
||||
describe('translateGeneric', () => {
|
||||
it('returns an object containing translated text', () => {
|
||||
expect.assertions(1);
|
||||
const seed = `// Add your code below this line
|
||||
Add your code above this line `;
|
||||
const transSeed = `// (Chinese) Add your code below this line (Chinese)
|
||||
Add your code above this line `;
|
||||
const knownComments = Object.keys(SIMPLE_TRANSLATION);
|
||||
const config = {
|
||||
knownComments,
|
||||
dict: SIMPLE_TRANSLATION,
|
||||
lang: 'chinese'
|
||||
};
|
||||
const actual = translateGeneric(
|
||||
{ text: seed, commentCounts: new Map() },
|
||||
config,
|
||||
'((?<!https?:)//\\s*)',
|
||||
'(\\s*$)'
|
||||
);
|
||||
expect(actual.text).toBe(transSeed);
|
||||
});
|
||||
it('takes the ids from the first certificate', () => {
|
||||
const actualIds = COMBINED_CERTIFICATE.tests.map(({ id }) => id);
|
||||
const expectedIds = ENGLISH_CERTIFICATE.tests.map(({ id }) => id);
|
||||
for (let i = 0; i < actualIds.length; i++) {
|
||||
expect(actualIds[i]).toBe(expectedIds[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('takes the titles from the second certificate', () => {
|
||||
const actualTitles = COMBINED_CERTIFICATE.tests.map(({ title }) => title);
|
||||
const expectedTitles = TRANSLATED_CERTIFICATE.tests.map(
|
||||
({ title }) => title
|
||||
);
|
||||
for (let i = 0; i < actualTitles.length; i++) {
|
||||
expect(actualTitles[i]).toBe(expectedTitles[i]);
|
||||
}
|
||||
});
|
||||
it('certificates do not have a forumTopicId property', () => {
|
||||
expect(Object.keys(COMBINED_CERTIFICATE).includes('forumTopicId')).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
it('takes the question from the second challenge', () => {
|
||||
expect(COMBINED_VIDEO_CHALLENGE.question).toBe(
|
||||
TRANSLATED_VIDEO_CHALLENGE.question
|
||||
it('returns an object containing a count of the replaced comments', () => {
|
||||
expect.assertions(1);
|
||||
const seed = `// Add your code below this line
|
||||
// Add your code above this line
|
||||
// Add your code below this line
|
||||
`;
|
||||
const expectedCommentCounts = new Map();
|
||||
expectedCommentCounts
|
||||
.set('(Chinese) Add your code below this line (Chinese)', 2)
|
||||
.set('(Chinese) Add your code above this line (Chinese)', 1);
|
||||
const knownComments = Object.keys(SIMPLE_TRANSLATION);
|
||||
const config = {
|
||||
knownComments,
|
||||
dict: SIMPLE_TRANSLATION,
|
||||
lang: 'chinese'
|
||||
};
|
||||
const actual = translateGeneric(
|
||||
{ text: seed, commentCounts: new Map() },
|
||||
config,
|
||||
'((?<!https?:)//\\s*)',
|
||||
'(\\s*$)'
|
||||
);
|
||||
expect(actual.commentCounts).toEqual(expectedCommentCounts);
|
||||
});
|
||||
});
|
||||
|
||||
@ -177,21 +83,21 @@ describe('translation parser', () => {
|
||||
Add your code above this line `;
|
||||
const transSeed = `// (Chinese) Add your code below this line (Chinese)
|
||||
Add your code above this line `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
transSeed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
it('does not translate urls', () => {
|
||||
const seed = `http:// Add your code below this line
|
||||
Add your code above this line `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
seed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(seed);
|
||||
const seedS = `https:// Add your code below this line
|
||||
Add your code above this line `;
|
||||
expect(
|
||||
translateComments(seedS, 'chinese', SIMPLE_TRANSLATION, 'js')
|
||||
translateComments(seedS, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(seedS);
|
||||
});
|
||||
|
||||
@ -200,9 +106,9 @@ describe('translation parser', () => {
|
||||
Add your code above this line `;
|
||||
const transSeed = `inline comment // (Chinese) Add your code below this line (Chinese)
|
||||
Add your code above this line `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
transSeed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
it('replaces multiple English comments with their translations', () => {
|
||||
@ -210,9 +116,9 @@ describe('translation parser', () => {
|
||||
// Add your code below this line `;
|
||||
const transSeed = `inline comment // (Chinese) Add your code below this line (Chinese)
|
||||
// (Chinese) Add your code below this line (Chinese) `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
transSeed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
it('replaces multiline English comments with their translations', () => {
|
||||
@ -220,9 +126,9 @@ describe('translation parser', () => {
|
||||
/* Add your code above this line */ change code below this line `;
|
||||
const transSeed = `multiline comment /* (Chinese) Add your code below this line (Chinese) */
|
||||
/* (Chinese) Add your code above this line (Chinese) */ change code below this line `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
transSeed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
it('replaces repeated multiline comments with their translations', () => {
|
||||
@ -230,17 +136,17 @@ describe('translation parser', () => {
|
||||
/* Add your code below this line */ change code below this line `;
|
||||
const transSeed = `multiline comment /* (Chinese) Add your code below this line (Chinese) */
|
||||
/* (Chinese) Add your code below this line (Chinese) */ change code below this line `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
transSeed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
it('ignores empty comments', () => {
|
||||
expect.assertions(1);
|
||||
const seed = '//';
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
seed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(seed);
|
||||
});
|
||||
|
||||
it('only replaces text inside comments, not between them', () => {
|
||||
@ -248,13 +154,13 @@ describe('translation parser', () => {
|
||||
/* Add your code above this line */ Add your code below this line /* */ `;
|
||||
const transSeed = `multiline comment /* (Chinese) Add your code below this line (Chinese) */
|
||||
/* (Chinese) Add your code above this line (Chinese) */ Add your code below this line /* */ `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
transSeed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(transSeed);
|
||||
|
||||
const seedTwo = `multiline /* */ Add your code below this line /* */ `;
|
||||
expect(
|
||||
translateComments(seedTwo, 'chinese', SIMPLE_TRANSLATION, 'js')
|
||||
translateComments(seedTwo, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(seedTwo);
|
||||
});
|
||||
|
||||
@ -264,7 +170,7 @@ describe('translation parser', () => {
|
||||
const transSeed = `<div> <!-- (Chinese) Add your code below this line (Chinese) -->
|
||||
<!-- (Chinese) Add your code above this line (Chinese) --> <span>change code below this line</span> `;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
@ -276,7 +182,7 @@ describe('translation parser', () => {
|
||||
/* (Chinese) Add your code below this line (Chinese) */
|
||||
</style>`;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
@ -289,15 +195,17 @@ describe('translation parser', () => {
|
||||
/* (Chinese) Add your code below this line (Chinese) */
|
||||
/* (Chinese) Add your code below this line (Chinese) */
|
||||
</style>`;
|
||||
const commentCounts = new Map();
|
||||
commentCounts.set('(Chinese) Add your code below this line (Chinese)', 2);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
).toBe(transSeed);
|
||||
).toEqual({ text: transSeed, commentCounts });
|
||||
});
|
||||
|
||||
it('ignores css comments outside style tags', () => {
|
||||
const seed = `/* Add your code below this line */`;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html').text
|
||||
).toBe(seed);
|
||||
});
|
||||
|
||||
@ -308,14 +216,14 @@ describe('translation parser', () => {
|
||||
<style>
|
||||
</style>`;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html').text
|
||||
).toBe(seed);
|
||||
});
|
||||
|
||||
it('only replaces inside English html comments', () => {
|
||||
const seed = `<div> <!-- --> Add your code below this line <!-- -->`;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html').text
|
||||
).toBe(seed);
|
||||
});
|
||||
|
||||
@ -325,7 +233,7 @@ describe('translation parser', () => {
|
||||
const transSeed = `{ /* (Chinese) Add your code below this line (Chinese) */ }
|
||||
{ /* (Chinese) Add your code above this line (Chinese) */ } <span>change code below this line</span> `;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'jsx')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'jsx').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
@ -337,7 +245,7 @@ describe('translation parser', () => {
|
||||
// (Chinese) Add your code below this line (Chinese)
|
||||
</script>`;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
@ -351,23 +259,23 @@ describe('translation parser', () => {
|
||||
// (Chinese) Add your code below this line (Chinese)
|
||||
</script>`;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
it('ignores html comments inside JavaScript', () => {
|
||||
const seed = `<div> <!-- Add your code below this line
|
||||
Add your code above this line --> <span>change code below this line</span> `;
|
||||
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
|
||||
seed
|
||||
);
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js').text
|
||||
).toBe(seed);
|
||||
});
|
||||
|
||||
it('ignores html comments inside jsx', () => {
|
||||
const seed = `<div> <!-- Add your code below this line
|
||||
Add your code above this line --> <span>change code below this line</span> `;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'jsx')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'jsx').text
|
||||
).toBe(seed);
|
||||
});
|
||||
|
||||
@ -407,7 +315,7 @@ describe('translation parser', () => {
|
||||
const seed = `{ /* Add your code below this line */ }`;
|
||||
const transSeed = `{ /* (Chinese) Add your code below this line (Chinese) */ }`;
|
||||
expect(
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'jsx')
|
||||
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'jsx').text
|
||||
).toBe(transSeed);
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user