freeCodeCamp/tools/challenge-parser/translation-parser/index.test.js

446 lines
16 KiB
JavaScript

/* global expect jest */
const {
mergeChallenges,
translateComments,
translateCommentsInChallenge
} = 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
} = 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', () => {
beforeEach(() => {
logSpy = jest.spyOn(console, 'warn').mockImplementation();
});
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
);
});
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
);
});
});
describe('translateCommentsInChallenge', () => {
it('returns a clone of the challenge if there are no comments', () => {
expect(
translateCommentsInChallenge(
ENGLISH_CHALLENGE_NO_FILES,
'chinese',
SIMPLE_TRANSLATION
)
).toEqual(ENGLISH_CHALLENGE_NO_FILES);
});
});
describe('translateComments', () => {
it('replaces single line English comments with their translations', () => {
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 `;
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).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
);
const seedS = `https:// Add your code below this line
Add your code above this line `;
expect(
translateComments(seedS, 'chinese', SIMPLE_TRANSLATION, 'js')
).toBe(seedS);
});
it('replaces inline English comments with their translations', () => {
const seed = `inline comment // Add your code below this line
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
);
});
it('replaces multiple English comments with their translations', () => {
const seed = `inline comment // Add your code below this line
// 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
);
});
it('replaces multiline English comments with their translations', () => {
const seed = `multiline comment /* Add your code below this line */
/* 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
);
});
it('replaces repeated multiline comments with their translations', () => {
const seed = `multiline comment /* Add your code below this line */
/* 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
);
});
it('ignores empty comments', () => {
expect.assertions(1);
const seed = '//';
expect(translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'js')).toBe(
seed
);
});
it('only replaces text inside comments, not between them', () => {
const seed = `multiline comment /* Add your code below this line */
/* 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
);
const seedTwo = `multiline /* */ Add your code below this line /* */ `;
expect(
translateComments(seedTwo, 'chinese', SIMPLE_TRANSLATION, 'js')
).toBe(seedTwo);
});
it('replaces English html comments with their translations', () => {
const seed = `<div> <!-- Add your code below this line -->
<!-- Add your code above this line --> <span>change code below this line</span> `;
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')
).toBe(transSeed);
});
it('replaces css comments with their translations', () => {
const seed = `<style>
/* Add your code below this line */
</style>`;
const transSeed = `<style>
/* (Chinese) Add your code below this line (Chinese) */
</style>`;
expect(
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
).toBe(transSeed);
});
it('replaces multiple css comments with their translations', () => {
const seed = `<style>
/* Add your code below this line */
/* Add your code below this line */
</style>`;
const transSeed = `<style>
/* (Chinese) Add your code below this line (Chinese) */
/* (Chinese) Add your code below this line (Chinese) */
</style>`;
expect(
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
).toBe(transSeed);
});
it('ignores css comments outside style tags', () => {
const seed = `/* Add your code below this line */`;
expect(
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
).toBe(seed);
});
it('ignores css comments between style tags', () => {
const seed = `<style>
</style>
/* Add your code below this line */
<style>
</style>`;
expect(
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
).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')
).toBe(seed);
});
it('replaces English JSX comments with their translations', () => {
const seed = `{ /* Add your code below this line */ }
{ /* Add your code above this line */ } <span>change code below this line</span> `;
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')
).toBe(transSeed);
});
it('replaces English script comments with their translations', () => {
const seed = `<script>
// Add your code below this line
</script>`;
const transSeed = `<script>
// (Chinese) Add your code below this line (Chinese)
</script>`;
expect(
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
).toBe(transSeed);
});
it('replaces multiple script comments with their translations', () => {
const seed = `<script>
/* Add your code below this line */
// Add your code below this line
</script>`;
const transSeed = `<script>
/* (Chinese) Add your code below this line (Chinese) */
// (Chinese) Add your code below this line (Chinese)
</script>`;
expect(
translateComments(seed, 'chinese', SIMPLE_TRANSLATION, 'html')
).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
);
});
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')
).toBe(seed);
});
it('throws if there is not an exact match (js)', () => {
expect.assertions(2);
const seedMulti = `/* Add your code below this line
Add your code above this line */ <span>change code below this line</span> `;
const seedInline = `// Add your code below this line, please`;
expect(() =>
translateComments(seedMulti, 'chinese', SIMPLE_TRANSLATION, 'js')
).toThrow();
expect(() =>
translateComments(seedInline, 'chinese', SIMPLE_TRANSLATION, 'js')
).toThrow();
});
it('only replaces exact matches (jsx)', () => {
expect.assertions(1);
const seedMulti = `{ /* Add your code below this line
Add your code above this line */ } <span>change code below this line</span> `;
expect(() =>
translateComments(seedMulti, 'chinese', SIMPLE_TRANSLATION, 'jsx')
).toThrow();
});
it('only replaces exact matches (html)', () => {
expect.assertions(1);
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, 'html')
).toThrow();
});
it('only translates jsx comments once', () => {
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')
).toBe(transSeed);
});
it('throws if the comment is not in the dictionary', () => {
expect.assertions(6);
const seedJSX = `{ /* this is not a comment */ }`;
const seedInline = `// this is not a comment `;
const seedMulti = `/* this is not a comment */`;
const seedCSS = `<style>
/* this is not a comment */
</style>`;
const seedHTML = `<div> <!-- this is not a comment --> `;
const seedScript = `<script> // this is not a comment </script>`;
expect(() =>
translateComments(seedJSX, 'chinese', SIMPLE_TRANSLATION, 'jsx')
).toThrow();
expect(() =>
translateComments(seedInline, 'chinese', SIMPLE_TRANSLATION, 'js')
).toThrow();
expect(() =>
translateComments(seedMulti, 'chinese', SIMPLE_TRANSLATION, 'js')
).toThrow();
expect(() =>
translateComments(seedCSS, 'chinese', SIMPLE_TRANSLATION, 'html')
).toThrow();
expect(() =>
translateComments(seedHTML, 'chinese', SIMPLE_TRANSLATION, 'html')
).toThrow();
expect(() =>
translateComments(seedScript, 'chinese', SIMPLE_TRANSLATION, 'html')
).toThrow();
});
});
});