fix: translate title and dashName correctly

This commit is contained in:
Oliver Eyton-Williams
2020-10-01 15:50:43 +02:00
committed by Mrugesh Mohapatra
parent ed2840cb1c
commit 1b895e7809
12 changed files with 156 additions and 127 deletions

View File

@ -1,5 +1,5 @@
--- ---
localeTitle: 向HTML Elements说你好 title: 向HTML Elements说你好
forumTopicId: 18276 forumTopicId: 18276
--- ---

View File

@ -1,10 +1,9 @@
--- ---
id: bd7123c8c441eddfaeb5bdef id: bd7123c8c441eddfaeb5bdef
title: Say Hello to HTML Elements title: HTML Elements说你好
challengeType: 0 challengeType: 0
videoUrl: '' videoUrl: ''
forumTopicId: 18276 forumTopicId: 18276
localeTitle: 向HTML Elements说你好
--- ---
## Description ## Description

View File

@ -1,10 +1,10 @@
--- ---
id: bd7123c8c441eddfaeb5bdef id: bd7123c8c441eddfaeb5bdef
title: Say Hello to HTML Elements originalTitle: Say Hello to HTML Elements
challengeType: 0 challengeType: 0
videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2' videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2'
forumTopicId: 18276 forumTopicId: 18276
localeTitle: 向HTML Elements说你好 title: 向HTML Elements说你好
--- ---
## Description ## Description

View File

@ -1,10 +1,10 @@
--- ---
id: bd7123c8c441eddfaeb5bdef id: bd7123c8c441eddfaeb5bdef
title: Say Hello to HTML Elements originalTitle: Say Hello to HTML Elements
challengeType: 0 challengeType: 0
videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2' videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2'
forumTopicId: 18276 forumTopicId: 18276
localeTitle: 向HTML Elements说你好 title: 向HTML Elements说你好
--- ---
## Description ## Description

View File

@ -1,10 +1,10 @@
--- ---
id: bd7123c8c441eddfaeb5bdef id: bd7123c8c441eddfaeb5bdef
title: Say Hello to HTML Elements originalTitle: Say Hello to HTML Elements
challengeType: 0 challengeType: 0
videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2' videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2'
forumTopicId: 18276 forumTopicId: 18276
localeTitle: 向HTML Elements说你好 title: 向HTML Elements说你好
--- ---
## Description ## Description

View File

@ -1,10 +1,10 @@
--- ---
id: bd7123c8c441eddfaeb5bdef id: bd7123c8c441eddfaeb5bdef
title: Say Hello to HTML Elements originalTitle: Say Hello to HTML Elements
challengeType: 0 challengeType: 0
videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2' videoUrl: 'https://scrimba.com/p/pVMPUv/cE8Gpt2'
forumTopicId: 18276 forumTopicId: 18276
localeTitle: 向HTML Elements说你好 title: 向HTML Elements说你好
--- ---
## Description ## Description

View File

@ -180,7 +180,11 @@ ${getFullPath('english')}
time time
} = meta; } = meta;
challenge.block = blockName; challenge.block = blockName;
challenge.dashedName = dasherize(challenge.title); challenge.dashedName =
lang === 'english'
? dasherize(challenge.title)
: dasherize(challenge.originalTitle);
delete challenge.originalTitle;
challenge.order = order; challenge.order = order;
challenge.superOrder = superOrder; challenge.superOrder = superOrder;
challenge.superBlock = superBlock; challenge.superBlock = superBlock;

View File

