freeCodeCamp/curriculum/challenges/chinese/06-quality-assurance/quality-assurance-projects/personal-library.md

7.9 KiB
Raw Blame History

id title challengeType forumTopicId dashedName
587d824a367417b2b2512c43 个人图书馆 4 301571 personal-library

--description--

构建一个 JavaScript 的全栈应用,在功能上与这个应用相似:https://personal-library.freecodecamp.rocks/。 可以采用下面的任意一种方式完成这个挑战:

完成本项目后,请将一个正常运行的 demo项目演示托管在可以公开访问的平台。 然后在 Solution Link 框中提交你的项目 URL。 此外,还可以将项目的源码提交到 GitHub Link 中。

--instructions--

  1. 将的 MongoDB 连接字符串添加到 .env 中(没有引号),DB
    示例: DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib
  2. .env 文件中设置 NODE_ENVtest中,没有引号
  3. 需要在 routes/api.js 中创建所有路由
  4. tests/2_functional-tests.js 中创建所有的功能测试

--hints--

提交自己的项目,而不是示例的 URL。

(getUserInput) => {
  assert(
    !/.*\/personal-library\.freecodecamp\.rocks/.test(getUserInput('url'))
  );
};

可以发送 POST 请求到 /api/books,带有 title 作为表单数据的一部分,来添加一本书。 返回的响应将是一个包含 title 和唯一的 _id 作为键的对象。 如果 title 未包含在请求中,返回的响应应该是字符串 missing required field title

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 发送 GET 请求,并返回代表所有书的 JSON 响应。 JSON 响应应该是一个包含有 title_idcommentcount 属性的对象数组 。

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);
  }
};

可以发送 GET 请求到 /api/books/{_id} 来检索一本书的单个对象,返回属性 title_idcomments 数组 (如果没有评论,则展示空数组)。 如果找不到书, 返回字符串 no book exists

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);
  }
};

可以发送一个 POST 请求,其中包含 comment 作为表单正文数据,请求到 /api/books/{_id} 以便将评论添加到书中。 返回的响应将是书对象,在先前测试中 GET /api/books/{_id} 类似。 如果请求中没有包含 comment ,返回字符串 missing required field comment。 如果找不到书, 返回字符串 no book exists

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} 发送 DELETE 请求,从收藏中删除一本书。 如果成功,返回的响应将是字符串 delete successful。 如果找不到书, 返回字符串 no book exists

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 发送 DELETE 请求来删除数据库中的所有书籍。 如果成功,返回的响应将是字符串 'complete delete successful

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);
  }
};

所有 10 项功能测试都已完成并通过。

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--

/**
  Backend challenges don't need solutions, 
  because they would need to be tested against a full working project. 
  Please check our contributing guidelines to learn more.
*/