feat(learn): Add Tests to Issue Tracker Project, Remove InfoSec Tests (#39627)

* Feature: Add Tests to Issue Tracker Project

* Refactored User Stories

* Apply suggestions from code review

Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>

* Modify verbiage for #39721

Signed-off-by: nhcarrigan <nhcarrigan@gmail.com>

Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
This commit is contained in:
Rex Schrader
2020-11-12 19:51:09 -08:00
committed by GitHub
parent bd593667f6
commit 5a9680a1a6

View File

@ -8,13 +8,25 @@ forumTopicId: 301569
## Description
<section id='description'>
Build a full stack JavaScript app that is functionally similar to this: <a href="https://issue-tracker.freecodecamp.rocks/" target="_blank">https://issue-tracker.freecodecamp.rocks/</a>.
Working on this project will involve you writing your code on Repl.it on our starter project. After completing this project you can copy your public Repl.it 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.
Start this project on Repl.it using <a href="https://repl.it/github/freeCodeCamp/boilerplate-project-issuetracker">this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-issuetracker/'>this repository</a> on GitHub! If you use Repl.it, remember to save the link to your project somewhere safe!
Working on this project will involve you writing your code using one of the following methods:
- Clone <a href='https://github.com/freeCodeCamp/boilerplate-project-issuetracker/'>this GitHub repo</a> and complete your project locally.
- Use <a href="https://repl.it/github/freeCodeCamp/boilerplate-project-issuetracker">this repl.it starter project</a> 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.
</section>
## Instructions
<section id='instructions'>
- Complete the neccessary 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"
</section>
## Tests
@ -22,26 +34,244 @@ Start this project on Repl.it using <a href="https://repl.it/github/freeCodeCamp
```yml
tests:
- text: I can provide my own project, not the example URL.
testString: |
- text: You can provide your own project, not the example URL.
testString: "
getUserInput => {
assert(!/.*\/issue-tracker\.freecodecamp\.rocks/.test(getUserInput('url')));
assert(!/.*\\/issue-tracker\\.freecodecamp\\.rocks/.test(getUserInput('url')));
}"
- text: 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`.
testString: '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);
}
- text: I can POST /api/issues/{projectname} with form data containing required issue_title, issue_text, created_by, and optional assigned_to and status_text.
testString: ''
- text: The object saved (and returned) will include all of those fields (blank for optional no input) and also include created_on(date/time), updated_on(date/time), open(boolean, true for open, false for closed), and _id.
testString: ''
- text: I can PUT /api/issues/{projectname} with a id and any fields in the object with a value to object said object. Returned will be 'successfully updated' or 'could not update '+id. This should always update updated_on. If no fields are sent return 'no updated field sent'.
testString: ''
- text: 'I can DELETE /api/issues/{projectname} with a id to completely delete an issue. If no _id is sent return ''id error'', success: ''deleted ''+id, failed: ''could not delete ''+id.'
testString: ''
- text: I can GET /api/issues/{projectname} for an array of all issues on that specific project with all the information for each issue as was returned when posted.
testString: ''
- text: I can filter my get request by also passing along any field and value in the query(ie. /api/issues/{project}?open=false). I can pass along as many fields/values as I want.
testString: ''
- text: All 11 functional tests are complete and passing.
testString: ''
}'
- text: 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`.
testString: '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);
}
}'
- text: If you send a `POST` request to `/api/issues/{projectname}` without the required fields, returned will be the error `{ error: 'required field(s) missing' }`
testString: '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);
}
}'
- text: 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.
testString: '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);
}
}'
- text: 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.
testString: '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);
}
}'
- text: 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 }`.
testString: '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);
}
}'
- text: When the `PUT` request sent to `/api/issues/{projectname}` does not include an `_id`, the return value is `{ error: 'missing _id' }`.
testString: '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);
}
}'
- text: 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 }`.
testString: '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);
}
}'
- text: 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 }`.
testString: '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);
}
}'
- text: All 14 functional tests are complete and passing.
testString: '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);
}
}'
```
</section>