diff --git a/curriculum/challenges/english/06-quality-assurance/quality-assurance-projects/personal-library.md b/curriculum/challenges/english/06-quality-assurance/quality-assurance-projects/personal-library.md
index 702c1a4c7f..9a2d1969e1 100644
--- a/curriculum/challenges/english/06-quality-assurance/quality-assurance-projects/personal-library.md
+++ b/curriculum/challenges/english/06-quality-assurance/quality-assurance-projects/personal-library.md
@@ -7,14 +7,26 @@ forumTopicId: 301571
## Description
-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 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 must be publicly visible for our testing.
-Start this project on Repl.it using this link or clone this repository on GitHub! If you use Repl.it, remember to save the link to your project somewhere safe!
+
+Build a full stack JavaScript app that is functionally similar to [this site](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-personal-library) and complete your project locally.
+- Use [our repl.it starter project](https://repl.it/github/freeCodeCamp/boilerplate-personal-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`
+
## Tests
@@ -22,28 +34,139 @@ Start this project on Repl.it using {
+ 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);
+ }
+ }"
+ - text: You can send a GET 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.
+ testString: "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);
+ }
+ }"
+ - text: You can send a GET 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
.
+ testString: "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);
+ }
+ }"
+ - text: You can send a POST 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 GET /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
.
+ testString: "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);
+ }
+ }"
+ - text: You can send a DELETE 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
.
+ testString: "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);
+ }
+ }"
+ - text: You can send a DELETE request to /api/books
to delete all books in the database. The returned response will be the string 'complete delete successful
if successful.
+ testString: "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);
+ }
+ }"
+ - text: All 10 functional tests required are complete and passing.
+ testString: "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);
+ }
+ }"
```