Back to Freecodecamp

Personal Library

curriculum/challenges/english/blocks/quality-assurance-projects/587d824a367417b2b2512c43.md

latest10.8 KB
Original Source

--description--

Build a full-stack JavaScript app that is functionally similar to this: <a href="https://personal-library.freecodecamp.rocks/" target="_blank" rel="noopener noreferrer nofollow">https://personal-library.freecodecamp.rocks/</a>. 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-library" target="_blank" rel="noopener noreferrer nofollow">this GitHub repo</a> and complete your project locally.
  • Use a site builder of your choice to complete the project. Be sure to incorporate all the files from our GitHub repo.

--instructions--

  1. Add your MongoDB connection string to .env without quotes as DB Example: DB=mongodb://admin:[email protected]: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
  assert(
    !/.*\/personal-library\.freecodecamp\.rocks/.test(code)
  );

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
  try {
    const response1 = await fetch(code + '/api/books', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Faux Book 1' })
    });
    if (!response1.ok) {
      throw Error(await response1.text());
    }
    let data1 = await response1.json();
    assert.isObject(data1);
    assert.property(data1, 'title');
    assert.equal(data1.title, 'Faux Book 1');
    assert.property(data1, '_id');
    const response2 = await fetch(code + '/api/books', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({})
    });
    if (!response2.ok) {
      throw Error(await response2.text());
    }
    let data2 = await response2.text();
    assert.isString(data2);
    assert.equal(data2, 'missing required field title');
  } catch (err) {
    throw new Error(err.responseText || err.message);
  }

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
  try {
    let url = code + '/api/books';
    let a = fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Faux Book A' })
    }).then(response => {
      if (!response.ok) throw Error(response.text());
      return response.json();
    });
    let b = fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Faux Book B' })
    }).then(response => {
      if (!response.ok) throw Error(response.text());
      return response.json();
    });
    let c = fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Faux Book C' })
    }).then(response => {
      if (!response.ok) throw Error(response.text());
      return response.json();
    });
    await Promise.all([a, b, c]).then(async () => {
      const response = await fetch(url);
      if (!response.ok) {
        throw Error(await response.text());
      }
      let data = await response.json();
      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);
  }

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
  try {
    let url = code + '/api/books';
    const noBookResponse = await fetch(url + '/5f665eb46e296f6b9b6a504d');
    if (!noBookResponse.ok) {
      throw Error(await noBookResponse.text());
    }
    let noBook = await noBookResponse.text();
    assert.isString(noBook);
    assert.equal(noBook, 'no book exists');
    const createResponse = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Faux Book Alpha' })
    });
    if (!createResponse.ok) {
      throw Error(await createResponse.text());
    }
    let sampleBook = await createResponse.json();
    assert.isObject(sampleBook);
    let bookId = sampleBook._id;
    const queryResponse = await fetch(url + '/' + bookId);
    if (!queryResponse.ok) {
      throw Error(await queryResponse.text());
    }
    let bookQuery = await queryResponse.json();
    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);
  }

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
  try {
    let url = code + '/api/books';
    const createResponse = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Notable Book' })
    });
    if (!createResponse.ok) {
      throw Error(await createResponse.text());
    }
    let commentTarget = await createResponse.json();
    assert.isObject(commentTarget);
    let bookId = commentTarget._id;
    const comment1Response = await fetch(url + '/' + bookId, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ comment: 'This book is fab!' })
    });
    if (!comment1Response.ok) {
      throw Error(await comment1Response.text());
    }
    let bookCom1 = await comment1Response.json();
    const comment2Response = await fetch(url + '/' + bookId, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ comment: 'I did not care for it' })
    });
    if (!comment2Response.ok) {
      throw Error(await comment2Response.text());
    }
    let bookCom2 = await comment2Response.json();
    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']);
    });
    const commentErrResponse = await fetch(url + '/' + bookId, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({})
    });
    if (!commentErrResponse.ok) {
      throw Error(await commentErrResponse.text());
    }
    let commentErr = await commentErrResponse.text();
    assert.isString(commentErr);
    assert.equal(commentErr, 'missing required field comment');
    const failingCommentResponse = await fetch(url + '/5f665eb46e296f6b9b6a504d', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ comment: 'Never Seen Comment' })
    });
    if (!failingCommentResponse.ok) {
      throw Error(await failingCommentResponse.text());
    }
    let failingComment = await failingCommentResponse.text();
    assert.isString(failingComment);
    assert.equal(failingComment, 'no book exists');
  } catch (err) {
    throw new Error(err.responseText || err.message);
  }

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
  try {
    let url = code + '/api/books';
    const createResponse = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Deletable Book' })
    });
    if (!createResponse.ok) {
      throw Error(await createResponse.text());
    }
    let deleteTarget = await createResponse.json();
    assert.isObject(deleteTarget);
    let bookId = deleteTarget._id;
    const deleteResponse = await fetch(url + '/' + bookId, { method: 'DELETE' });
    if (!deleteResponse.ok) {
      throw Error(await deleteResponse.text());
    }
    let doDelete = await deleteResponse.text();
    assert.isString(doDelete);
    assert.equal(doDelete, 'delete successful');
    const failingDeleteResponse = await fetch(url + '/5f665eb46e296f6b9b6a504d', { method: 'DELETE' });
    if (!failingDeleteResponse.ok) {
      throw Error(await failingDeleteResponse.text());
    }
    let failingDelete = await failingDeleteResponse.text();
    assert.isString(failingDelete);
    assert.equal(failingDelete, 'no book exists');
  } catch (err) {
    throw new Error(err.responseText || err.message);
  }

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
  try {
    const response = await fetch(code + '/api/books', { method: 'DELETE' });
    if (!response.ok) {
      throw Error(await response.text());
    }
    const deleteAll = await response.text();
    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
  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);
    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);
  }