chore(i8n,learn): processed translations

This commit is contained in:
Crowdin Bot
2021-02-06 04:42:36 +00:00
committed by Mrugesh Mohapatra
parent 15047f2d90
commit e5c44a3ae5
3274 changed files with 172122 additions and 14164 deletions

View File

@@ -1,77 +1,328 @@
---
id: 5e601c0d5ac9d0ecd8b94afe
title: American British Translator
challengeType: 4
dashedName: american-british-translator
---
# --description--
Build a full stack JavaScript app that is functionally similar to this: <https://youthful-grave-scabiosa.glitch.me/>.
Build a full stack JavaScript app that is functionally similar to this: <https://american-british-translator.freecodecamp.rocks/>. Working on this project will involve you writing your code using one of the following methods:
Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicly visible for our testing.
- Clone [this GitHub repo](https://github.com/freeCodeCamp/boilerplate-project-american-british-english-translator/) and complete your project locally.
- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-american-british-english-translator) to complete your project.
- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
Start this project on Glitch using [this link](https://glitch.com/edit/#!/remix/clone-from-repo?REPO_URL=https://github.com/freeCodeCamp/boilerplate-project-american-british-english-translator/) or clone [this repository](https://github.com/freeCodeCamp/boilerplate-project-american-british-english-translator/) on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!
When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your project's source code in the `GitHub Link` field.
# --instructions--
- All logic can go into `/components/translator.js`
- Complete the `/api/translate` route in `/routes/api.js`
- Create all of the unit/functional tests in `tests/1_unit-tests.js` and `tests/2_functional-tests.js`
- See the JavaScript files in `/components` for the different spelling and terms your application should translate
- To run the tests on Repl.it, set `NODE_ENV` to `test` without quotes in the `.env` file
- To run the tests in the console, use the command `npm run test`. To open the Repl.it console, press Ctrl+Shift+P (Cmd if on a Mac) and type "open shell"
Write the following tests in `tests/1_unit-tests.js`:
- Translate `Mangoes are my favorite fruit.` to British English
- Translate `I ate yogurt for breakfast.` to British English
- Translate `We had a party at my friend's condo.` to British English
- Translate `Can you toss this in the trashcan for me?` to British English
- Translate `The parking lot was full.` to British English
- Translate `Like a high tech Rube Goldberg machine.` to British English
- Translate `To play hooky means to skip class or work.` to British English
- Translate `No Mr. Bond, I expect you to die.` to British English
- Translate `Dr. Grosh will see you now.` to British English
- Translate `Lunch is at 12:15 today.` to British English
- Translate `We watched the footie match for a while.` to American English
- Translate `Paracetamol takes up to an hour to work.` to American English
- Translate `First, caramelise the onions.` to American English
- Translate `I spent the bank holiday at the funfair.` to American English
- Translate `I had a bicky then went to the chippy.` to American English
- Translate `I've just got bits and bobs in my bum bag.` to American English
- Translate `The car boot sale at Boxted Airfield was called off.` to American English
- Translate `Have you met Mrs Kalyani?` to American English
- Translate `Prof Joyner of King's College, London.` to American English
- Translate `Tea time is usually around 4 or 4.30.` to American English
- Highlight translation in `Mangoes are my favorite fruit.`
- Highlight translation in `I ate yogurt for breakfast.`
- Highlight translation in `We watched the footie match for a while.`
- Highlight translation in `Paracetamol takes up to an hour to work.`
Write the following tests in `tests/2_functional-tests.js`:
- Translation with text and locale fields: POST request to `/api/translate`
- Translation with text and invalid locale field: POST request to `/api/translate`
- Translation with missing text field: POST request to `/api/translate`
- Translation with missing locale field: POST request to `/api/translate`
- Translation with empty text: POST request to `/api/translate`
- Translation with text that needs no translation: POST request to `/api/translate`
# --hints--
I can enter a simple sentence into the text area and select whether to translate to British or American English from the dropdown menu.
I can provide my own project, not the example URL.
```js
(getUserInput) => {
assert(
!/.*\/american-british-translator\.freecodecamp\.rocks/.test(
getUserInput('url')
)
);
};
```
When the "Translate" button is pressed, append the translated sentence to the `translated-sentence` `div`. See the JavaScript files in `/public` for the different spelling and terms your application should translate.
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.
```js
async (getUserInput) => {
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(getUserInput('url') + '/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);
}
};
```
Your application 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 `/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>`.
```js
async (getUserInput) => {
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(getUserInput('url') + '/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);
}
};
```
Your application 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 `/public/american-to-british-titles.js` for the different titles your application should handle.
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 `/public/american-to-british-titles.js` for the different titles your application should handle.
```js
async (getUserInput) => {
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(getUserInput('url') + '/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.
```js
async (getUserInput) => {
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(getUserInput('url') + '/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 the sentence in the text area has no spelling or terms that should be translated, append the message "Everything looks good to me!" to the `translated-sentence` `div`.
If one or more of the required fields is missing, return `{ error: 'Required field(s) missing' }`.
```js
async (getUserInput) => {
try {
const locale = 'american-to-british';
let data = await fetch(getUserInput('url') + '/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 there is no text in the text area, append the message "Error: No text to translate." to the `error-msg` `div` so the text appears in red.
If `text` is empty, return `{ error: 'No text to translate' }`
```js
async (getUserInput) => {
try {
const locale = 'american-to-british';
let data = await fetch(getUserInput('url') + '/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);
}
};
```
I can press the "Clear Input" button to remove all text from the text area and the `translated-sentence` `div`.
If `locale` does not match one of the two specified locales, return `{ error: 'Invalid value for locale field' }`.
```js
async (getUserInput) => {
try {
const text = "Ceci n'est pas une pipe";
const locale = 'french-to-american';
let data = await fetch(getUserInput('url') + '/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);
}
};
```
All 20 unit tests are complete and passing. See `/tests/1_unit-tests.js` for the sentences you should write tests for.
If `text` requires no translation, return `"Everything looks good to me!"` for the `translation` value.
```js
async (getUserInput) => {
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(getUserInput('url') + '/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 4 functional tests are complete and passing. See `/tests/2_functional-tests.js` for the functionality you should write tests for.
All 24 unit tests are complete and passing. See `/tests/1_unit-tests.js` for the expected behavior you should write tests for.
```js
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
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. See `/tests/2_functional-tests.js` for the functionality you should write tests for.
```js
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
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);
}
};
```
# --solutions--

View File

@@ -1,63 +1,361 @@
---
id: 587d8249367417b2b2512c42
title: 问题跟踪器
title: Issue Tracker
challengeType: 4
videoUrl: ''
forumTopicId: 301569
dashedName: issue-tracker
---
# --description--
构建一个功能类似于此的完整堆栈JavaScript应用程序 [https](https://protective-garage.glitch.me/) [//protective-garage.glitch.me/](https://protective-garage.glitch.me/) 。在这个项目上工作将涉及您在我们的入门项目上在Glitch上编写代码。完成此项目后您可以将公共故障网址到应用程序的主页复制到此屏幕进行测试您可以选择在另一个平台上编写项目但必须公开显示我们的测试。使用[此链接](https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-issuetracker/)在Glitch上启动此项目或在GitHub上克隆[此存储库](https://github.com/freeCodeCamp/boilerplate-project-issuetracker/) 如果您使用Glitch请记住将项目链接保存到安全的地方
Build a full stack JavaScript app that is functionally similar to this: <https://issue-tracker.freecodecamp.rocks/>. Working on this project will involve you writing your code using one of the following methods:
- Clone [this GitHub repo](https://github.com/freeCodeCamp/boilerplate-project-issuetracker/) and complete your project locally.
- Use [this repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-issuetracker) to complete your project.
- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your project's source code in the `GitHub Link` field.
# --instructions--
- Complete the necessary routes in `/routes/api.js`
- Create all of the functional tests in `tests/2_functional-tests.js`
- Copy the `sample.env` file to `.env` and set the variables appropriately
- To run the tests uncomment `NODE_ENV=test` in your `.env` file
- To run the tests in the console, use the command `npm run test`. To open the Repl.it console, press Ctrl+Shift+P (Cmd if on a Mac) and type "open shell"
Write the following tests in `tests/2_functional-tests.js`:
- Create an issue with every field: POST request to `/api/issues/{project}`
- Create an issue with only required fields: POST request to `/api/issues/{project}`
- Create an issue with missing required fields: POST request to `/api/issues/{project}`
- View issues on a project: GET request to `/api/issues/{project}`
- View issues on a project with one filter: GET request to `/api/issues/{project}`
- View issues on a project with multiple filters: GET request to `/api/issues/{project}`
- Update one field on an issue: PUT request to `/api/issues/{project}`
- Update multiple fields on an issue: PUT request to `/api/issues/{project}`
- Update an issue with missing `_id`: PUT request to `/api/issues/{project}`
- Update an issue with no fields to update: PUT request to `/api/issues/{project}`
- Update an issue with an invalid `_id`: PUT request to `/api/issues/{project}`
- Delete an issue: DELETE request to `/api/issues/{project}`
- Delete an issue with an invalid `_id`: DELETE request to `/api/issues/{project}`
- Delete an issue with missing `_id`: DELETE request to `/api/issues/{project}`
# --hints--
防止跨站点脚本XSS攻击。
You can provide your own project, not the example URL.
```js
(getUserInput) => {
assert(!/.*\/issue-tracker\.freecodecamp\.rocks/.test(getUserInput('url')));
};
```
我可以使用包含所需issue_titleissue_textcreated_by和可选的assigned_tostatus_text的表单数据POST / api / issues / {projectname}。
You can send a `POST` request to `/api/issues/{projectname}` with form data containing the required fields `issue_title`, `issue_text`, `created_by`, and optionally `assigned_to` and `status_text`.
```js
async (getUserInput) => {
try {
let test_data = {
issue_title: 'Faux Issue Title',
issue_text: 'Functional Test - Required Fields Only',
created_by: 'fCC'
};
const data = await $.post(
getUserInput('url') + '/api/issues/fcc-project',
test_data
);
assert.isObject(data);
assert.nestedInclude(data, test_data);
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
保存和返回的对象将包括所有这些字段空白表示可选无输入还包括created_on日期/时间updated_on日期/时间打开布尔值true表示打开false表示关闭和\_id。
The `POST` request to `/api/issues/{projectname}` will return the created object, and must include all of the submitted fields. Excluded optional fields will be returned as empty strings. Additionally, include `created_on` (date/time), `updated_on` (date/time), `open` (boolean, `true` for open - default value, `false` for closed), and `_id`.
```js
async (getUserInput) => {
try {
let test_data = {
issue_title: 'Faux Issue Title 2',
issue_text: 'Functional Test - Every field filled in',
created_by: 'fCC',
assigned_to: 'Chai and Mocha'
};
const data = await $.post(
getUserInput('url') + '/api/issues/fcc-project',
test_data
);
assert.isObject(data);
assert.nestedInclude(data, test_data);
assert.property(data, 'created_on');
assert.isNumber(Date.parse(data.created_on));
assert.property(data, 'updated_on');
assert.isNumber(Date.parse(data.updated_on));
assert.property(data, 'open');
assert.isBoolean(data.open);
assert.isTrue(data.open);
assert.property(data, '_id');
assert.isNotEmpty(data._id);
assert.property(data, 'status_text');
assert.isEmpty(data.status_text);
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以使用id和对象中的任何字段PUT / api / issues / {projectname},并使用值来对象表示对象。返回将“成功更新”或“无法更新”+ id。这应该始终更新updated_on。如果没有发送字段则返回“未发送更新字段”。
If you send a `POST` request to `/api/issues/{projectname}` without the required fields, returned will be the error `{ error: 'required field(s) missing' }`
```js
async (getUserInput) => {
try {
let test_data = { created_by: 'fCC' };
const data = await $.post(getUserInput('url') + '/api/issues/fcc-project', {
created_by: 'fCC'
});
assert.isObject(data);
assert.property(data, 'error');
assert.equal(data.error, 'required field(s) missing');
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以使用id DELETE / api / issues / {projectname}来完全删除问题。如果没有发送\_id则返回“id error”成功“删除”+ id失败“无法删除”+ id。
You can send a `GET` request to `/api/issues/{projectname}` for an array of all issues for that specific `projectname`, with all the fields present for each issue.
```js
async (getUserInput) => {
try {
let test_data = { issue_text: 'Get Issues Test', created_by: 'fCC' };
const url =
getUserInput('url') +
'/api/issues/get_issues_test_' +
Date.now().toString().substring(7);
const data1 = await $.post(
url,
Object.assign(test_data, { issue_title: 'Faux Issue 1' })
);
assert.isObject(data1);
const data2 = await $.post(
url,
Object.assign(test_data, { issue_title: 'Faux Issue 2' })
);
assert.isObject(data2);
const data3 = await $.post(
url,
Object.assign(test_data, { issue_title: 'Faux Issue 3' })
);
assert.isObject(data3);
const getIssues = await $.get(url);
assert.isArray(getIssues);
assert.lengthOf(getIssues, 3);
let re = new RegExp('Faux Issue \\d');
getIssues.forEach((issue) => {
assert.property(issue, 'issue_title');
assert.match(issue.issue_title, re);
assert.property(issue, 'issue_text');
assert.property(issue, 'created_by');
assert.property(issue, 'assigned_to');
assert.property(issue, 'status_text');
assert.property(issue, 'open');
assert.property(issue, 'created_on');
assert.property(issue, 'updated_on');
assert.property(issue, '_id');
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以GET / api / issues / {projectname}获取该特定项目中所有问题的数组,其中包含发布时返回的每个问题的所有信息。
You can send a `GET` request to `/api/issues/{projectname}` and filter the request by also passing along any field and value as a URL query (ie. `/api/issues/{project}?open=false`). You can pass one or more field/value pairs at once.
```js
async (getUserInput) => {
try {
let test_data = {
issue_title: 'To be Filtered',
issue_text: 'Filter Issues Test'
};
const url =
getUserInput('url') +
'/api/issues/get_issues_test_' +
Date.now().toString().substring(7);
const data1 = await $.post(
url,
Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Bob' })
);
const data2 = await $.post(
url,
Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Bob' })
);
const data3 = await $.post(
url,
Object.assign(test_data, { created_by: 'Alice', assigned_to: 'Eric' })
);
const data4 = await $.post(
url,
Object.assign(test_data, { created_by: 'Carol', assigned_to: 'Eric' })
);
const getSingle = await $.get(url + '?created_by=Alice');
assert.isArray(getSingle);
assert.lengthOf(getSingle, 3);
const getMultiple = await $.get(url + '?created_by=Alice&assigned_to=Bob');
assert.isArray(getMultiple);
assert.lengthOf(getMultiple, 2);
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以通过传递查询中的任何字段和值来过滤我的获取请求(即。/ api / issues / {project}open = false。我可以传递尽可能多的字段/值。
You can send a `PUT` request to `/api/issues/{projectname}` with an `_id` and one or more fields to update. On success, the `updated_on` field should be updated, and returned should be `{ result: 'successfully updated', '_id': _id }`.
```js
async (getUserInput) => {
try {
let initialData = {
issue_title: 'Issue to be Updated',
issue_text: 'Functional Test - Put target',
created_by: 'fCC'
};
const url = getUserInput('url') + '/api/issues/fcc-project';
const itemToUpdate = await $.post(url, initialData);
const updateSucccess = await $.ajax({
url: url,
type: 'PUT',
data: { _id: itemToUpdate._id, issue_text: 'New Issue Text' }
});
assert.isObject(updateSucccess);
assert.deepEqual(updateSucccess, {
result: 'successfully updated',
_id: itemToUpdate._id
});
const getUpdatedId = await $.get(url + '?_id=' + itemToUpdate._id);
assert.isArray(getUpdatedId);
assert.isObject(getUpdatedId[0]);
assert.isAbove(
Date.parse(getUpdatedId[0].updated_on),
Date.parse(getUpdatedId[0].created_on)
);
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
所有11项功能测试都已完成并通过。
When the `PUT` request sent to `/api/issues/{projectname}` does not include an `_id`, the return value is `{ error: 'missing _id' }`.
```js
async (getUserInput) => {
try {
const url = getUserInput('url') + '/api/issues/fcc-project';
const badUpdate = await $.ajax({ url: url, type: 'PUT' });
assert.isObject(badUpdate);
assert.property(badUpdate, 'error');
assert.equal(badUpdate.error, 'missing _id');
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
When the `PUT` request sent to `/api/issues/{projectname}` does not include update fields, the return value is `{ error: 'no update field(s) sent', '_id': _id }`. On any other error, the return value is `{ error: 'could not update', '_id': _id }`.
```js
async (getUserInput) => {
try {
const url = getUserInput('url') + '/api/issues/fcc-project';
const badUpdate = await $.ajax({
url: url,
type: 'PUT',
data: { _id: '5f665eb46e296f6b9b6a504d' }
});
assert.deepEqual(badUpdate, {
error: 'no update field(s) sent',
_id: '5f665eb46e296f6b9b6a504d'
});
const badIdUpdate = await $.ajax({
url: url,
type: 'PUT',
data: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
});
assert.deepEqual(badIdUpdate, {
error: 'could not update',
_id: '5f665eb46e296f6b9b6a504d'
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
You can send a `DELETE` request to `/api/issues/{projectname}` with an `_id` to delete an issue. If no `_id` is sent, the return value is `{ error: 'missing _id' }`. On success, the return value is `{ result: 'successfully deleted', '_id': _id }`. On failure, the return value is `{ error: 'could not delete', '_id': _id }`.
```js
async (getUserInput) => {
try {
let initialData = {
issue_title: 'Issue to be Deleted',
issue_text: 'Functional Test - Delete target',
created_by: 'fCC'
};
const url = getUserInput('url') + '/api/issues/fcc-project';
const itemToDelete = await $.post(url, initialData);
assert.isObject(itemToDelete);
const deleteSuccess = await $.ajax({
url: url,
type: 'DELETE',
data: { _id: itemToDelete._id }
});
assert.isObject(deleteSuccess);
assert.deepEqual(deleteSuccess, {
result: 'successfully deleted',
_id: itemToDelete._id
});
const noId = await $.ajax({ url: url, type: 'DELETE' });
assert.isObject(noId);
assert.deepEqual(noId, { error: 'missing _id' });
const badIdDelete = await $.ajax({
url: url,
type: 'DELETE',
data: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
});
assert.isObject(badIdDelete);
assert.deepEqual(badIdDelete, {
error: 'could not delete',
_id: '5f665eb46e296f6b9b6a504d'
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
All 14 functional tests are complete and passing.
```js
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
assert.isAtLeast(getTests.length, 14, 'At least 14 tests passed');
getTests.forEach((test) => {
assert.equal(test.state, 'passed', 'Test in Passed State');
assert.isAtLeast(
test.assertions.length,
1,
'At least one assertion per test'
);
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
# --solutions--

View File

@@ -1,93 +1,308 @@
---
id: 587d8249367417b2b2512c41
title: 公制 - 英制转换器
title: Metric-Imperial Converter
challengeType: 4
videoUrl: ''
forumTopicId: 301570
dashedName: metric-imperial-converter
---
# --description--
构建一个功能类似于此的完整堆栈JavaScript应用程序 <https://incongruous-beard.glitch.me/>。在这个项目上工作将涉及您在我们的入门项目上在Glitch上编写代码。完成此项目后您可以将公共故障网址到应用程序的主页复制到此屏幕进行测试您可以选择在另一个平台上编写项目但必须公开显示我们的测试。使用[此链接](https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-metricimpconverter/)在Glitch上启动此项目或在GitHub上克隆[此存储库](https://github.com/freeCodeCamp/boilerplate-project-metricimpconverter/) 如果您使用Glitch请记住将项目链接保存到安全的地方
Build a full stack JavaScript app that is functionally similar to this: <https://metric-imperial-converter.freecodecamp.rocks/>. Working on this project will involve you writing your code using one of the following methods:
- Clone [this GitHub repo](https://github.com/freeCodeCamp/boilerplate-project-metricimpconverter/) and complete your project locally.
- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-metricimpconverter) to complete your project.
- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your project's source code in the `GitHub Link` field.
# --instructions--
- Complete the necessary conversion logic in `/controllers/convertHandler.js`
- Complete the necessary routes in `/routes/api.js`
- Copy the `sample.env` file to `.env` and set the variables appropriately
- To run the tests uncomment `NODE_ENV=test` in your `.env` file
- To run the tests in the console, use the command `npm run test`. To open the Repl.it console, press Ctrl+Shift+P (Cmd if on a Mac) and type "open shell"
Write the following tests in `tests/1_unit-tests.js`:
- `convertHandler` should correctly read a whole number input.
- `convertHandler` should correctly read a decimal number input.
- `convertHandler` should correctly read a fractional input.
- `convertHandler` should correctly read a fractional input with a decimal.
- `convertHandler` should correctly return an error on a double-fraction (i.e. `3/2/3`).
- `convertHandler` should correctly default to a numerical input of `1` when no numerical input is provided.
- `convertHandler` should correctly read each valid input unit.
- `convertHandler` should correctly return an error for an invalid input unit.
- `convertHandler` should return the correct return unit for each valid input unit.
- `convertHandler` should correctly return the spelled-out string unit for each valid input unit.
- `convertHandler` should correctly convert `gal` to `L`.
- `convertHandler` should correctly convert `L` to `gal`.
- `convertHandler` should correctly convert `mi` to `km`.
- `convertHandler` should correctly convert `km` to `mi`.
- `convertHandler` should correctly convert `lbs` to `kg`.
- `convertHandler` should correctly convert `kg` to `lbs`.
Write the following tests in `tests/2_functional-tests.js`:
- Convert a valid input such as `10L`: `GET` request to `/api/convert`.
- Convert an invalid input such as `32g`: `GET` request to `/api/convert`.
- Convert an invalid number such as `3/7.2/4kg`: `GET` request to `/api/convert`.
- Convert an invalid number AND unit such as `3/7.2/4kilomegagram`: `GET` request to `/api/convert`.
- Convert with no number such as `kg`: `GET` request to `/api/convert`.
# --hints--
我将阻止客户端尝试猜测嗅探MIME类型。
You can provide your own project, not the example URL.
```js
getUserInput => {
assert(
!/.*\/metric-imperial-converter\.freecodecamp\.rocks/.test(
getUserInput('url')
)
);
};
```
我会阻止跨站点脚本XSS攻击。
You can `GET` `/api/convert` with a single parameter containing an accepted number and unit and have it converted. (Hint: Split the input by looking for the index of the first character which will mark the start of the unit)
```js
```
我可以使用包含可接受的数字和单位的单个参数进行GET / api / convert并将其转换。 (提示:通过查找将标记单元开头的第一个字符的索引来拆分输入)
You can convert `'gal'` to `'L'` and vice versa. (1 gal to 3.78541 L)
```js
async getUserInput => {
try {
const data1 = await $.get(getUserInput('url') + '/api/convert?input=1gal');
assert.equal(data1.returnNum, 3.78541);
assert.equal(data1.returnUnit, 'L');
const data2 = await $.get(getUserInput('url') + '/api/convert?input=10gal');
assert.equal(data2.returnNum, 37.8541);
assert.equal(data2.returnUnit, 'L');
const data3 = await $.get(getUserInput('url') + '/api/convert?input=1l');
assert.equal(data3.returnNum, 0.26417);
assert.equal(data3.returnUnit, 'gal');
const data4 = await $.get(getUserInput('url') + '/api/convert?input=10l');
assert.equal(data4.returnNum, 2.64172);
assert.equal(data4.returnUnit, 'gal');
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
我可以将'gal'转换为'L',反之亦然。 1加仑到3.78541升)
You can convert `'lbs'` to `'kg'` and vice versa. (1 lbs to 0.453592 kg)
```js
async getUserInput => {
try {
const data1 = await $.get(getUserInput('url') + '/api/convert?input=1lbs');
assert.equal(data1.returnNum, 0.45359);
assert.equal(data1.returnUnit, 'kg');
const data2 = await $.get(getUserInput('url') + '/api/convert?input=10lbs');
assert.equal(data2.returnNum, 4.53592);
assert.equal(data2.returnUnit, 'kg');
const data3 = await $.get(getUserInput('url') + '/api/convert?input=1kg');
assert.equal(data3.returnNum, 2.20462);
assert.equal(data3.returnUnit, 'lbs');
const data4 = await $.get(getUserInput('url') + '/api/convert?input=10kg');
assert.equal(data4.returnNum, 22.04624);
assert.equal(data4.returnUnit, 'lbs');
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
我可以将'lbs'转换为'kg',反之亦然。 1磅至0.453592千克)
You can convert `'mi'` to `'km'` and vice versa. (1 mi to 1.60934 km)
```js
async getUserInput => {
try {
const data1 = await $.get(getUserInput('url') + '/api/convert?input=1mi');
assert.equal(data1.returnNum, 1.60934);
assert.equal(data1.returnUnit, 'km');
const data2 = await $.get(getUserInput('url') + '/api/convert?input=10mi');
assert.equal(data2.returnNum, 16.0934);
assert.equal(data2.returnUnit, 'km');
const data3 = await $.get(getUserInput('url') + '/api/convert?input=1km');
assert.equal(data3.returnNum, 0.62137);
assert.equal(data3.returnUnit, 'mi');
const data4 = await $.get(getUserInput('url') + '/api/convert?input=10km');
assert.equal(data4.returnNum, 6.21373);
assert.equal(data4.returnUnit, 'mi');
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
我可以将'mi'转换为'km',反之亦然。 1英里到1.60934公里)
All incoming units should be accepted in both upper and lower case, but should be returned in both the `initUnit` and `returnUnit` in lower case, except for liter, which should be represented as an uppercase `'L'`.
```js
async getUserInput => {
try {
const data1 = await $.get(getUserInput('url') + '/api/convert?input=1gal');
assert.equal(data1.initUnit, 'gal');
assert.equal(data1.returnUnit, 'L');
const data2 = await $.get(getUserInput('url') + '/api/convert?input=10L');
assert.equal(data2.initUnit, 'L');
assert.equal(data2.returnUnit, 'gal');
const data3 = await $.get(getUserInput('url') + '/api/convert?input=1l');
assert.equal(data3.initUnit, 'L');
assert.equal(data3.returnUnit, 'gal');
const data4 = await $.get(getUserInput('url') + '/api/convert?input=10KM');
assert.equal(data4.initUnit, 'km');
assert.equal(data4.returnUnit, 'mi');
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
如果我的计量单位无效,则返回“无效单位”。
If the unit of measurement is invalid, returned will be `'invalid unit'`.
```js
async getUserInput => {
try {
const data = await $.get(getUserInput('url') + '/api/convert?input=1min');
assert(data.error === 'invalid unit' || data === 'invalid unit');
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
如果我的号码无效,则返回“无效号码”。
If the number is invalid, returned will be `'invalid number'`.
```js
async getUserInput => {
try {
const data = await $.get(
getUserInput('url') + '/api/convert?input=1//2gal'
);
assert(data.error === 'invalid number' || data === 'invalid number');
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
如果两者都无效,则返回“无效数字和单位”。
If both the unit and number are invalid, returned will be `'invalid number and unit'`.
```js
async getUserInput => {
try {
const data = await $.get(
getUserInput('url') + '/api/convert?input=1//2min'
);
assert(
data.error === 'invalid number and unit' ||
data === 'invalid number and unit'
);
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
我可以在我的参数中使用分数小数或两者即5,1 / 2,2.5 / 6但如果没有提供则默认为1。
You can use fractions, decimals or both in the parameter (ie. 5, 1/2, 2.5/6), but if nothing is provided it will default to 1.
```js
async getUserInput => {
try {
const data1 = await $.get(getUserInput('url') + '/api/convert?input=mi');
assert.approximately(data1.initNum, 1, 0.001);
assert.approximately(data1.returnNum, 1.60934, 0.001);
assert.equal(data1.returnUnit, 'km');
const data2 = await $.get(getUserInput('url') + '/api/convert?input=1/5mi');
assert.approximately(data2.initNum, 1 / 5, 0.1);
assert.approximately(data2.returnNum, 0.32187, 0.001);
assert.equal(data2.returnUnit, 'km');
const data3 = await $.get(
getUserInput('url') + '/api/convert?input=1.5/7km'
);
assert.approximately(data3.initNum, 1.5 / 7, 0.001);
assert.approximately(data3.returnNum, 0.13315, 0.001);
assert.equal(data3.returnUnit, 'mi');
const data4 = await $.get(
getUserInput('url') + '/api/convert?input=3/2.7km'
);
assert.approximately(data4.initNum, 3 / 2.7, 0.001);
assert.approximately(data4.returnNum, 0.69041, 0.001);
assert.equal(data4.returnUnit, 'mi');
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我的返回将包含initNuminitUnitreturnNumreturnUnit和字符串拼写单元,格式为“{initNum} {initial_Units}转换为{returnNum} {return_Units}”结果舍入为字符串中的5位小数。
Your return will consist of the `initNum`, `initUnit`, `returnNum`, `returnUnit`, and `string` spelling out units in the format `'{initNum} {initUnitString} converts to {returnNum} {returnUnitString}'` with the result rounded to 5 decimals.
```js
async getUserInput => {
try {
const data = await $.get(getUserInput('url') + '/api/convert?input=2mi');
assert.equal(data.initNum, 2);
assert.equal(data.initUnit, 'mi');
assert.approximately(data.returnNum, 3.21868, 0.001);
assert.equal(data.returnUnit, 'km', 'returnUnit did not match');
assert.equal(data.string, '2 miles converts to 3.21868 kilometers');
} catch (xhr) {
throw new Error(xhr.responseText || xhr.message);
}
};
```
所有16个单元测试都已完成并通过。
All 16 unit tests are complete and passing.
```js
async getUserInput => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
const unitTests = getTests.filter(test => {
return !!test.context.match(/Unit Tests/gi);
});
assert.isAtLeast(unitTests.length, 16, 'At least 16 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);
}
};
```
所有5个功能测试都已完成并通过。
All 5 functional tests are complete and passing.
```js
async getUserInput => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
const functTests = getTests.filter(test => {
return !!test.context.match(/Functional Tests/gi);
});
assert.isAtLeast(functTests.length, 5, 'At least 5 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);
}
};
```
# --solutions--

View File

@@ -1,75 +1,213 @@
---
id: 587d824a367417b2b2512c43
title: 个人图书馆
title: Personal Library
challengeType: 4
videoUrl: ''
forumTopicId: 301571
dashedName: personal-library
---
# --description--
构建一个功能类似于此的完整堆栈JavaScript应用程序 [https](https://fuzzy-mink.glitch.me/) [//spark-cathedral.glitch.me/](https://fuzzy-mink.glitch.me/) 。在这个项目上工作将涉及您在我们的入门项目上在Glitch上编写代码。完成此项目后您可以将公共故障网址到应用程序的主页复制到此屏幕进行测试您可以选择在另一个平台上编写项目但必须公开显示我们的测试。使用[此链接](https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-library/)在Glitch上启动此项目或在GitHub上克隆[此存储库](https://github.com/freeCodeCamp/boilerplate-project-library/) 如果您使用Glitch请记住将项目链接保存到安全的地方
Build a full stack JavaScript app that is functionally similar to this: <https://personal-library.freecodecamp.rocks/>. Working on this project will involve you writing your code using one of the following methods:
- Clone [this GitHub repo](https://github.com/freeCodeCamp/boilerplate-project-library) and complete your project locally.
- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-library)) to complete your project.
- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your project's source code in the `GitHub Link` field.
# --instructions--
1. Add your MongoDB connection string to `.env` without quotes as `DB`
Example: `DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib`
2. In your `.env` file set `NODE_ENV` to `test`, without quotes
3. You need to create all routes within `routes/api.js`
4. You will create all functional tests in `tests/2_functional-tests.js`
# --hints--
我的网站上的任何内容都不会缓存在我的客户端中。
You can provide your own project, not the example URL.
```js
(getUserInput) => {
assert(
!/.*\/personal-library\.freecodecamp\.rocks/.test(getUserInput('url'))
);
};
```
标题会说该网站由'PHP 4.2.0'提供支持,即使它不是(作为安全措施)。
You can send a <b>POST</b> request to `/api/books` with `title` as part of the form data to add a book. The returned response will be an object with the `title` and a unique `_id` as keys. If `title` is not included in the request, the returned response should be the string `missing required field title`.
```js
async (getUserInput) => {
try {
let data1 = await $.post(getUserInput('url') + '/api/books', {
title: 'Faux Book 1'
});
assert.isObject(data1);
assert.property(data1, 'title');
assert.equal(data1.title, 'Faux Book 1');
assert.property(data1, '_id');
let data2 = await $.post(getUserInput('url') + '/api/books');
assert.isString(data2);
assert.equal(data2, 'missing required field title');
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以将标题发布到/ api / books以添加书籍并返回将是具有标题和唯一\_id的对象。
You can send a <b>GET</b> request to `/api/books` and receive a JSON response representing all the books. The JSON response will be an array of objects with each object (book) containing `title`, `_id`, and `commentcount` properties.
```js
async (getUserInput) => {
try {
let url = getUserInput('url') + '/api/books';
let a = $.post(url, { title: 'Faux Book A' });
let b = $.post(url, { title: 'Faux Book B' });
let c = $.post(url, { title: 'Faux Book C' });
Promise.all([a, b, c]).then(async () => {
let data = await $.get(url);
assert.isArray(data);
assert.isAtLeast(data.length, 3);
data.forEach((book) => {
assert.isObject(book);
assert.property(book, 'title');
assert.isString(book.title);
assert.property(book, '_id');
assert.property(book, 'commentcount');
assert.isNumber(book.commentcount);
});
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以获取/ api / books来检索包含title\_id和commentcount的所有书籍的数组。
You can send a <b>GET</b> request to `/api/books/{_id}` to retrieve a single object of a book containing the properties `title`, `_id`, and a `comments` array (empty array if no comments present). If no book is found, return the string `no book exists`.
```js
async (getUserInput) => {
try {
let url = getUserInput('url') + '/api/books';
let noBook = await $.get(url + '/5f665eb46e296f6b9b6a504d');
assert.isString(noBook);
assert.equal(noBook, 'no book exists');
let sampleBook = await $.post(url, { title: 'Faux Book Alpha' });
assert.isObject(sampleBook);
let bookId = sampleBook._id;
let bookQuery = await $.get(url + '/' + bookId);
assert.isObject(bookQuery);
assert.property(bookQuery, 'title');
assert.equal(bookQuery.title, 'Faux Book Alpha');
assert.property(bookQuery, 'comments');
assert.isArray(bookQuery.comments);
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以使用/ api / books / {id}来检索包含\_title\_id和注释数组的书的单个对象如果没有注释则为空数组
You can send a <b>POST</b> request containing `comment` as the form body data to `/api/books/{_id}` to add a comment to a book. The returned response will be the books object similar to <b>GET</b> `/api/books/{_id}` request in an earlier test. If `comment` is not included in the request, return the string `missing required field comment`. If no book is found, return the string `no book exists`.
```js
async (getUserInput) => {
try {
let url = getUserInput('url') + '/api/books';
let commentTarget = await $.post(url, { title: 'Notable Book' });
assert.isObject(commentTarget);
let bookId = commentTarget._id;
let bookCom1 = await $.post(url + '/' + bookId, {
comment: 'This book is fab!'
});
let bookCom2 = await $.post(url + '/' + bookId, {
comment: 'I did not care for it'
});
assert.isObject(bookCom2);
assert.property(bookCom2, '_id');
assert.property(bookCom2, 'title');
assert.property(bookCom2, 'comments');
assert.lengthOf(bookCom2.comments, 2);
bookCom2.comments.forEach((comment) => {
assert.isString(comment);
assert.oneOf(comment, ['This book is fab!', 'I did not care for it']);
});
let commentErr = await $.post(url + '/' + bookId);
assert.isString(commentErr);
assert.equal(commentErr, 'missing required field comment');
let failingComment = await $.post(url + '/5f665eb46e296f6b9b6a504d', {
comment: 'Never Seen Comment'
});
assert.isString(failingComment);
assert.equal(failingComment, 'no book exists');
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以在/ api / books / {id}上发表评论为书籍添加评论并返回类似于get / api / books / {id}的书籍对象,包括新评论。
You can send a <b>DELETE</b> request to `/api/books/{_id}` to delete a book from the collection. The returned response will be the string `delete successful` if successful. If no book is found, return the string `no book exists`.
```js
async (getUserInput) => {
try {
let url = getUserInput('url') + '/api/books';
let deleteTarget = await $.post(url, { title: 'Deletable Book' });
assert.isObject(deleteTarget);
let bookId = deleteTarget._id;
let doDelete = await $.ajax({ url: url + '/' + bookId, type: 'DELETE' });
assert.isString(doDelete);
assert.equal(doDelete, 'delete successful');
let failingDelete = await $.ajax({
url: url + '/5f665eb46e296f6b9b6a504d',
type: 'DELETE'
});
assert.isString(failingDelete);
assert.equal(failingDelete, 'no book exists');
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
我可以删除/ api / books / {\_ id}从集合中删除一本书。如果成功,返回将“删除成功”。
You can send a <b>DELETE</b> request to `/api/books` to delete all books in the database. The returned response will be the string `'complete delete successful` if successful.
```js
async (getUserInput) => {
try {
const deleteAll = await $.ajax({
url: getUserInput('url') + '/api/books',
type: 'DELETE'
});
assert.isString(deleteAll);
assert.equal(deleteAll, 'complete delete successful');
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
如果我试图要求一本不存在的书,我将被退回'没有书存在'。
All 10 functional tests required are complete and passing.
```js
```
我可以向/ api / books发送删除请求以删除数据库中的所有书籍。如果成功返回将是“完全删除成功”。
```js
```
所有6项功能测试都是完整的并且通过。
```js
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
assert.isAtLeast(getTests.length, 10, 'At least 10 tests passed');
getTests.forEach((test) => {
assert.equal(test.state, 'passed', 'Test in Passed State');
assert.isAtLeast(
test.assertions.length,
1,
'At least one assertion per test'
);
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
# --solutions--

View File

@@ -1,89 +1,351 @@
---
id: 5e601bf95ac9d0ecd8b94afd
title: Sudoku Solver
challengeType: 4
dashedName: sudoku-solver
---
# --description--
Build a full stack JavaScript app that is functionally similar to this: <https://bottlenose-eucalyptus.glitch.me/>.
Build a full stack JavaScript app that is functionally similar to this: <https://sudoku-solver.freecodecamp.rocks/>. Working on this project will involve you writing your code using one of the following methods:
Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicly visible for our testing.
- Clone [this GitHub repo](https://github.com/freecodecamp/boilerplate-project-sudoku-solver) and complete your project locally.
- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-project-sudoku-solver) to complete your project.
- Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.
Start this project on Glitch using [this link](https://glitch.com/edit/#!/remix/clone-from-repo?REPO_URL=https://github.com/freeCodeCamp/boilerplate-project-sudoku-solver/) or clone [this repository](https://github.com/freeCodeCamp/boilerplate-project-sudoku-solver/) on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!
When you are done, make sure a working demo of your project is hosted somewhere public. Then submit the URL to it in the `Solution Link` field. Optionally, also submit a link to your project's source code in the `GitHub Link` field.
# --instructions--
- All puzzle logic can go into `/controllers/sudoku-solver.js`
- The `validate` function should take a given puzzle string and check it to see if it has 81 valid characters for the input.
- The `check` functions should be validating against the *current* state of the board.
- The `solve` function should handle solving any given valid puzzle string, not just the test inputs and solutions. You are expected to write out the logic to solve this.
- All routing logic can go into `/routes/api.js`
- See the `puzzle-strings.js` file in `/controllers` for some sample puzzles your application should solve
- To run the challenge tests on this page, set `NODE_ENV` to `test` without quotes in the `.env` file
- To run the tests in the console, use the command `npm run test`. To open the Repl.it console, press Ctrl+Shift+P (Cmd if on a Mac) and type "open shell"
Write the following tests in `tests/1_unit-tests.js`:
- Logic handles a valid puzzle string of 81 characters
- Logic handles a puzzle string with invalid characters (not 1-9 or `.`)
- Logic handles a puzzle string that is not 81 characters in length
- Logic handles a valid row placement
- Logic handles an invalid row placement
- Logic handles a valid column placement
- Logic handles an invalid column placement
- Logic handles a valid region (3x3 grid) placement
- Logic handles an invalid region (3x3 grid) placement
- Valid puzzle strings pass the solver
- Invalid puzzle strings fail the solver
- Solver returns the the expected solution for an incomplete puzzzle
Write the following tests in `tests/2_functional-tests.js`
- Solve a puzzle with valid puzzle string: POST request to `/api/solve`
- Solve a puzzle with missing puzzle string: POST request to `/api/solve`
- Solve a puzzle with invalid characters: POST request to `/api/solve`
- Solve a puzzle with incorrect length: POST request to `/api/solve`
- Solve a puzzle that cannot be solved: POST request to `/api/solve`
- Check a puzzle placement with all fields: POST request to `/api/check`
- Check a puzzle placement with single placement conflict: POST request to `/api/check`
- Check a puzzle placement with multiple placement conflicts: POST request to `/api/check`
- Check a puzzle placement with all placement conflicts: POST request to `/api/check`
- Check a puzzle placement with missing required fields: POST request to `/api/check`
- Check a puzzle placement with invalid characters: POST request to `/api/check`
- Check a puzzle placement with incorrect length: POST request to `/api/check`
- Check a puzzle placement with invalid placement coordinate: POST request to `/api/check`
- Check a puzzle placement with invalid placement value: POST request to `/api/check`
# --hints--
I can enter a sudoku puzzle by filling in the text area with either a number or period (.) to represent an empty cell.
You should provide your own project, not the example URL.
```js
(getUserInput) => {
const url = getUserInput('url');
assert(!/.*\/sudoku-solver\.freecodecamp\.rocks/.test(getUserInput('url')));
};
```
When a valid number is entered in the text area, the same number is applied to the correct cell of the sudoku grid.
You can `POST` `/api/solve` with form data containing `puzzle` which will be a string containing a combination of numbers (1-9) and periods `.` to represent empty spaces. The returned object will contain a `solution` property with the solved puzzle.
```js
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output =
'769235418851496372432178956174569283395842761628713549283657194516924837947381625';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'solution');
assert.equal(parsed.solution, output);
};
```
I can enter a sudoku puzzle by adding numbers directly to the sudoku grid.
If the object submitted to `/api/solve` is missing `puzzle`, the returned value will be `{ error: 'Required field missing' }`
```js
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Required field missing';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ notpuzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
When a valid number is entered in the sudoku grid, the same number appears in the correct position in the text area.
If the puzzle submitted to `/api/solve` contains values which are not numbers or periods, the returned value will be `{ error: 'Invalid characters in puzzle' }`
```js
async (getUserInput) => {
const input =
'AA9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Invalid characters in puzzle';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
The text area should only update the corresponding sudoku grid cell when a whole number between 1 and 9 is entered.
If the puzzle submitted to `/api/solve` is greater or less than 81 characters, the returned value will be `{ error: 'Expected puzzle to be 81 characters long' }`
```js
async (getUserInput) => {
const input =
'9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Expected puzzle to be 81 characters long';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
The sudoku grid should only update the puzzle string in the text area when a whole number between 1 and 9 is entered into a cell.
If the puzzle submitted to `/api/solve` is invalid or cannot be solved, the returned value will be `{ error: 'Puzzle cannot be solved' }`
```js
async (getUserInput) => {
const input =
'9.9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Puzzle cannot be solved';
const data = await fetch(getUserInput('url') + '/api/solve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
I can solve an incomplete puzzle by clicking the "Solve" button. When a solution is found, the sudoku grid and text area are automatically populated with the correct numbers for each cell in the grid or position in the text area.
You can `POST` to `/api/check` an object containing `puzzle`, `coordinate`, and `value` where the `coordinate` is the letter A-I indicating the row, followed by a number 1-9 indicating the column, and `value` is a number from 1-9.
```js
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'A1';
const value = '7';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'valid');
assert.isTrue(parsed.valid);
};
```
This sudoku solver is not expected to be able to solve every incomplete puzzle. See `/public/puzzle-strings.js` for a list of puzzle strings it should be able to solve along with their solutions.
The return value from the `POST` to `/api/check` will be an object containing a `valid` property, which is `true` if the number may be placed at the provided coordinate and `false` if the number may not. If false, the returned object will also contain a `conflict` property which is an array containing the strings `"row"`, `"column"`, and/or `"region"` depending on which makes the placement invalid.
```js
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'A1';
const value = '1';
const conflict = ['row', 'column'];
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'valid');
assert.isFalse(parsed.valid);
assert.property(parsed, 'conflict');
assert.include(parsed.conflict, 'row');
assert.include(parsed.conflict, 'column');
};
```
If the puzzle is not 81 numbers or periods long, append the message "Error: Expected puzzle to be 81 characters long." to the `error-msg` `div` so the text appears in red.
If the puzzle submitted to `/api/check` contains values which are not numbers or periods, the returned value will be `{ error: 'Invalid characters in puzzle' }`
```js
async (getUserInput) => {
const input =
'AA9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'A1';
const value = '1';
const output = 'Invalid characters in puzzle';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
I can clear the text area and sudoku grid by clicking the "Clear" button.
If the puzzle submitted to `/api/check` is greater or less than 81 characters, the returned value will be `{ error: 'Expected puzzle to be 81 characters long' }`
```js
async (getUserInput) => {
const input =
'9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const coordinate = 'A1';
const value = '1';
const output = 'Expected puzzle to be 81 characters long';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
All 6 unit tests are complete and passing. See `/tests/1_unit-tests.js` for the expected behavior you should write tests for.
If the object submitted to `/api/check` is missing `puzzle`, `coordinate` or `value`, the returned value will be `{ error: Required field(s) missing }`
```js
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Required field(s) missing';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
All 4 functional tests are complete and passing. See `/tests/2_functional-tests.js` for the functionality you should write tests for.
If the coordinate submitted to `api/check` does not point to an existing grid cell, the returned value will be `{ error: 'Invalid coordinate'}`
```js
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Invalid coordinate';
const coordinate = 'XZ18';
const value = '7';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
If the `value` submitted to `/api/check` is not a number between 1 and 9, the returned values will be `{ error: 'Invalid value' }`
```js
async (getUserInput) => {
const input =
'..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
const output = 'Invalid value';
const coordinate = 'A1';
const value = 'X';
const data = await fetch(getUserInput('url') + '/api/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: input, coordinate, value })
});
const parsed = await data.json();
assert.property(parsed, 'error');
assert.equal(parsed.error, output);
};
```
All 12 unit tests are complete and passing. See `/tests/1_unit-tests.js` for the expected behavior you should write tests for.
```js
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
const units = getTests.filter((el) => el.context.includes('UnitTests'));
assert.isAtLeast(units.length, 12, 'At least 12 tests passed');
units.forEach((test) => {
assert.equal(test.state, 'passed', 'Test 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 14 functional tests are complete and passing. See `/tests/2_functional-tests.js` for the functionality you should write tests for.
```js
async (getUserInput) => {
try {
const getTests = await $.get(getUserInput('url') + '/_api/get-tests');
assert.isArray(getTests);
const funcs = getTests.filter((el) =>
el.context.includes('Functional Tests')
);
assert.isAtLeast(funcs.length, 14, 'At least 14 tests passed');
funcs.forEach((test) => {
assert.equal(test.state, 'passed', 'Test in Passed State');
assert.isAtLeast(
test.assertions.length,
1,
'At least one assertion per test'
);
});
} catch (err) {
throw new Error(err.responseText || err.message);
}
};
```
# --solutions--