curriculum/challenges/english/blocks/quality-assurance-projects/5e601c0d5ac9d0ecd8b94afe.md
Build a full-stack JavaScript app that is functionally similar to this: <a href="https://american-british-translator.freecodecamp.rocks/" target="_blank" rel="noopener noreferrer nofollow">https://american-british-translator.freecodecamp.rocks/</a>. Working on this project will involve you writing your code using one of the following methods:
/components/translator.js/api/translate route in /routes/api.jstests/1_unit-tests.js and tests/2_functional-tests.js/components for the different spelling and terms your application should translateNODE_ENV to test without quotes in the .env filenpm run test.Write the following tests in tests/1_unit-tests.js:
Mangoes are my favorite fruit. to British EnglishI ate yogurt for breakfast. to British EnglishWe had a party at my friend's condo. to British EnglishCan you toss this in the trashcan for me? to British EnglishThe parking lot was full. to British EnglishLike a high tech Rube Goldberg machine. to British EnglishTo play hooky means to skip class or work. to British EnglishNo Mr. Bond, I expect you to die. to British EnglishDr. Grosh will see you now. to British EnglishLunch is at 12:15 today. to British EnglishWe watched the footie match for a while. to American EnglishParacetamol takes up to an hour to work. to American EnglishFirst, caramelise the onions. to American EnglishI spent the bank holiday at the funfair. to American EnglishI had a bicky then went to the chippy. to American EnglishI've just got bits and bobs in my bum bag. to American EnglishThe car boot sale at Boxted Airfield was called off. to American EnglishHave you met Mrs Kalyani? to American EnglishProf Joyner of King's College, London. to American EnglishTea time is usually around 4 or 4.30. to American EnglishMangoes are my favorite fruit.I ate yogurt for breakfast.We watched the footie match for a while.Paracetamol takes up to an hour to work.Write the following tests in tests/2_functional-tests.js:
/api/translate/api/translate/api/translate/api/translate/api/translate/api/translateYou should provide your own project, not the example URL.
assert(
!/.*\/american-british-translator\.freecodecamp\.rocks/.test(
code
)
);
You can POST to /api/translate with a body containing text with the text to translate and locale with either american-to-british or british-to-american. The returned object should contain the submitted text and translation with the translated text.
try {
const text = 'Mangoes are my favorite fruit.';
const locale = 'american-to-british';
const output = {
text: 'Mangoes are my favorite fruit.',
translation:
'Mangoes are my <span class="highlight">favourite</span> fruit.'
};
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.property(parsed, 'text');
assert.property(parsed, 'translation');
assert.deepEqual(parsed, output);
} catch (err) {
throw new Error(err.responseText || err.message);
}
The /api/translate route should handle the way time is written in American and British English. For example, ten thirty is written as "10.30" in British English and "10:30" in American English. The span element should wrap the entire time string, i.e. <span class="highlight">10:30</span>.
try {
const text = 'Lunch is at 12:15 today.';
const locale = 'american-to-british';
const output = {
text: text,
translation: 'Lunch is at <span class="highlight">12.15</span> today.'
};
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.property(parsed, 'text');
assert.property(parsed, 'translation');
assert.deepEqual(parsed, output);
} catch (err) {
throw new Error(err.responseText || err.message);
}
The /api/translate route should also handle the way titles/honorifics are abbreviated in American and British English. For example, Doctor Wright is abbreviated as "Dr Wright" in British English and "Dr. Wright" in American English. See /components/american-to-british-titles.js for the different titles your application should handle.
try {
const text = 'Dr. Grosh will see you now.';
const locale = 'american-to-british';
const output = {
text: text,
translation: '<span class="highlight">Dr</span> Grosh will see you now.'
};
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.property(parsed, 'text');
assert.property(parsed, 'translation');
assert.deepEqual(parsed, output);
} catch (err) {
throw new Error(err.responseText || err.message);
}
Wrap any translated spelling or terms with <span class="highlight">...</span> tags so they appear in green.
try {
const text = 'Mangoes are my favorite fruit.';
const locale = 'american-to-british';
const output = {
text: 'Mangoes are my favorite fruit.',
translation:
'Mangoes are my <span class="highlight">favourite</span> fruit.'
};
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.property(parsed, 'text');
assert.property(parsed, 'translation');
assert.deepEqual(parsed, output);
} catch (err) {
throw new Error(err.responseText || err.message);
}
If one or more of the required fields is missing, return { error: 'Required field(s) missing' }.
try {
const locale = 'american-to-british';
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.property(parsed, 'error');
assert.equal(parsed.error, 'Required field(s) missing');
} catch (err) {
throw new Error(err.responseText || err.message);
}
If text is empty, return { error: 'No text to translate' }
try {
const locale = 'american-to-british';
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: '', locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.property(parsed, 'error');
assert.equal(parsed.error, 'No text to translate');
} catch (err) {
throw new Error(err.responseText || err.message);
}
If locale does not match one of the two specified locales, return { error: 'Invalid value for locale field' }.
try {
const text = "Ceci n'est pas une pipe";
const locale = 'french-to-american';
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.property(parsed, 'error');
assert.equal(parsed.error, 'Invalid value for locale field');
} catch (err) {
throw new Error(err.responseText || err.message);
}
If text requires no translation, return "Everything looks good to me!" for the translation value.
try {
const locale = 'british-to-american';
const output = {
text: 'SaintPeter and nhcarrigan give their regards!',
translation: 'Everything looks good to me!'
};
let data = await fetch(code + '/api/translate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: output.text, locale })
});
let parsed = await data.json();
assert.isObject(parsed);
assert.isObject(parsed);
assert.property(parsed, 'text');
assert.property(parsed, 'translation');
assert.deepEqual(parsed, output);
} catch (err) {
throw new Error(err.responseText || err.message);
}
All 24 unit tests are complete and passing.
try {
const response = await fetch(code + '/_api/get-tests');
if (!response.ok) {
throw Error(await response.text());
}
const getTests = await response.json();
assert.isArray(getTests);
const unitTests = getTests.filter((test) => {
return !!test.context.match(/Unit Tests/gi);
});
assert.isAtLeast(unitTests.length, 24, 'At least 24 tests passed');
unitTests.forEach((test) => {
assert.equal(test.state, 'passed', 'Tests in Passed State');
assert.isAtLeast(
test.assertions.length,
1,
'At least one assertion per test'
);
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
All 6 functional tests are complete and passing.
try {
const response = await fetch(code + '/_api/get-tests');
if (!response.ok) {
throw Error(await response.text());
}
const getTests = await response.json();
assert.isArray(getTests);
const functTests = getTests.filter((test) => {
return !!test.context.match(/Functional Tests/gi);
});
assert.isAtLeast(functTests.length, 6, 'At least 6 tests passed');
functTests.forEach((test) => {
assert.equal(test.state, 'passed', 'Tests in Passed State');
assert.isAtLeast(
test.assertions.length,
1,
'At least one assertion per test'
);
});
} catch (err) {
throw new Error(err.responseText || err.message);
}