@ -17,100 +17,90 @@ const fileJoi = Joi.object().keys({
history: [Joi.array().items(Joi.string().allow('')), Joi.string().allow('')] history: [Joi.array().items(Joi.string().allow('')), Joi.string().allow('')]
}); });
function getSchemaForLang(lang) { const schema = Joi.object().keys({
let schema = Joi.object().keys({ block: Joi.string(),
block: Joi.string(), blockId: Joi.objectId(),
blockId: Joi.objectId(), challengeOrder: Joi.number(),
challengeOrder: Joi.number(), challengeType: Joi.number()
challengeType: Joi.number() .min(0)
.min(0) .max(11)
.max(11) .required(),
checksum: Joi.number(),
dashedName: Joi.string(),
description: Joi.when('challengeType', {
is: Joi.only([challengeTypes.step, challengeTypes.video]),
then: Joi.string().allow(''),
otherwise: Joi.string().required()
}),
fileName: Joi.string(),
files: Joi.object().keys({
indexcss: fileJoi,
indexhtml: fileJoi,
indexjs: fileJoi,
indexjsx: fileJoi
}),
guideUrl: Joi.string().uri({ scheme: 'https' }),
videoUrl: Joi.string().allow(''),
forumTopicId: Joi.number(),
helpRoom: Joi.string(),
id: Joi.objectId().required(),
instructions: Joi.string().allow(''),
isComingSoon: Joi.bool(),
isLocked: Joi.bool(),
isPrivate: Joi.bool(),
name: Joi.string(),
order: Joi.number(),
// video challenges only:
videoId: Joi.when('challengeType', {
is: challengeTypes.video,
then: Joi.string().required()
}),
question: Joi.object().keys({
text: Joi.string().required(),
answers: Joi.array()
.items(Joi.string())
.required(), .required(),
checksum: Joi.number(), solution: Joi.number().required()
dashedName: Joi.string(), }),
description: Joi.when('challengeType', { required: Joi.array().items(
is: Joi.only([challengeTypes.step, challengeTypes.video]), Joi.object().keys({
then: Joi.string().allow(''), link: Joi.string(),
otherwise: Joi.string().required() raw: Joi.bool(),
}), src: Joi.string(),
fileName: Joi.string(), crossDomain: Joi.bool()
files: Joi.object().keys({ })
),
solutions: Joi.array().items(
Joi.object().keys({
indexcss: fileJoi, indexcss: fileJoi,
indexhtml: fileJoi, indexhtml: fileJoi,
indexjs: fileJoi, indexjs: fileJoi,
indexjsx: fileJoi indexjsx: fileJoi,
}), indexpy: fileJoi
guideUrl: Joi.string().uri({ scheme: 'https' }), })
videoUrl: Joi.string().allow(''), ),
forumTopicId: Joi.number(), superBlock: Joi.string(),
helpRoom: Joi.string(), superOrder: Joi.number(),
id: Joi.objectId().required(), suborder: Joi.number(),
instructions: Joi.string().allow(''), tests: Joi.array().items(
isComingSoon: Joi.bool(), // public challenges
isLocked: Joi.bool(), Joi.object().keys({
isPrivate: Joi.bool(),
name: Joi.string(),
order: Joi.number(),
// video challenges only:
videoId: Joi.when('challengeType', {
is: challengeTypes.video,
then: Joi.string().required()
}),
question: Joi.object().keys({
text: Joi.string().required(), text: Joi.string().required(),
answers: Joi.array() testString: Joi.string()
.items(Joi.string()) .allow('')
.required(), .required()
solution: Joi.number().required()
}), }),
required: Joi.array().items( // our tests used in certification verification
Joi.object().keys({ Joi.object().keys({
link: Joi.string(), id: Joi.string().required(),
raw: Joi.bool(), title: Joi.string().required()
src: Joi.string(), })
crossDomain: Joi.bool() ),
}) template: Joi.string().allow(''),
), time: Joi.string().allow(''),
solutions: Joi.array().items( title: Joi.string().required()
Joi.object().keys({ });
indexcss: fileJoi,
indexhtml: fileJoi,
indexjs: fileJoi,
indexjsx: fileJoi,
indexpy: fileJoi
})
),
superBlock: Joi.string(),
superOrder: Joi.number(),
suborder: Joi.number(),
tests: Joi.array().items(
// public challenges
Joi.object().keys({
text: Joi.string().required(),
testString: Joi.string()
.allow('')
.required()
}),
// our tests used in certification verification
Joi.object().keys({
id: Joi.string().required(),
title: Joi.string().required()
})
),
template: Joi.string().allow(''),
time: Joi.string().allow(''),
title: Joi.string().required()
});
if (lang !== 'english') { exports.challengeSchemaValidator = () => {
// TODO: make this required again once all current challenges have it.
schema = schema.append({
localeTitle: Joi.string().allow('')
});
}
return schema;
}
exports.challengeSchemaValidator = lang => {
const schema = getSchemaForLang(lang);
return challenge => Joi.validate(challenge, schema); return challenge => Joi.validate(challenge, schema);
}; };

View File

@ -245,7 +245,7 @@ function validateBlock(challenge) {
function populateTestsForLang({ lang, challenges, meta }) { function populateTestsForLang({ lang, challenges, meta }) {
const mongoIds = new MongoIds(); const mongoIds = new MongoIds();
const challengeTitles = new ChallengeTitles(); const challengeTitles = new ChallengeTitles();
const validateChallenge = challengeSchemaValidator(lang); const validateChallenge = challengeSchemaValidator();
describe(`Check challenges (${lang})`, function() { describe(`Check challenges (${lang})`, function() {
this.timeout(5000); this.timeout(5000);
@ -253,18 +253,8 @@ function populateTestsForLang({ lang, challenges, meta }) {
const dashedBlockName = dasherize(challenge.block); const dashedBlockName = dasherize(challenge.block);
describe(challenge.block || 'No block', function() { describe(challenge.block || 'No block', function() {
describe(challenge.title || 'No title', function() { describe(challenge.title || 'No title', function() {
it('Matches a title in meta.json', function() { // Note: the title in meta.json are purely for human readability and
const index = meta[dashedBlockName].findIndex( // do not include translations, so we do not validate against them.
arr => arr[1] === challenge.title
);
if (index < 0) {
throw new AssertionError(
`Cannot find title "${challenge.title}" in meta.json file`
);
}
});
it('Matches an ID in meta.json', function() { it('Matches an ID in meta.json', function() {
const index = meta[dashedBlockName].findIndex( const index = meta[dashedBlockName].findIndex(
arr => arr[0] === challenge.id arr => arr[0] === challenge.id

View File

@ -122,11 +122,10 @@ const ENGLISH_VIDEO_CHALLENGE = {
const TRANSLATED_CERTIFICATE = { const TRANSLATED_CERTIFICATE = {
id: '561add10cb82ac38a17513bc', id: '561add10cb82ac38a17513bc',
title: 'Responsive Web Design Certificate', title: '响应式网页设计证书',
challengeType: 7, challengeType: 7,
isPrivate: true, isPrivate: true,
videoUrl: '', videoUrl: '',
localeTitle: '响应式网页设计证书',
tests: [ tests: [
{ id: 'bd7158d8c442eddfaeb5bd18', title: 'Build a Tribute Page' }, { id: 'bd7158d8c442eddfaeb5bd18', title: 'Build a Tribute Page' },
{ id: '587d78af367417b2b2512b03', title: 'Build a Survey Form' }, { id: '587d78af367417b2b2512b03', title: 'Build a Survey Form' },
@ -151,11 +150,40 @@ const TRANSLATED_CERTIFICATE = {
const TRANSLATED_CHALLENGE = { const TRANSLATED_CHALLENGE = {
id: 'id', id: 'id',
title: 'Title', 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, challengeType: 0,
videoUrl: 'https://scrimba.com/', videoUrl: 'https://scrimba.com/',
forumTopicId: 9876, forumTopicId: 9876,
localeTitle: 'Translated title',
tests: [ tests: [
{ {
text: 'Translated test text', text: 'Translated test text',
@ -194,11 +222,10 @@ const TRANSLATED_VIDEO_CHALLENGE = {
const WRONG_NUM_TESTS_CHALLENGE = { const WRONG_NUM_TESTS_CHALLENGE = {
id: 'id', id: 'id',
title: 'Title', title: 'Translated title',
challengeType: 0, challengeType: 0,
videoUrl: 'https://scrimba.com/', videoUrl: 'https://scrimba.com/',
forumTopicId: 12345, forumTopicId: 12345,
localeTitle: 'Translated title',
tests: [ tests: [
{ {
text: 'Translated test text', text: 'Translated test text',
@ -227,5 +254,6 @@ exports.ENGLISH_CHALLENGE_NO_FILES = ENGLISH_CHALLENGE_NO_FILES;
exports.ENGLISH_VIDEO_CHALLENGE = ENGLISH_VIDEO_CHALLENGE; exports.ENGLISH_VIDEO_CHALLENGE = ENGLISH_VIDEO_CHALLENGE;
exports.TRANSLATED_CERTIFICATE = TRANSLATED_CERTIFICATE; exports.TRANSLATED_CERTIFICATE = TRANSLATED_CERTIFICATE;
exports.TRANSLATED_CHALLENGE = TRANSLATED_CHALLENGE; exports.TRANSLATED_CHALLENGE = TRANSLATED_CHALLENGE;
exports.TRANSLATED_CHALLENGE_NO_TITLE = TRANSLATED_CHALLENGE_NO_TITLE;
exports.TRANSLATED_VIDEO_CHALLENGE = TRANSLATED_VIDEO_CHALLENGE; exports.TRANSLATED_VIDEO_CHALLENGE = TRANSLATED_VIDEO_CHALLENGE;
exports.WRONG_NUM_TESTS_CHALLENGE = WRONG_NUM_TESTS_CHALLENGE; exports.WRONG_NUM_TESTS_CHALLENGE = WRONG_NUM_TESTS_CHALLENGE;

View File

@ -1,3 +1,4 @@
const { isEmpty } = require('lodash');
const clone = require('lodash/cloneDeep'); const clone = require('lodash/cloneDeep');
exports.translateComments = (text, lang, dict, codeLang) => { exports.translateComments = (text, lang, dict, codeLang) => {
@ -40,14 +41,16 @@ exports.mergeChallenges = (engChal, transChal) => {
...engChal, ...engChal,
description: transChal.description, description: transChal.description,
instructions: transChal.instructions, instructions: transChal.instructions,
localeTitle: transChal.localeTitle, originalTitle: engChal.title,
// TODO: throw in production?
title: isEmpty(transChal.title) ? engChal.title : transChal.title,
forumTopicId: transChal.forumTopicId forumTopicId: transChal.forumTopicId
}; };
if (!hasTests) if (!hasTests)
throw Error( throw Error(
`Both challenges must have tests or questions. `Both challenges must have tests or questions.
title: ${engChal.title} title: ${engChal.title}
localeTitle: ${transChal.localeTitle}` translated title: ${transChal.title}`
); );
// TODO: this should break the build when we go to production, but // TODO: this should break the build when we go to production, but
// not for testing. // not for testing.
@ -55,7 +58,7 @@ exports.mergeChallenges = (engChal, transChal) => {
console.error( console.error(
`Challenges in both languages must have the same number of tests. `Challenges in both languages must have the same number of tests.
title: ${engChal.title} title: ${engChal.title}
localeTitle: ${transChal.localeTitle}` translated title: ${transChal.title}`
); );
return challenge; return challenge;
} }
@ -63,7 +66,7 @@ exports.mergeChallenges = (engChal, transChal) => {
// throw Error( // throw Error(
// `Challenges in both languages must have the same number of tests. // `Challenges in both languages must have the same number of tests.
// title: ${engChal.title} // title: ${engChal.title}
// localeTitle: ${transChal.localeTitle}` // translated title: ${transChal.title}`
// ); // );
if (transChal.tests) { if (transChal.tests) {

View File

@ -12,6 +12,7 @@ const {
ENGLISH_VIDEO_CHALLENGE, ENGLISH_VIDEO_CHALLENGE,
TRANSLATED_CERTIFICATE, TRANSLATED_CERTIFICATE,
TRANSLATED_CHALLENGE, TRANSLATED_CHALLENGE,
TRANSLATED_CHALLENGE_NO_TITLE,
TRANSLATED_VIDEO_CHALLENGE TRANSLATED_VIDEO_CHALLENGE
// WRONG_NUM_TESTS_CHALLENGE // WRONG_NUM_TESTS_CHALLENGE
} = require('./__fixtures__/challenge-objects'); } = require('./__fixtures__/challenge-objects');
@ -22,6 +23,11 @@ const COMBINED_CHALLENGE = mergeChallenges(
TRANSLATED_CHALLENGE TRANSLATED_CHALLENGE
); );
const COMBINED_CHALLENGE_NO_TITLE = mergeChallenges(
ENGLISH_CHALLENGE,
TRANSLATED_CHALLENGE_NO_TITLE
);
const COMBINED_CHALLENGE_TWO_SOLUTIONS = mergeChallenges( const COMBINED_CHALLENGE_TWO_SOLUTIONS = mergeChallenges(
ENGLISH_CHALLENGE_TWO_SOLUTIONS, ENGLISH_CHALLENGE_TWO_SOLUTIONS,
TRANSLATED_CHALLENGE TRANSLATED_CHALLENGE
@ -100,10 +106,19 @@ describe('translation parser', () => {
expect(actualStrings[i]).toBe(expectedStrings[i]); expect(actualStrings[i]).toBe(expectedStrings[i]);
} }
}); });
it('takes the localTitle from the second challenge', () => { it('takes the title from the second challenge', () => {
expect(COMBINED_CHALLENGE.localeTitle).toBe( expect(COMBINED_CHALLENGE.title).toBe(TRANSLATED_CHALLENGE.title);
TRANSLATED_CHALLENGE.localeTitle });
);
// 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. // TODO: reinstate this after alpha testing.
// it('throws an error if the numbers of tests do not match', () => { // it('throws an error if the numbers of tests do not match', () => {