chore(i18n,learn): processed translations (#44851)
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: 589fc832f9fc0f352b528e78
|
||||
title: 新しいユーザーを通知する
|
||||
challengeType: 2
|
||||
forumTopicId: 301546
|
||||
dashedName: announce-new-users
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
多くのチャットルームは、ユーザーが接続または切断したときに、チャットにいる他のすべての接続中ユーザーにそのことを通知して表示することができます。 接続時と切断時にすでにイベントをエミットしているはずなので、このような機能をサポートするようにこのイベントを変更するだけです。 そのための最も論理的な方法として、イベントに伴う 3 つのデータを送信します。それらは、接続/切断したユーザーの名前、現在のユーザー数、および、その名前が接続されているか切断されているかを示すデータです。
|
||||
|
||||
イベント名を `'user'` に変更してください、そして、フィールド 「name」、「currentUsers」および「connected」(送信されたユーザーが接続する場合は `true`、切断する場合は `false`) を含むオブジェクトを渡してください。 必ず両方の「user count」イベントを変更し、切断イベントではフィールド 「connected」に対して、接続時にイベントが発生するような `true` ではなく `false` を送信するように設定してください。
|
||||
|
||||
```js
|
||||
io.emit('user', {
|
||||
name: socket.request.user.name,
|
||||
currentUsers,
|
||||
connected: true
|
||||
});
|
||||
```
|
||||
|
||||
これで必要なすべての情報がクライアントに揃い、ユーザーが接続または切断したときに現在のユーザー数と通知が正しく表示されます! クライアント側でこのイベントを処理するには、`'user'` をリッスンし、それから jQuery を使用して現在のユーザー数を更新し、`#num-users` のテキストを `'{NUMBER} users online'` に変更する必要があります。また、id が `messages` の順序なしリストに、`'{NAME} has {joined/left} the chat.'` という内容の `<li>` を追加します。
|
||||
|
||||
この実装は次のようになります。
|
||||
|
||||
```js
|
||||
socket.on('user', data => {
|
||||
$('#num-users').text(data.currentUsers + ' users online');
|
||||
let message =
|
||||
data.name +
|
||||
(data.connected ? ' has joined the chat.' : ' has left the chat.');
|
||||
$('#messages').append($('<li>').html('<b>' + message + '</b>'));
|
||||
});
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/bf95a0f74b756cf0771cd62c087b8286)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
イベント `'user'` を、name、currentUsers および connected とともにエミットする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/io.emit.*('|")user\1.*name.*currentUsers.*connected/gis,
|
||||
'You should have an event emitted named user sending name, currentUsers, and connected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
クライアントは、イベント `'user'` の新しいデータを適切に処理して表示する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/public/client.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/socket.on.*('|")user\1[^]*num-users/gi,
|
||||
'You should change the text of "#num-users" within on your client within the "user" event listener to show the current users connected'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/socket.on.*('|")user\1[^]*messages.*li/gi,
|
||||
'You should append a list item to "#messages" on your client within the "user" event listener to announce a user came or went'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,96 @@
|
||||
---
|
||||
id: 5895f70df9fc0f352b528e68
|
||||
title: 認証ストラテジー
|
||||
challengeType: 2
|
||||
forumTopicId: 301547
|
||||
dashedName: authentication-strategies
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ストラテジーとは、ユーザーを認証する方法です。 ローカルに保存された情報に基づいてユーザーを認証できるようにするためのストラテジーを使用したり (初めてユーザーに登録してもらう場合)、Google や GitHub などのさまざまなプロバイダーからユーザーを認証できるようにするためのストラテジーを使用したりできます。 このプロジェクトでは、ローカルストラテジーを設定します。 何百ものストラテジーのリストを見るには、Passportの[こちら](http://passportjs.org/)のサイトにアクセスしてください。
|
||||
|
||||
`passport-local@~1.0.0` を依存関係として追加し、次のようにサーバーに追加します: `const LocalStrategy = require('passport-local');`
|
||||
|
||||
ここでPassportに対して、いくつかの設定を定義してインスタンス化した LocalStrategy オブジェクトを **use** するよう伝える必要があります。 このオブジェクト (および今後すべてのもの) はデータベース接続に依存するため、必ずデータベース接続でカプセル化してください!
|
||||
|
||||
```js
|
||||
passport.use(new LocalStrategy(
|
||||
function(username, password, done) {
|
||||
myDataBase.findOne({ username: username }, function (err, user) {
|
||||
console.log('User '+ username +' attempted to log in.');
|
||||
if (err) { return done(err); }
|
||||
if (!user) { return done(null, false); }
|
||||
if (password !== user.password) { return done(null, false); }
|
||||
return done(null, user);
|
||||
});
|
||||
}
|
||||
));
|
||||
```
|
||||
|
||||
ここでは、ローカルで誰かを認証しようとするときに使用するプロセスを定義しています。 まず、入力されたユーザー名を使用してデータベースでユーザーの検索を試みた後、パスワードが一致するかどうかを確認します。そして、パスワード間違いなどのエラーが検出されなければ、`user` のオブジェクトが返されて認証されます。
|
||||
|
||||
多くのストラテジーは異なる設定を使用して設定されていますが、通常は、そのストラテジーのリポジトリにある README に基づいて設定するのが簡単です。 良い例として GitHub ストラテジーがあります。このストラテジーでは、ユーザーは GitHub の認証ページに送信されて認証されるため、ユーザー名やパスワードのことを気にする必要がありません。 ユーザーがログインして同意している限り、GitHub は、私たちが使用できるようにユーザーのプロファイルを返します。
|
||||
|
||||
次のステップでは、フォームデータに基づいてユーザーを検証するための認証ストラテジーを実際に呼び出す方法を設定します!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
Passport-local を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'passport-local',
|
||||
'Your project should list "passport-local " as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Passport-local を正しく require して設定する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/require.*("|')passport-local("|')/gi,
|
||||
'You should have required passport-local'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/new LocalStrategy/gi,
|
||||
'You should have told passport to use a new strategy'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/findOne/gi,
|
||||
'Your new local strategy should use the findOne query to find a username based on the inputs'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,154 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e77
|
||||
title: Socket.IOによる認証
|
||||
challengeType: 2
|
||||
forumTopicId: 301548
|
||||
dashedName: authentication-with-socket-io
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
今の時点で、誰がウェブソケットに接続しているかを判断することはできません。 `req.user` にはユーザーオブジェクトが含まれていますが、そうなっているのはユーザーがウェブサーバーとやり取りするときだけであり、ウェブソケットでは `req` (リクエスト) がないため、ユーザーデータはありません。 誰がウェブソケットに接続しているのかを知るための方法の 1 つとして、Passport セッションを含む Cookie を解析してデコードした後、デシリアライズしてユーザーオブジェクトを取得することができます。 幸い、 NPM にはまさにこのためのパッケージがあり、複雑な作業をシンプルにしてくれます!
|
||||
|
||||
`passport.socketio@~3.7.0`、`connect-mongo@~3.2.0` および `cookie-parser@~1.4.5` を依存関係として追加し、それらをそれぞれ `passportSocketIo`、`MongoStore` および `cookieParser` として require してください。 また、前に require した `express-session` から、新しいメモリストアを初期化する必要があります。 次のようになります。
|
||||
|
||||
```js
|
||||
const MongoStore = require('connect-mongo')(session);
|
||||
const URI = process.env.MONGO_URI;
|
||||
const store = new MongoStore({ url: URI });
|
||||
```
|
||||
|
||||
あとは、Socket.IO にそれを使用してオプションを設定するように伝えるだけです。 このコードは必ず既存のソケットコードの前に追加し、既存のコネクションリスナーには追加しないでください。 サーバーでは次のようになります。
|
||||
|
||||
```js
|
||||
io.use(
|
||||
passportSocketIo.authorize({
|
||||
cookieParser: cookieParser,
|
||||
key: 'express.sid',
|
||||
secret: process.env.SESSION_SECRET,
|
||||
store: store,
|
||||
success: onAuthorizeSuccess,
|
||||
fail: onAuthorizeFail
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
Socket.IO の Passport 認証の設定は、API の `session` ミドルウェアの設定方法によく似ています。 これは、同じ認証方式の使用を意図しているためです。つまり、Cookie からセッション id を取得して検証します。
|
||||
|
||||
前に `session` ミドルウェアを設定したときは、セッションの Cookie 名 (`key`) を明示的に設定してはいませんでした。 これは、`session` パッケージでデフォルト値を使用していたためです。 これで、Cookie から同じ値にアクセスする必要がある別のパッケージが追加されました。両方の設定オブジェクトへ明示的に `key` の値を設定する必要があります。
|
||||
|
||||
Socket.IO キーに一致する `session` ミドルウェアに、`key` と Cookie 名を追加してください。 また、`saveUninitialized: true` を設定した近くで、`store` 参照をオプションに追加してください。 これは、どのセッションに関連付けるかを Socket.IO に伝えるために必要です。
|
||||
|
||||
<hr />
|
||||
|
||||
次に、`success` と `fail` コールバック関数を定義します。
|
||||
|
||||
```js
|
||||
function onAuthorizeSuccess(data, accept) {
|
||||
console.log('successful connection to socket.io');
|
||||
|
||||
accept(null, true);
|
||||
}
|
||||
|
||||
function onAuthorizeFail(data, message, error, accept) {
|
||||
if (error) throw new Error(message);
|
||||
console.log('failed connection to socket.io:', message);
|
||||
accept(null, false);
|
||||
}
|
||||
```
|
||||
|
||||
これでソケットオブジェクトでユーザーオブジェクトに `socket.request.user` としてアクセスできるようになりました。 たとえば、次のように追加できます。
|
||||
|
||||
```js
|
||||
console.log('user ' + socket.request.user.name + ' connected');
|
||||
```
|
||||
|
||||
接続したサーバーコンソールにログインします!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、[こちら](https://gist.github.com/camperbot/1414cc9433044e306dd7fd0caa1c6254)でここまでのプロジェクトを確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
`passport.socketio` を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'passport.socketio',
|
||||
'Your project should list "passport.socketio" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`cookie-parser` を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'cookie-parser',
|
||||
'Your project should list "cookie-parser" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
passportSocketIo を適切に require する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/require\((['"])passport\.socketio\1\)/gi,
|
||||
'You should correctly require and instantiate "passport.socketio"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
passportSocketIo を正しく設定する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/io\.use\(\s*\w+\.authorize\(/,
|
||||
'You should register "passport.socketio" as socket.io middleware and provide it correct options'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: 589690e6f9fc0f352b528e6e
|
||||
title: モジュールを使用してプロジェクトを整理する
|
||||
challengeType: 2
|
||||
forumTopicId: 301549
|
||||
dashedName: clean-up-your-project-with-modules
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ここまで作成したものはすべて `server.js` ファイルにあります。 このままではコードが管理しずらくなって、あまり拡張できなくなる可能性があります。 `routes.js` と `auth.js` という 2 つの新しいファイルを作成してください。
|
||||
|
||||
どちらも次のコードから始める必要があります。
|
||||
|
||||
```js
|
||||
module.exports = function (app, myDataBase) {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
サーバーファイルの先頭で `const routes = require('./routes.js');` のようにしてファイルを require します。データベースとの接続を正常に確立した直後に、`routes(app, myDataBase)` のようにしてそれぞれをインスタンス化します。
|
||||
|
||||
さらに、サーバー内のすべてのルートを新しいファイルに貼り付け、サーバーファイルからそれらのルートを削除します。 `ensureAuthenticated` 関数についても、ルーティングのために特別に作成したものなので貼り付けます。 ここで、使用している `const passport = require('passport');` などの依存関係を `routes.js` ファイルのエクスポート行の先頭に正しく追加する必要があります。
|
||||
|
||||
以上の追加を、エラーがなくなりサーバーファイルにルーティングがなくなるまで続けます (**catchブロック内のルートは除きます**)。
|
||||
|
||||
続いて auth.js ファイルでも同じ作業をしてください。ファイルには、シリアル化やローカルストラテジーの設定など、認証に関連するすべてのものが含まれています。そして、サーバーファイルからそれらを消去してください。 サーバーの同じ場所で、依存関係を追加し、`auth(app, myDataBase)` を呼び出してください。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、完了したプロジェクトを[こちら](https://gist.github.com/camperbot/2d06ac5c7d850d8cf073d2c2c794cc92)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
モジュールが存在する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/require\s*\(('|")\.\/routes(\.js)?\1\)/gi,
|
||||
'You should have required your new files'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/client\s*\.db[^]*routes/gi,
|
||||
'Your new modules should be called after your connection to the database'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,107 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e75
|
||||
title: エミットで通信する
|
||||
challengeType: 2
|
||||
forumTopicId: 301550
|
||||
dashedName: communicate-by-emitting
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<dfn>エミット (emit)</dfn> は、最もよく使用する通信方法です。 サーバーから 「io」へ何かをエミットするときは、接続中のすべてのソケットにイベントの名前とデータを送信します。 エミットの良い例として、たとえば新しいユーザーが接続するたびに、接続したユーザーの現在の数をエミットすることができます。
|
||||
|
||||
まず、現在接続をリッスンしている場所の直前に、ユーザーを追跡する変数を追加してください。
|
||||
|
||||
```js
|
||||
let currentUsers = 0;
|
||||
```
|
||||
|
||||
誰かが接続したときは、ユーザー数をインクリメントしてからその数をエミットする必要があります。 そこで、コネクションリスナー内にインクリメンターを追加します。
|
||||
|
||||
```js
|
||||
++currentUsers;
|
||||
```
|
||||
|
||||
また、ユーザー数をインクリメントした後にイベントをエミットする必要があります (これもコレクションリスナー内で行います) 。 イベント名は「user count」、データは `currentUsers` としてください。
|
||||
|
||||
```js
|
||||
io.emit('user count', currentUsers);
|
||||
```
|
||||
|
||||
これで、クライアントでこのイベントをリッスンする方法を実装できました! サーバーの接続をリッスンするのと同様に、`on` キーワードを使用します。
|
||||
|
||||
```js
|
||||
socket.on('user count', function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
```
|
||||
|
||||
ここで、アプリを読み込んで認証を実行してみてください。クライアントコンソールに、現在のユーザー数である「1」が表示されるはずです。 より多くのクライアントを読み込んで認証し、数字が増えるのを確認してください。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/28ef7f1078f56eb48c7b1aeea35ba1f5)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
currentUsers を定義する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/currentUsers/gi,
|
||||
'You should have variable currentUsers defined'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
新しい接続ごとにサーバーから現在のユーザー数をエミットする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/io.emit.*('|")user count('|").*currentUsers/gi,
|
||||
'You should emit "user count" with data currentUsers'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
クライアントでは「user count」イベントをリッスンしている必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/public/client.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/socket.on.*('|")user count('|")/gi,
|
||||
'Your client should be connection to server with the connection defined as socket'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: 5895f70df9fc0f352b528e6a
|
||||
title: 新しいミドルウェアを作成する
|
||||
challengeType: 2
|
||||
forumTopicId: 301551
|
||||
dashedName: create-new-middleware
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
通常、ユーザーは認証されていてもいなくても、 url を入力することで `/profile` にアクセスできます。 ここではこの動作を防ぐために、プロファイルページをレンダーする前に、ユーザーが認証されているかどうかを先に確認する必要があるとします。 こうした例に最適なのがミドルウェアです。
|
||||
|
||||
ここでのチャレンジは、ミドルウェア関数 `ensureAuthenticated(req, res, next)` を作成することです。この関数は、`request` 時に Passport の `isAuthenticated` メソッドを呼び出してユーザーが認証されているかどうかを確認します。メソッドでは、`req.user` が定義されているかどうかを確認します。 定義されている場合は、`next()` を呼び出します。定義されていない場合は、ホームページにリダイレクトすることでログインリクエストに応答します。 このミドルウェアの実装は次のようになります。
|
||||
|
||||
```js
|
||||
function ensureAuthenticated(req, res, next) {
|
||||
if (req.isAuthenticated()) {
|
||||
return next();
|
||||
}
|
||||
res.redirect('/');
|
||||
};
|
||||
```
|
||||
|
||||
次に、*ensureAuthenticated* を、プロファイルページのリクエストに対するミドルウェアとして、ページをレンダーする関数を含む get リクエスト引数の前に追加してください。
|
||||
|
||||
```js
|
||||
app
|
||||
.route('/profile')
|
||||
.get(ensureAuthenticated, (req,res) => {
|
||||
res.render(process.cwd() + '/views/pug/profile');
|
||||
});
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/ae49b8778cab87e93284a91343da0959)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
ミドルウェア ensureAuthenticate を /profile ルート上に実装する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/ensureAuthenticated[^]*req.isAuthenticated/gi,
|
||||
'Your ensureAuthenticated middleware should be defined and utilize the req.isAuthenticated function'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/profile[^]*get[^]*ensureAuthenticated/gi,
|
||||
'Your ensureAuthenticated middleware should be attached to the /profile route'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
認証されていないため、/profile への get リクエストを正しく / にリダイレクトする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/profile').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/Home page/gi,
|
||||
'An attempt to go to the profile at this point should redirect to the homepage since we are not logged in'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: 589fc831f9fc0f352b528e76
|
||||
title: 切断を処理する
|
||||
challengeType: 2
|
||||
forumTopicId: 301552
|
||||
dashedName: handle-a-disconnect
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
お気づきかもしれませんが、ここまでは単にユーザー数を増やしているだけです。 ユーザーの切断処理も最初の接続と同じくらい簡単です。ただし、サーバー全体でリッスンするのではなくソケットごとにリッスンする必要があります。
|
||||
|
||||
これを行うには、データを渡さずにソケットで `'disconnect'` をリッスンする別のリスナーを、既存の `'connect'` リスナーの中に追加してください。 この機能をテストするには、ユーザーが切断したことをコンソールに記録するだけです。
|
||||
|
||||
```js
|
||||
socket.on('disconnect', () => {
|
||||
/*anything you want to do on disconnect*/
|
||||
});
|
||||
```
|
||||
|
||||
クライアントで現在のユーザーの更新されたカウントを継続的に保つようにするには、切断が発生したときに currentUsers を 1 減らし、それから、更新されたカウントで「user count」イベントをエミットする必要があります!
|
||||
|
||||
**注:** `'disconnect'` とまったく同様に、ソケットがサーバーへエミットできる他のすべてのイベントについても、「socket」を定義しているコネクションリスナーの中で処理する必要があります。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/ab1007b76069884fb45b215d3c4496fa)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
サーバーはソケットからの切断イベントを処理する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(data, /socket.on.*('|")disconnect('|")/gi, '');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
クライアントは「user count」イベントをリッスンする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/public/client.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/socket.on.*('|")user count('|")/gi,
|
||||
'Your client should be connection to server with the connection defined as socket'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,86 @@
|
||||
---
|
||||
id: 58a25c98f9fc0f352b528e7f
|
||||
title: パスワードをハッシュする
|
||||
challengeType: 2
|
||||
forumTopicId: 301553
|
||||
dashedName: hashing-your-passwords
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
情報セキュリティのセクションに話を戻しますが、プレーンテキストのパスワードを保存することは*決して安全ではありません*。 ここでは、この問題を解決するために BCrypt を実装します。
|
||||
|
||||
`bcrypt@~5.0.0` を依存関係として追加し、サーバーで require してください。 2 つの重要な箇所でハッシュを処理する必要があります。1 つは、新しいアカウントの登録/保存を処理する箇所で、もう 1 つは、ログイン時にパスワードが正しいことを確認する箇所です。
|
||||
|
||||
現在の登録ルート上では、`password: req.body.password` のように、ユーザーのパスワードをデータベースへ挿入しています。 この代わりにハッシュの保存を実装する簡単な方法として、データベース ロジックの前に `const hash = bcrypt.hashSync(req.body.password, 12);` を追加し、データベースに保存する `req.body.password` を `password: hash` に置き換えることができます。
|
||||
|
||||
さらに認証ストラテジーでは、プロセスを完了する前のコードで `if (password !== user.password) { return done(null, false); }` のチェックを実行します。 以上の変更を加えると、`user.password` がハッシュになります。 既存のコードに変更を加える前に、ステートメントではパスワードが**一致しない**かどうかをチェックし、一致しない場合に非認証を返していることに注目してください。 上記を考慮するとコードは次のようになり、ハッシュに対して入力されたパスワードを適切にチェックします。
|
||||
|
||||
```js
|
||||
if (!bcrypt.compareSync(password, user.password)) {
|
||||
return done(null, false);
|
||||
}
|
||||
```
|
||||
|
||||
これで、パスワードを保存する必要がある場合に、最も重要なセキュリティ機能の1つを実装できます!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/dc16cca09daea4d4151a9c36a1fab564)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
BCrypt を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'bcrypt',
|
||||
'Your project should list "bcrypt" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
BCrypt を正しく require して実装する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/require.*("|')bcrypt\1/gi,
|
||||
'You should have required bcrypt'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/bcrypt.hashSync/gi,
|
||||
'You should use hash the password in the registration'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/bcrypt.compareSync/gi,
|
||||
'You should compare the password to the hash in your strategy'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: 5895f70ef9fc0f352b528e6b
|
||||
title: プロファイルをまとめる
|
||||
challengeType: 2
|
||||
forumTopicId: 301554
|
||||
dashedName: how-to-put-a-profile-together
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`/profile` にアクセスしているユーザーが認証されたことを確認できるようになったので、ページの `req.user` に含まれている情報を使用できます!
|
||||
|
||||
プロパティ `username` および `req.user.username` の値を含むオブジェクトを、プロファイルビューのレンダーメソッドの 2 番目の引数として渡してください。 次に、`profile.pug` ビューに移動し、既存の `h1` 要素の下に同レベルのインデントで次の行を追加してください。
|
||||
|
||||
```pug
|
||||
h2.center#welcome Welcome, #{username}!
|
||||
```
|
||||
|
||||
これで、クラス「`center`」と id「`welcome`」を持ち、「`Welcome,`」の後にユーザー名を含む `h2` 要素が作成されます。
|
||||
|
||||
また、`profile.pug` で、ユーザーの認証解除のロジックを受け持つ `/logout` ルートを参照するリンクを追加してください。
|
||||
|
||||
```pug
|
||||
a(href='/logout') Logout
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/136b3ad611cc80b41cab6f74bb460f6a)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
Pug render 変数を /profile に正しく追加する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/username:( |)req.user.username/gi,
|
||||
'You should be passing the variable username with req.user.username into the render function of the profile page'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 5895f70df9fc0f352b528e69
|
||||
title: Passport のストラテジーの使用方法
|
||||
challengeType: 2
|
||||
forumTopicId: 301555
|
||||
dashedName: how-to-use-passport-strategies
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`index.pug` ファイルには、実はログインフォームがあります。 これまでは、インラインの JavaScript `if showLogin` があり、その後でフォームがインデントされていたために非表示になっていました。 `showLogin` が変数として定義されていなかったため、フォームを含むコードブロックがレンダーされませんでした。 このページの `res.render` で、新しい変数を `showLogin: true` としてオブジェクトに追加してください。 ページを更新すると、フォームが表示されます! このフォームは、`/login` で **POST** を実行するように設定されています。つまり、ここで POST を受け入れてユーザーを認証する必要があります。
|
||||
|
||||
このチャレンジでは、POST リクエストを受け入れるためにルート `/login` を追加する必要があります。 このルートで認証するには、レスポンスを送信する前にそのためのミドルウェアを追加する必要があります。 それには、レスポンスで `function(req,res)` の前に別のミドルウェアの引数を渡すだけです! 使用するミドルウェアは `passport.authenticate('local')` です。
|
||||
|
||||
`passport.authenticate` は引数として `{ failureRedirect: '/' }` などのいくつかのオプションを受け取ることができるのでとても便利です。ですので必ず追加してください。 ミドルウェア (認証ミドルウェアが成功した場合にのみ呼び出されます) を使用した後のレスポンスではユーザーを `/profile` にリダイレクトする必要があり、ルートでビュー `profile.pug` をレンダーする必要があります。
|
||||
|
||||
認証が成功すると、ユーザーオブジェクトが `req.user` に保存されます。
|
||||
|
||||
この時点で、ユーザー名とパスワードをフォームへ入力すると、ホームページ `/` にリダイレクトされ、サーバーのコンソールに `'User {USERNAME} attempted to log in.'` と表示されます。これは、今の段階では、登録されていないユーザーをログインさせないためです。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/7ad011ac54612ad53188b500c5e99cb9)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのステップを server.js に正しく実装する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/showLogin:( |)true/gi,
|
||||
'You should be passing the variable "showLogin" as true to your render function for the homepage'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/failureRedirect:( |)('|")\/('|")/gi,
|
||||
'Your code should include a failureRedirect to the "/" route'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/login[^]*post[^]*local/gi,
|
||||
'You should have a route for login which accepts a POST and passport.authenticates local'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
/login への POST リクエストで / に正しくリダイレクトする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/login').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/Looks like this page is being rendered from Pug into HTML!/gi,
|
||||
'A login attempt at this point should redirect to the homepage since we do not have any registered users'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,91 @@
|
||||
---
|
||||
id: 5895f70cf9fc0f352b528e67
|
||||
title: Passport ユーザーのシリアライズを実装する
|
||||
challengeType: 2
|
||||
forumTopicId: 301556
|
||||
dashedName: implement-the-serialization-of-a-passport-user
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
今の段階ではまだデータベースを設定していないため、実際のユーザーオブジェクトを読み込んでいません。 これはさまざまな方法で実現できますが、このプロジェクトでは、サーバーを起動し、アプリのライフサイクル全体にわたって永続的な接続を維持した時点で、データベースに接続します。 接続するために、データベースの接続文字列 (例: `mongodb+srv://:@cluster0-jvwxi.mongodb.net/?retryWrites=true&w=majority`) を環境変数 `MONGO_URI` に追加してください 。 この変数は `connection.js` ファイルで使用されます。
|
||||
|
||||
*[MongoDB Atlas](https://www.mongodb.com/cloud/atlas) で無料のデータベースを設定することができます。*
|
||||
|
||||
ここではデータベースに接続し、リクエストのリッスンを開始します。 その目的は、データベースの接続前やデータベースエラーの発生時にリクエストを許可しないことです。 そのためには、次のようなコードでシリアライズとアプリルートを処理します。
|
||||
|
||||
```js
|
||||
myDB(async client => {
|
||||
const myDataBase = await client.db('database').collection('users');
|
||||
|
||||
// Be sure to change the title
|
||||
app.route('/').get((req, res) => {
|
||||
//Change the response to render the Pug template
|
||||
res.render('pug', {
|
||||
title: 'Connected to Database',
|
||||
message: 'Please login'
|
||||
});
|
||||
});
|
||||
|
||||
// Serialization and deserialization here...
|
||||
|
||||
// Be sure to add this...
|
||||
}).catch(e => {
|
||||
app.route('/').get((req, res) => {
|
||||
res.render('pug', { title: e, message: 'Unable to login' });
|
||||
});
|
||||
});
|
||||
// app.listen out here...
|
||||
```
|
||||
|
||||
`deserializeUser` の `myDataBase` コードを必ずコメント解除してください。そして `done(null, null)` を編集して `doc` を含めてください。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/175f2f585a2d8034044c7e8857d5add7)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
データベース接続が存在する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/Connected to Database/gi,
|
||||
'You successfully connected to the database!'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
デシリアライズで DB を正しく使用している必要があり、`doc` を指定して `done(null, null)` を呼び出す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/null,\s*doc/gi,
|
||||
'The callback in deserializeUser of (null, null) should be altered to (null, doc)'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,114 @@
|
||||
---
|
||||
id: 589a69f5f9fc0f352b528e71
|
||||
title: ソーシャル認証の実装Ⅱ
|
||||
challengeType: 2
|
||||
forumTopicId: 301557
|
||||
dashedName: implementation-of-social-authentication-ii
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
GitHub 認証の最後の設定作業として、ストラテジーそのものを作成します。 そのためには、`passport-github@~1.1.0` という依存関係をプロジェクトに追加し、`auth.js` で `GithubStrategy` として require する必要があります。たとえば `const GitHubStrategy = require('passport-github').Strategy;` のようになります。 環境変数を使用できるように、`dotenv` の require と設定を忘れないでください。
|
||||
|
||||
GitHub ストラテジーを設定するには、インスタンス化された `GitHubStrategy` を使用するよう Passport に指示する必要があります。これは 2 つの引数を受け取ります。1 つはオブジェクト (`clientID`、`clientSecret` および `callbackURL` を含む) で、もう 1 つはユーザーが正常に認証されたときに呼び出される関数です。この関数は、ユーザーが新規かどうかを判断し、ユーザーのデータベースオブジェクトに最初にどのフィールドを保存するかを決めます。 この手順は多くのストラテジーで一般的ですが、特定のストラテジーの GitHub README に概説されているように、より多くの情報が必要な場合もあります。 たとえば、Google では*スコープ*も必要です。スコープは、リクエストがどのような種類の情報を返すよう求めているのかを判断し、そのようなアクセスを承認するようユーザーに求めます。 ここで実装しているストラテジーの使用方法は[こちら](https://github.com/jaredhanson/passport-github/)に記載されていますが、私たちは freeCodeCamp で順調に作業を進めています!
|
||||
|
||||
新しいストラテジーは次のようになります。
|
||||
|
||||
```js
|
||||
passport.use(new GitHubStrategy({
|
||||
clientID: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
callbackURL: /*INSERT CALLBACK URL ENTERED INTO GITHUB HERE*/
|
||||
},
|
||||
function(accessToken, refreshToken, profile, cb) {
|
||||
console.log(profile);
|
||||
//Database logic here with callback containing our user object
|
||||
}
|
||||
));
|
||||
```
|
||||
|
||||
認証はまだ成功せず、データベース ロジックとコールバックがないためエラーになりますが、コンソールに GitHub プロファイルが記録されるはずです!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/ff3a1166684c1b184709ac0bee30dee6)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
passport-github 依存関係を追加する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'passport-github',
|
||||
'Your project should list "passport-github" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
passport-github を require する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/auth.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/require.*("|')passport-github("|')/gi,
|
||||
'You should have required passport-github'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
GitHub ストラテジーをここまで正しく設定する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/auth.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/passport\.use.*new GitHubStrategy/gis,
|
||||
'Passport should use a new GitHubStrategy'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/callbackURL:\s*("|').*("|')/gi,
|
||||
'You should have a callbackURL'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/process\.env(\.GITHUB_CLIENT_SECRET|\[(?<q>"|')GITHUB_CLIENT_SECRET\k<q>\])/g,
|
||||
'You should use process.env.GITHUB_CLIENT_SECRET'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/process\.env(\.GITHUB_CLIENT_ID|\[(?<q>"|')GITHUB_CLIENT_ID\k<q>\])/g,
|
||||
'You should use process.env.GITHUB_CLIENT_ID'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,80 @@
|
||||
---
|
||||
id: 589a8eb3f9fc0f352b528e72
|
||||
title: ソーシャル認証の実装Ⅲ
|
||||
challengeType: 2
|
||||
forumTopicId: 301558
|
||||
dashedName: implementation-of-social-authentication-iii
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ストラテジーの最後の部分では、GitHub から返されたプロファイルを処理します。 ユーザーのデータベースオブジェクトが存在する場合はそれを読み込み、存在しない場合は作成する必要があります。 プロファイルからのフィールドを設定し、ユーザーのオブジェクトを返します。 GitHub からは固有の *id* が各プロファイル内に提供されます。この id を使用してユーザーを検索しシリアライズすることができます (すでに実装済み) 。 以下は、プロジェクトで使用できる実装の例です。新しいストラテジーの 2 番目の引数となる関数の中で、現在の `console.log(profile);` のすぐ下に記述します。
|
||||
|
||||
```js
|
||||
myDataBase.findOneAndUpdate(
|
||||
{ id: profile.id },
|
||||
{
|
||||
$setOnInsert: {
|
||||
id: profile.id,
|
||||
name: profile.displayName || 'John Doe',
|
||||
photo: profile.photos[0].value || '',
|
||||
email: Array.isArray(profile.emails)
|
||||
? profile.emails[0].value
|
||||
: 'No public email',
|
||||
created_on: new Date(),
|
||||
provider: profile.provider || ''
|
||||
},
|
||||
$set: {
|
||||
last_login: new Date()
|
||||
},
|
||||
$inc: {
|
||||
login_count: 1
|
||||
}
|
||||
},
|
||||
{ upsert: true, new: true },
|
||||
(err, doc) => {
|
||||
return cb(null, doc.value);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`findOneAndUpdate` では、オブジェクトを検索して更新することができます。 オブジェクトが存在しない場合は、そのオブジェクトが挿入され、コールバック関数で使用できるようになります。 この例では、`last_login` を常に設定し、`login_count` を `1` 増やして、新しいオブジェクト (新規ユーザー) が挿入された場合にのみ、フィールドの大部分を設定します。 デフォルト値の使用に注意してください。 場合によっては、返されるプロファイルに必ずしもすべての情報が設定されていなかったり、ユーザーがプロファイルを非公開にしていたりします。 そのような場合は、エラーを防ぐための処理を実行します。
|
||||
|
||||
これでアプリにログインできるはずです。試してみてください!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/183e968f0e01d81dde015d45ba9d2745)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
GitHub ストラテジーの設定を完了する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/auth.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/GitHubStrategy[^]*myDataBase/gi,
|
||||
'Strategy should use now use the database to search for the user'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/GitHubStrategy[^]*return cb/gi,
|
||||
'Strategy should return the callback function "cb"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,108 @@
|
||||
---
|
||||
id: 589a69f5f9fc0f352b528e70
|
||||
title: ソーシャル認証の実装
|
||||
challengeType: 2
|
||||
forumTopicId: 301559
|
||||
dashedName: implementation-of-social-authentication
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
アプリでこの種の認証を行うには、以下のパスに従います。
|
||||
|
||||
1. ユーザーは、特定のストラテジー (たとえば、GitHub) を使用して認証を受けるためのルートに自身を送信するボタンまたはリンクをクリックします。
|
||||
2. ルートは `passport.authenticate('github')` を呼び出し、ユーザーを GitHub へリダイレクトします。
|
||||
3. ユーザーがたどり着いた GitHub のページで、ユーザーがまだログインしていない場合はログインが許可されます。 次に、アプリからのプロファイルへのアクセスを承認するようユーザーに求めます。
|
||||
4. ユーザーはアプリの特定のコールバック URL に戻り、ユーザーが承認した場合はプロファイルが返されます。
|
||||
5. これでユーザーが認証され、アプリでは、プロファイルが返却されたプロファイルかどうかを確認します。そうでない場合はデータベースに保存します。
|
||||
|
||||
OAuth を使用したストラテジーでは、少なくとも*クライアント ID* と*クライアント シークレット*が必要です。サービスはこれらを使用して、認証リクエストが誰からのものか、またそれが有効かどうかを確認します。 これらは、認証を実装しようとしている GitHub などのサイトから取得され、アプリに固有のものです。これらの情報は**共有すべきではありません**。したがって、公開リポジトリにアップロードしたり、コード内に直接書き込んだりしないでください。 通常は、それらを `.env` ファイルに保存し、`process.env.GITHUB_CLIENT_ID` などのように参照します。 このチャレンジでは、GitHub ストラテジーを使用します。
|
||||
|
||||
GitHub からの*クライアント ID とシークレット*の取得は、「開発者設定」のアカウントプロファイル設定で実行され、その後は「[OAuth アプリケーション](https://github.com/settings/developers)」で実行されます。 「Register a new application (新しいアプリを登録する)」をクリックし、アプリに名前を付け、URL を Replit のホームページに貼り付けます (**プロジェクトコードの URL ではありません**)。最後に、コールバック URL をホームページと同じ URL に貼り付けますが、`/auth/github/callback` を追加します。 ユーザーはここにリダイレクトされ、GitHub で認証された後、処理が行われます。 返された情報を `'GITHUB_CLIENT_ID'` および `'GITHUB_CLIENT_SECRET'` として `.env` ファイルに保存します。
|
||||
|
||||
`routes.js` ファイルで、`showRegistration: true` の後に、`showSocialAuth: true` をホームページルートに追加します。 GET リクエストを受け付けるルートを 2 つ作成します。それらは、`/auth/github` と `/auth/github/callback` です。 1 つ目は、Passport を呼び出して `'github'` を認証するだけです。 2 つ目は、Passport を呼び出して `'github'` を認証した結果失敗し、`/` へリダイレクトした後、リダイレクトが成功した場合は `/profile` へリダイレクトします(前回のプロジェクトと同様です)。
|
||||
|
||||
`/auth/github/callback` の内容は、たとえば通常のログイン処理と似たものになります。
|
||||
|
||||
```js
|
||||
app.route('/login')
|
||||
.post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => {
|
||||
res.redirect('/profile');
|
||||
});
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/1f7f6f76adb178680246989612bea21e)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
ルート `/auth/github` が正確である必要があります。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
try {
|
||||
const res = await fetch(getUserInput('url') + '/_api/routes.js');
|
||||
if (res.ok) {
|
||||
const data = await res.text();
|
||||
assert.match(
|
||||
data.replace(/\s/g, ''),
|
||||
/passport.authenticate.*?github/g,
|
||||
'Route auth/github should only call passport.authenticate with github'
|
||||
);
|
||||
} else {
|
||||
throw new Error(res.statusText);
|
||||
}
|
||||
const res2 = await fetch(getUserInput('url') + '/_api/app-stack');
|
||||
if (res2.ok) {
|
||||
const data2 = JSON.parse(await res2.json());
|
||||
const dataLayer = data2.find(layer => layer?.route?.path === '/auth/github');
|
||||
assert.deepInclude(dataLayer?.route, { methods: {get: true}, path: "/auth/github"});
|
||||
assert.deepInclude(dataLayer?.route?.stack?.[0], {method: "get", name: "authenticate"});
|
||||
} else {
|
||||
throw new Error(res2.statusText);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ルート `/auth/github/callback` が正確である必要があります。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
try {
|
||||
const res = await fetch(getUserInput('url') + '/_api/routes.js');
|
||||
if (res.ok) {
|
||||
const data = await res.text();
|
||||
assert.match(
|
||||
data.replace(/\s/g, ''),
|
||||
/failureRedirect:("|')\/\1/g,
|
||||
'Route auth/github/callback should accept a get request and call passport.authenticate for github with a failure redirect to home'
|
||||
);
|
||||
} else {
|
||||
throw new Error(res.statusText);
|
||||
}
|
||||
const res2 = await fetch(getUserInput('url') + '/_api/app-stack');
|
||||
if (res2.ok) {
|
||||
const data2 = JSON.parse(await res2.json());
|
||||
const dataLayer = data2.find(layer => layer?.route?.path === '/auth/github/callback');
|
||||
assert.deepInclude(dataLayer?.route, { methods: {get: true}, path: "/auth/github/callback"});
|
||||
assert.deepInclude(dataLayer?.route?.stack?.[0], {method: "get", name: "authenticate"});
|
||||
} else {
|
||||
throw new Error(res2.statusText);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,81 @@
|
||||
---
|
||||
id: 58965611f9fc0f352b528e6c
|
||||
title: ユーザーをログアウトさせる
|
||||
challengeType: 2
|
||||
forumTopicId: 301560
|
||||
dashedName: logging-a-user-out
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ログアウトロジックの作成は簡単です。 ルートで、ユーザーの認証を解除し、ビューをレンダーするのではなくホームページにリダイレクトするだけです。
|
||||
|
||||
Passport では、リダイレクトする前に `req.logout();` を呼び出すだけで簡単にユーザーの認証を解除することができます。
|
||||
|
||||
```js
|
||||
app.route('/logout')
|
||||
.get((req, res) => {
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
});
|
||||
```
|
||||
|
||||
「ページが見つかりません」(404) を処理していないことにお気づきかもしれません。 Node では通常は次のミドルウェアでこれを処理します。 他のすべてのルートの後に、これを追加してください。
|
||||
|
||||
```js
|
||||
app.use((req, res, next) => {
|
||||
res.status(404)
|
||||
.type('text')
|
||||
.send('Not Found');
|
||||
});
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/c3eeb8a3ebf855e021fd0c044095a23b)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
`req.Logout` を `/logout` ルートで呼び出す必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/req.logout/gi,
|
||||
'You should be calling req.logout() in your /logout route'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ログアウトからホームページにリダイレクトする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/logout').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/Home page/gi,
|
||||
'When a user logs out they should be redirected to the homepage'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,205 @@
|
||||
---
|
||||
id: 58966a17f9fc0f352b528e6d
|
||||
title: 新規ユーザーの登録
|
||||
challengeType: 2
|
||||
forumTopicId: 301561
|
||||
dashedName: registration-of-new-users
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
新しいユーザーがサイトでアカウントを登録することを許可する必要があります。 ホームページの `res.render` で、渡されたオブジェクトに新しい変数 `showRegistration: true` を追加してください。 ページを更新すると、すでに `index.pug` ファイルで作成した登録フォームが表示されます! このフォームは `/register` の **POST** を実行するように設定されています。つまり、ここで **POST** を受け付けてデータベースにユーザーオブジェクトを作成します。
|
||||
|
||||
登録ルートのロジックは、「新規ユーザーの登録 > 新規ユーザーの認証 > /profileへのリダイレクト」になります。
|
||||
|
||||
ステップ 1 の新規ユーザーの登録のロジックでは、findOne コマンド > を使用してデータベースクエリを実行します。ユーザーが返された場合は、そのユーザーが存在しているためホームにリダイレクトします。 *または*、ユーザーが未定義でエラーが発生しなかった場合は、ユーザー名とパスワードを使用してデータベース に「insertOne」し、エラーが発生しない限り、 *next* を呼び出してステップ 2 の新規ユーザー認証に進みます。認証のロジックについてはすでに POST の */login* ルートに記述してあります。
|
||||
|
||||
```js
|
||||
app.route('/register')
|
||||
.post((req, res, next) => {
|
||||
myDataBase.findOne({ username: req.body.username }, function(err, user) {
|
||||
if (err) {
|
||||
next(err);
|
||||
} else if (user) {
|
||||
res.redirect('/');
|
||||
} else {
|
||||
myDataBase.insertOne({
|
||||
username: req.body.username,
|
||||
password: req.body.password
|
||||
},
|
||||
(err, doc) => {
|
||||
if (err) {
|
||||
res.redirect('/');
|
||||
} else {
|
||||
// The inserted document is held within
|
||||
// the ops property of the doc
|
||||
next(null, doc.ops[0]);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
passport.authenticate('local', { failureRedirect: '/' }),
|
||||
(req, res, next) => {
|
||||
res.redirect('/profile');
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/b230a5b3bbc89b1fa0ce32a2aa7b083e)で確認できます。
|
||||
|
||||
**注:** これ以降、*ピクチャー・イン・ピクチャー*対応ブラウザーの使用に関連する問題が生じる可能性があります。 エディター内でアプリのプレビューができるオンライン IDE を使用している場合は、新しいタブでこのプレビューを開くことを推奨します。
|
||||
|
||||
# --hints--
|
||||
|
||||
ルートを登録し、ホームに表示する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/showRegistration:( |)true/gi,
|
||||
'You should be passing the variable showRegistration as true to your render function for the homepage'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/register[^]*post[^]*findOne[^]*username:( |)req.body.username/gi,
|
||||
'You should have a route accepted a post request on register that querys the db with findone and the query being username: req.body.username'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
登録が正しく動作する必要があります。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const user = `freeCodeCampTester${Date.now()}`;
|
||||
const xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function () {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
test(this);
|
||||
} else {
|
||||
throw new Error(`${this.status} ${this.statusText}`);
|
||||
}
|
||||
};
|
||||
xhttp.open('POST', url + '/register', true);
|
||||
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhttp.send(`username=${user}&password=${user}`);
|
||||
function test(xhttpRes) {
|
||||
const data = xhttpRes.responseText;
|
||||
assert.match(
|
||||
data,
|
||||
/Profile/gi,
|
||||
'Register should work, and redirect successfully to the profile.'
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
ログインが正しく動作する必要があります。
|
||||
|
||||
```js
|
||||
async (getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
const user = `freeCodeCampTester${Date.now()}`;
|
||||
const xhttpReg = new XMLHttpRequest();
|
||||
xhttpReg.onreadystatechange = function () {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
login();
|
||||
} else {
|
||||
throw new Error(`${this.status} ${this.statusText}`);
|
||||
}
|
||||
};
|
||||
xhttpReg.open('POST', url + '/register', true);
|
||||
xhttpReg.setRequestHeader(
|
||||
'Content-type',
|
||||
'application/x-www-form-urlencoded'
|
||||
);
|
||||
xhttpReg.send(`username=${user}&password=${user}`);
|
||||
function login() {
|
||||
const xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function () {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
test(this);
|
||||
} else {
|
||||
throw new Error(`${this.status} ${this.statusText}`);
|
||||
}
|
||||
};
|
||||
xhttp.open('POST', url + '/login', true);
|
||||
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhttp.send(`username=${user}&password=${user}`);
|
||||
}
|
||||
function test(xhttpRes) {
|
||||
const data = xhttpRes.responseText;
|
||||
assert.match(
|
||||
data,
|
||||
/Profile/gi,
|
||||
'Login should work if previous test was done successfully and redirect successfully to the profile.'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
new RegExp(user, 'g'),
|
||||
'The profile should properly display the welcome to the user logged in'
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
ログアウト正しくが動作する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.ajax({
|
||||
url: getUserInput('url') + '/logout',
|
||||
type: 'GET',
|
||||
xhrFields: { withCredentials: true }
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.match(data, /Home/gi, 'Logout should redirect to home');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ログアウト後にプロファイルが動作しなくなるようにする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.ajax({
|
||||
url: getUserInput('url') + '/profile',
|
||||
type: 'GET',
|
||||
crossDomain: true,
|
||||
xhrFields: { withCredentials: true }
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/Home/gi,
|
||||
'Profile should redirect to home when we are logged out now again'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 589fc832f9fc0f352b528e79
|
||||
title: チャットメッセージを送信して表示する
|
||||
challengeType: 2
|
||||
forumTopicId: 301562
|
||||
dashedName: send-and-display-chat-messages
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ここでは、クライアントからサーバーにチャットメッセージを送信できるようにし、サーバーからすべてのクライアントにエミットできるようにします。 `client.js` ファイルには、すでにメッセージフォームの送信時の処理を行うコードブロックが用意されています。
|
||||
|
||||
```js
|
||||
$('form').submit(function() {
|
||||
/*logic*/
|
||||
});
|
||||
```
|
||||
|
||||
フォームの送信コードの中で、`messageToSend` を定義した後、ただしテキストボックス `#m` をクリアする前に、イベントをエミットする必要があります。 イベント名は `'chat message'` にし、データは `messageToSend` にする必要があります。
|
||||
|
||||
```js
|
||||
socket.emit('chat message', messageToSend);
|
||||
```
|
||||
|
||||
次に、サーバー上でソケットをリッスンし、 `message` というデータを持つイベント `'chat message'` を受信する必要があります。 サーバーでイベントを受信したら、`name` と `message` を含むオブジェクトデータを使用して、イベント `'chat message'` をすべてのソケット `io.emit` にエミットする必要があります。
|
||||
|
||||
`client.js` では、イベント `'chat message'` をリッスンし、イベントを受信したら、名前、コロン、メッセージを使用してリストアイテムを `#messages` の末尾に追加する必要があります。
|
||||
|
||||
この時点でチャットが完全に機能し、すべてのクライアントにメッセージが送信されるはずです!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
サーバーは、`'chat message'` をリッスンして正しくエミットする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/socket.on.*('|")chat message('|")[^]*io.emit.*('|")chat message('|").*name.*message/gis,
|
||||
'Your server should listen to the socket for "chat message" then emit to all users "chat message" with name and message in the data object'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
クライアントは、イベント `'chat message'` から受信した新しいデータを適切に処理して表示する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/public/client.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/socket.on.*('|")chat message('|")[^]*messages.*li/gis,
|
||||
'You should append a list item to #messages on your client within the "chat message" event listener to display the new message'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,131 @@
|
||||
---
|
||||
id: 5895f70cf9fc0f352b528e66
|
||||
title: ユーザーオブジェクトのシリアライズ
|
||||
challengeType: 2
|
||||
forumTopicId: 301563
|
||||
dashedName: serialization-of-a-user-object
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
シリアライズとデシリアライズは認証に関する重要な概念です。 オブジェクトをシリアライズするとは、その内容を小さな*キー*に変換して、元のオブジェクトにデシリアライズできるようにすることを意味します。 これにより、 新しいページをリクエストするたびにユーザー名とパスワードのような認証データを送信することなく、誰がサーバーと通信したかを知ることができます。
|
||||
|
||||
これを適切に設定するには、シリアライズ関数とデシリアライズ関数が必要です。 Passport では、これらを `passport.serializeUser( OURFUNCTION )` と `passport.deserializeUser( OURFUNCTION )` で作成します。
|
||||
|
||||
`serializeUser` は 2 つの引数を指定して呼び出します。1 つ目は完全なユーザーオブジェクトで、2 つ目は Passport で使用されるコールバックです。 コールバックからは、ユーザーを特定するための一意のキーを返す必要があります。オブジェクトのユーザー `_id` を使用するのが最も簡単です。 この _id は MongoDB によって生成されるため、一意になります。 同様に、`deserializeUser` もそのキーと Passport のコールバック関数を指定して呼び出しますが、ここでは、前述のキーを受け取り、完全なユーザー オブジェクトをコールバックに返す必要があります。 Mongo の `_id` のクエリ検索を行うには、`const ObjectID = require('mongodb').ObjectID;` を作成する必要があり、それを使用するために `new ObjectID(THE_ID)` を呼び出す必要があります。 `mongodb@~3.6.0` を依存関係として追加してください。 以下の例をご覧ください。
|
||||
|
||||
```js
|
||||
passport.serializeUser((user, done) => {
|
||||
done(null, user._id);
|
||||
});
|
||||
|
||||
passport.deserializeUser((id, done) => {
|
||||
myDataBase.findOne({ _id: new ObjectID(id) }, (err, doc) => {
|
||||
done(null, null);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
注: この `deserializeUser` は、次のステップで DB を設定するまではエラーをスローします。そのため、ここではブロック全体をコメントアウトして、関数 `deserializeUser` で `done(null, null)` を呼び出すだけにしてください。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/7068a0d09e61ec7424572b366751f048)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
ユーザー関数を正しくシリアライズする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/passport.serializeUser/gi,
|
||||
'You should have created your passport.serializeUser function'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/null,\s*user._id/gi,
|
||||
'There should be a callback in your serializeUser with (null, user._id)'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ユーザー関数を正しくデシリアライズする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/passport.deserializeUser/gi,
|
||||
'You should have created your passport.deserializeUser function'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/null,\s*null/gi,
|
||||
'There should be a callback in your deserializeUser with (null, null) for now'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
MongoDB を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'mongodb',
|
||||
'Your project should list "mongodb" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ObjectId を含めて Mongodb を正しく require する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/require.*("|')mongodb\1/gi,
|
||||
'You should have required mongodb'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/new ObjectID.*id/gi,
|
||||
'Even though the block is commented out, you should use new ObjectID(id) for when we add the database'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,116 @@
|
||||
---
|
||||
id: 5895f700f9fc0f352b528e63
|
||||
title: テンプレートエンジンを設定する
|
||||
challengeType: 2
|
||||
forumTopicId: 301564
|
||||
dashedName: set-up-a-template-engine
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
チャレンジに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freeCodeCamp/boilerplate-advancednode/)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-advancednode)を使用してチャレンジを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。
|
||||
|
||||
テンプレートエンジンを使用すると、(*Pug* で記述するような) 静的なテンプレートファイルをアプリで使用できます。 実行時にテンプレートエンジンによって、テンプレートファイル内の変数がサーバーから提供される実際の値に置き換えられます。 そして、テンプレートが静的な HTML ファイルに変換され、クライアントへ送信されます。 このアプローチにより、HTML ページの設計が容易になり、クライアントから API 呼び出しを行うことなく、ページに変数を表示することができます。
|
||||
|
||||
`pug@~3.0.0` を `package.json` ファイルに依存関係として追加してください。
|
||||
|
||||
どのテンプレートエンジンを使用しているかを Express に知らせる必要があります。 `set` メソッドを使用して、`view engine` プロパティの値として `pug` を割り当ててください (`app.set('view engine', 'pug')` と記述します)。
|
||||
|
||||
`views/pug` ディレクトリ内でインデックスファイルが正しくレンダーされるまで、ページは読み込まれません。
|
||||
|
||||
`/` ルートの `res.render()` 宣言の引数を、`views/pug` ディレクトリへのファイルパスに変更してください。 パスは相対パス (ビューを基準) または絶対パスであり、ファイル拡張子を必要としません。
|
||||
|
||||
すべてが順調にいけば、アプリのホームページに「`Pug template is not defined.`」というメッセージが表示されなくなり、Pug テンプレートが正常にレンダーされたことを示すメッセージが表示されます!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/3515cd676ea4dfceab4e322f59a37791)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
Pug を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'pug',
|
||||
'Your project should list "pug" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ビューエンジンを Pug にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/('|")view engine('|"),( |)('|")pug('|")/gi,
|
||||
'Your project should set Pug as a view engine'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
正しい ExpressJS メソッドを使用して、レスポンスからインデックスページをレンダーします。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/FCC Advanced Node and Express/gi,
|
||||
'You successfully rendered the Pug template!'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Pug が正しく動作している必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/pug-success-message/gi,
|
||||
'Your projects home page should now be rendered by pug with the projects .pug file unaltered'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,132 @@
|
||||
---
|
||||
id: 5895f70cf9fc0f352b528e65
|
||||
title: Passport を設定する
|
||||
challengeType: 2
|
||||
forumTopicId: 301565
|
||||
dashedName: set-up-passport
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
ここで *Passport* を設定して、最終的にユーザーのアカウント登録やログインができるようにしましょう! Passport の他に、express-session を使用してセッションを処理します。 このミドルウェアを使用してセッション id をクライアントの Cookie として保存しておき、サーバーでその id を使用してセッションデータにアクセスすることができます。 クライアントではサーバーへのユーザー認証に Cookie を使用しますが、この方法によって個人のアカウント情報が Cookie に入り込むのを防ぎ、*キー*を通じてのみサーバーに保存されているデータにアクセスするようになります。
|
||||
|
||||
Passport をプロジェクトで使用できるように設定するには、まず package.json に依存関係として追加する必要があります。 `passport@~0.4.1`
|
||||
|
||||
また、express-session を依存関係として追加してください。 express-session はたくさんの高度な機能を備えていますが、ここでは基本的な機能のみを使用します。 `express-session@~1.17.1`
|
||||
|
||||
ここでセッション設定を行い、Passport を初期化する必要があります。 最初に変数 「session」と「passport」を作成し、「express-session」と「passport」をそれぞれ require してください。
|
||||
|
||||
セッションを使用するように Express アプリを設定するため、いくつかの基本的なオプションを定義します。 必ず.envファイルに「SESSION_SECRET」を追加し、ランダムな値を指定してください。 この値を使用して、Cookie の暗号化に使用されるハッシュを計算します!
|
||||
|
||||
```js
|
||||
app.use(session({
|
||||
secret: process.env.SESSION_SECRET,
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: false }
|
||||
}));
|
||||
```
|
||||
|
||||
また、Express アプリに「passport.initialize()」と「passport.session()」を **use** するよう指示してください (たとえば、`app.use(passport.initialize());` などとします)。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/4068a7662a2f9f5d5011074397d6788c)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
Passport と express-session を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'passport',
|
||||
'Your project should list "passport" as a dependency'
|
||||
);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'express-session',
|
||||
'Your project should list "express-session" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
依存関係を正しく require する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/require.*("|')passport("|')/gi,
|
||||
'You should have required passport'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/require.*("|')express-session("|')/gi,
|
||||
'You should have required express-session'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Express アプリで新しい依存関係を使用する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/passport.initialize/gi,
|
||||
'Your express app should use "passport.initialize()"'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/passport.session/gi,
|
||||
'Your express app should use "passport.session()"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
セッションとセッションシークレットを正しく設定する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/secret *: *process\.env(\.SESSION_SECRET|\[(?<q>"|')SESSION_SECRET\k<q>\])/g,
|
||||
'Your express app should have express-session set up with your secret as process.env.SESSION_SECRET'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: 589fc830f9fc0f352b528e74
|
||||
title: 環境を設定する
|
||||
challengeType: 2
|
||||
forumTopicId: 301566
|
||||
dashedName: set-up-the-environment
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
以降のチャレンジでは `chat.pug` ファイルを使用します。 そこで、`routes.js` ファイルで、`/chat` を指す GET ルートを追加してください。/chat は、`ensureAuthenticated` を利用し、レスポンスへの引数として渡された `{ user: req.user }` を使用して `chat.pug` をレンダーします。 次に、既存の `/auth/github/callback` ルートを変更して、`req.session.user_id = req.user.id` を設定し、`/chat` にリダイレクトしてください。
|
||||
|
||||
`socket.io@~2.3.0` を依存関係として追加し、(Node.js に組み込まれている) `http`を使用して、以下のようにサーバーでソケットの require を定義しインスタンス化してください。
|
||||
|
||||
```javascript
|
||||
const http = require('http').createServer(app);
|
||||
const io = require('socket.io')(http);
|
||||
```
|
||||
|
||||
*http* サーバーを *Express アプリ*にマウントしたので、*http* サーバーからリッスンする必要があります。 `app.listen` の行を `http.listen` に変更します。
|
||||
|
||||
最初の処理として、クライアントからの新しい接続をリッスンします。 <dfn>on</dfn> キーワードがその処理を行い、特定のイベントがないかリッスンします。 ここでは、エミットされたイベントのタイトルを含む文字列と、データを渡すための関数の、2 つの引数が必要です。 コネクションリスナーの場合は、*ソケット*を使用して第 2 引数でデータを定義します。 ソケットとは、接続している個々のクライアントのことです。
|
||||
|
||||
サーバーへの接続をリッスンするには、データベース接続の中に次を追加します。
|
||||
|
||||
```javascript
|
||||
io.on('connection', socket => {
|
||||
console.log('A user has connected');
|
||||
});
|
||||
```
|
||||
|
||||
クライアントが接続できるようにするには、認証後にページによってロードされる `client.js` に次を追加する必要があります。
|
||||
|
||||
```js
|
||||
/*global io*/
|
||||
let socket = io();
|
||||
```
|
||||
|
||||
コメントにより、通常「io」がファイル内で定義されていない場合に表示されるエラーを表示しないようにしています。 chat.pug 内のページの Socket.IO ライブラリには、すでに信頼性の高い CDN を追加してあります。
|
||||
|
||||
では、アプリの読み込みと認証を試してみましょう。サーバーコンソールに「A user has connected」と表示されるはずです!
|
||||
|
||||
**注:** `io()` は、同じ url またはサーバー上でホストされているソケットに接続している場合にのみ動作します。 他の場所でホストされている外部ソケットに接続するには、`io.connect('URL');` を使用します。
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/aae41cf59debc1a4755c9a00ee3859d1)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
`socket.io` を依存関係にする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'socket.io',
|
||||
'Your project should list "socket.io" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`http` を `http` として正しく require しインスタンス化する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/http.*=.*require.*('|")http\1/gi,
|
||||
'Your project should list "http" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`socket.io` を `io` として正しく require しインスタンス化する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/io.*=.*require.*('|")socket.io\1.*http/gi,
|
||||
'You should correctly require and instantiate socket.io as io.'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Socket.IO で接続をリッスンする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/io.on.*('|")connection\1.*socket/gi,
|
||||
'io should listen for "connection" and socket should be the 2nd arguments variable'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
クライアントからサーバーに接続する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/public/client.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/socket.*=.*io/gi,
|
||||
'Your client should be connection to server with the connection defined as socket'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: 5895f70bf9fc0f352b528e64
|
||||
title: テンプレートエンジンのパワーを活用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301567
|
||||
dashedName: use-a-template-engines-powers
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
テンプレートエンジンを使用する最大の利点の 1 つは、HTML にレンダーする前に変数をサーバーからテンプレートファイルに渡せることです。
|
||||
|
||||
Pug ファイルでは変数を使用することができます。それには、変数名を `#{variable_name}` として参照し、要素上の他のテキストとともにインラインで記述します。または、要素に対して `p=variable_name` のように空白を付けずに等号を使用します (この例では変数の値が p 要素のテキストに割り当てられます)。
|
||||
|
||||
Pug のシンタックスと構造については、[こちら](https://github.com/pugjs/pug)の GitHub README をぜひご覧してください。 Pug は、空白とタブを使用してネストされた要素を示し、少ないコード量で美しいサイトを作成することを目標としています。
|
||||
|
||||
プロジェクトに含まれている pug ファイル「index.pug」では、変数 *title* と *message* を使用しました。
|
||||
|
||||
これらをサーバーから渡すには、*res.render* への 2 番目の引数として、変数とその値を持つオブジェクトを追加する必要があります。 たとえば、インデックスビューの変数を設定するために、`{title: 'Hello', message: 'Please login'}` というオブジェクトを渡すことができます。
|
||||
|
||||
コードは `res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});` のようになります。これでページを更新すると、index.pug ファイルに配置されているビュー内の正しい場所に、これらの値がレンダーされるはずです!
|
||||
|
||||
正しいと思ったら、ページを送信してください。 エラーが発生している場合は、ここまでに完了したプロジェクトを[こちら](https://gist.github.com/camperbot/4af125119ed36e6e6a8bb920db0c0871)で確認できます。
|
||||
|
||||
# --hints--
|
||||
|
||||
Pug で変数を正しくレンダーする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/pug-variable("|')>Please login/gi,
|
||||
'Your projects home page should now be rendered by pug with the projects .pug file unaltered'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 587d824c367417b2b2512c4c
|
||||
title: .deepEqual と .notDeepEqual で deep equality をアサートする
|
||||
challengeType: 2
|
||||
forumTopicId: 301587
|
||||
dashedName: assert-deep-equality-with--deepequal-and--notdeepequal
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`deepEqual()` は、2 つのオブジェクトが deep equal であることをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Equality` スイート内の `#7` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.deepEqual` または `assert.notDeepEqual` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに、正しいメソッドを選ぶ必要があります - `deepEqual` もしくは `notDeepEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'deepEqual',
|
||||
'The order of the keys does not matter'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに、正しいメソッドを選ぶ必要があります - `deepEqual` もしくは `notDeepEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'notDeepEqual',
|
||||
'The position of elements within an array does matter'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: 587d824c367417b2b2512c4d
|
||||
title: 2 つの要素のプロパティを比較する
|
||||
challengeType: 2
|
||||
forumTopicId: 301588
|
||||
dashedName: compare-the-properties-of-two-elements
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Comparisons` スイート内の `#8` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isAbove` または `assert.isAtMost` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに、正しいメソッドを選ぶ必要があります - `isAbove` もしくは `isAtMost` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'isAtMost',
|
||||
'5 is at most (<=) 5'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに、正しいメソッドを選ぶ必要があります- `isAbove` もしくは `isAtMost` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'isAbove', '1 is greater than 0');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに、正しいメソッドを選ぶ必要があります- `isAbove` もしくは `isAtMost` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[2].method,
|
||||
'isAbove',
|
||||
'Math.PI = 3.14159265 is greater than 3'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
4 番目のアサーションに、正しいメソッドを選ぶ必要があります - `isAbove` もしくは `isAtMost` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[3].method,
|
||||
'isAtMost',
|
||||
'1 - Math.random() is > 0 and <= 1. It is atMost 1 !'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 587d824a367417b2b2512c46
|
||||
title: JavaScript アサーションがどのように機能するかを学ぶ
|
||||
challengeType: 2
|
||||
forumTopicId: 301589
|
||||
dashedName: learn-how-javascript-assertions-work
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
これらのチャレンジに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freeCodeCamp/boilerplate-mochachai/)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-mochachai)を使用してチャレンジを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Basic Assertions` スイート内の `#1` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isNull` または `assert.isNotNull` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=0').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `isNull` もしくは `isNotNull` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=0').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'isNull', 'Null is null');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります- `isNull` もしくは `isNotNull` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=0').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'isNotNull', '1 is not null');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: 587d824f367417b2b2512c5a
|
||||
title: Chai-HTTP を使用して API レスポンスで機能テストを実行するⅢ - PUT メソッド
|
||||
challengeType: 2
|
||||
forumTopicId: 301590
|
||||
dashedName: run-functional-tests-on-an-api-response-using-chai-http-iii---put-method
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`PUT` リクエストをテストする時、一緒にデータを送信することがよくあります。 `PUT` リクエストに含まれるデータは、リクエストボディと呼ばれます。
|
||||
|
||||
`PUT` リクエストと JSON オブジェクトを `'/travellers'` エンドポイントへ送信するために、`chai-http` プラグインの `put` メソッドと `send` メソッドを使用することができます。
|
||||
|
||||
```js
|
||||
chai
|
||||
.request(server)
|
||||
.put('/travellers')
|
||||
.send({
|
||||
"surname": [last name of a traveller of the past]
|
||||
})
|
||||
...
|
||||
```
|
||||
|
||||
するとルートは次のように応答します。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": [first name],
|
||||
"surname": [last name],
|
||||
"dates": [birth - death years]
|
||||
}
|
||||
```
|
||||
|
||||
`'/travellers'` エンドポイントへの異なるレスポンスについては、サーバーのコードを参照してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/2_functional-tests.js` 内で、`'Send {surname: "Colombo"}'` テスト (`// #3`) を変更し、`put` メソッドと `send` メソッドを使用して `'/travellers'` エンドポイントをテストしてください。
|
||||
|
||||
PUT リクエストで、次の JSON オブジェクトを送信してください。
|
||||
|
||||
```json
|
||||
{
|
||||
"surname": "Colombo"
|
||||
}
|
||||
```
|
||||
|
||||
`request.end` コールバック内で、以下を確認してください。
|
||||
|
||||
1. `status` は、`200` でなければなりません。
|
||||
2. `type` は、`application/json` でなければなりません。
|
||||
3. `body.name` は、`Cristoforo` でなければなりません。
|
||||
4. `body.surname` は、`Colombo` でなければなりません。
|
||||
|
||||
上記のアサーション順序に従ってください (この順序を想定しています)。 また、完了したら必ず `assert.fail()` を削除してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.status` が 200 かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'equal');
|
||||
assert.equal(data.assertions[0].args[0], 'res.status');
|
||||
assert.equal(data.assertions[0].args[1], '200');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.type` が `'application/json'` かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'equal');
|
||||
assert.equal(data.assertions[1].args[0], 'res.type');
|
||||
assert.match(data.assertions[1].args[1], /('|")application\/json\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.body.name` が `'Cristoforo'` かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'equal');
|
||||
assert.equal(data.assertions[2].args[0], 'res.body.name');
|
||||
assert.match(data.assertions[2].args[1], /('|")Cristoforo\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.body.surname` が `'Colombo'` かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[3].method, 'equal');
|
||||
assert.equal(data.assertions[3].args[0], 'res.body.surname');
|
||||
assert.match(data.assertions[3].args[1], /('|")Colombo\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,126 @@
|
||||
---
|
||||
id: 587d824f367417b2b2512c5b
|
||||
title: Chai-HTTP を使用して API レスポンスで機能テストを実行する Ⅳ - PUT メソッド
|
||||
challengeType: 2
|
||||
forumTopicId: 301591
|
||||
dashedName: run-functional-tests-on-an-api-response-using-chai-http-iv---put-method
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
この演習は、前回のものと似ています。
|
||||
|
||||
`PUT` リクエストをテストする方法が分かったので、テストを最初から実行してみましょう。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/2_functional-tests.js` 内で、`'Send {surname: "da Verrazzano"}'` テスト (`// #4`) を変更し、`put` メソッドと `send` メソッドを使用して `'/travellers'` エンドポイントをテストしてください。
|
||||
|
||||
PUT リクエストで、以下の JSON オブジェクトを送信してください。
|
||||
|
||||
```json
|
||||
{
|
||||
"surname": "da Verrazzano"
|
||||
}
|
||||
```
|
||||
|
||||
`request.end` コールバック内で、以下を確認してください。
|
||||
|
||||
1. `status` は、`200` でなければなりません。
|
||||
2. `type` は、 `application/json` でなければなりません。
|
||||
3. `body.name` は、`Giovanni` でなければなりません。
|
||||
4. `body.surname` は、`da Verrazzano` でなければなりません。
|
||||
|
||||
上記のアサーション順序に従ってください (この順序を想定しています)。 また、完了したら必ず `assert.fail()` を削除してください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.status` が 200 かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'equal');
|
||||
assert.equal(data.assertions[0].args[0], 'res.status');
|
||||
assert.equal(data.assertions[0].args[1], '200');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.type` が `'application/json'` かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'equal');
|
||||
assert.equal(data.assertions[1].args[0], 'res.type');
|
||||
assert.match(data.assertions[1].args[1], /('|")application\/json\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.body.name` が `'Giovanni'` かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'equal');
|
||||
assert.equal(data.assertions[2].args[0], 'res.body.name');
|
||||
assert.match(data.assertions[2].args[1], /('|")Giovanni\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.body.surname` が `'da Verrazzano'` かどうかをテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[3].method, 'equal');
|
||||
assert.equal(data.assertions[3].args[0], 'res.body.surname');
|
||||
assert.match(data.assertions[3].args[1], /('|")da Verrazzano\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 587d824f367417b2b2512c59
|
||||
title: Chai-HTTPを使用して API エンドポイントで機能テストを実行するⅡ
|
||||
challengeType: 2
|
||||
forumTopicId: 301592
|
||||
dashedName: run-functional-tests-on-api-endpoints-using-chai-http-ii
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/2_functional-tests.js` 内で、`'Test GET /hello with your name'` テスト (`// #2`) を変更し、レスポンスの `status` と `text` をアサートしてテストに合格させてください。
|
||||
|
||||
ルートに `?name=<your_name>` を追加して名前を URL クエリとして送信してください。 エンドポイントは `'hello <your_name>'` で応答します。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=1').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.status` == 200 をテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=1').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'equal');
|
||||
assert.equal(data.assertions[0].args[0], 'res.status');
|
||||
assert.equal(data.assertions[0].args[1], '200');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.text` == `'hello <your_name>'` をテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=1').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'equal');
|
||||
assert.equal(data.assertions[1].args[0], 'res.text');
|
||||
assert.match(data.assertions[1].args[1], /hello [\w\d_-]/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,100 @@
|
||||
---
|
||||
id: 587d824e367417b2b2512c58
|
||||
title: Chai-HTTP を使用して API エンドポイントの機能テストを実行する
|
||||
challengeType: 2
|
||||
forumTopicId: 301593
|
||||
dashedName: run-functional-tests-on-api-endpoints-using-chai-http
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
Mocha では、`chai-http` というプラグインを使用して API エンドポイントへの呼び出しのような非同期操作をテストできます。
|
||||
|
||||
`chai-http` を使用して `'GET /hello?name=[name] => "hello [name]"'` というスイートをテストする例を次に示します。
|
||||
|
||||
```js
|
||||
suite('GET /hello?name=[name] => "hello [name]"', function () {
|
||||
test('?name=John', function (done) {
|
||||
chai
|
||||
.request(server)
|
||||
.get('/hello?name=John')
|
||||
.end(function (err, res) {
|
||||
assert.equal(res.status, 200, 'Response status should be 200');
|
||||
assert.equal(res.text, 'hello John', 'Response should be "hello John"');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
テストでは、サーバーへ `GET` リクエストを送信し、名前を URL クエリ文字列 (`?name=John`) として指定します。 `end` メソッドのコールバック関数では、レスポンスオブジェクト (`res`) を受信します。レスポンスオブジェクトには `status` プロパティが含まれています。
|
||||
|
||||
最初の `assert.equal` は、ステータスが `200` に等しいかどうかを確認します。 2 番目の `assert.equal` は、レスポンス文字列 (`res.text`) が `"hello John"` と等しいことを確認します。
|
||||
|
||||
また、テストのコールバック関数の `done` パラメーターに注意してください。 非同期操作が完了したことを示すために、この関数をテストの最後に引数なしで呼び出す必要があります。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/2_functional-tests.js` 内で、`'Test GET /hello with no name'` テスト (`// #1`) を変更し、レスポンスの `status` と `text` をアサートしてテストに合格させてください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
URL クエリはなしとしてください。 名前の URL クエリがない場合、エンドポイントは `hello Guest` で応答します。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.status` == 200 をテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'equal');
|
||||
assert.equal(data.assertions[0].args[0], 'res.status');
|
||||
assert.equal(data.assertions[0].args[1], '200');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
`res.text` == `'hello Guest'` をテストする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'equal');
|
||||
assert.equal(data.assertions[1].args[0], 'res.text');
|
||||
assert.match(data.assertions[1].args[1], /('|")hello Guest\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,115 @@
|
||||
---
|
||||
id: 5f8884f4c46685731aabfc41
|
||||
title: ヘッドレスブラウザーを使用して機能テストを実行するⅡ
|
||||
challengeType: 2
|
||||
forumTopicId: 301594
|
||||
dashedName: run-functional-tests-using-a-headless-browser-ii
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/2_functional-tests.js` 内の `'Submit the surname "Vespucci" in the HTML form'` テスト (`// #5`) で、以下を自動化してください。
|
||||
|
||||
1. フォームに姓 `Vespucci` を入力します。
|
||||
2. 送信ボタンを押します。
|
||||
|
||||
`pressButton` コールバック内で以下を実行してください。
|
||||
|
||||
1. ステータスが OK `200` であることをアサートします。
|
||||
2. 要素 `span#name` 内のテキストが `'Amerigo'` であることをアサートします。
|
||||
3. 要素 `span#surname` 内のテキストが `'Vespucci'` であることをアサートします。
|
||||
4. 要素 `span#dates` が存在し、そのカウントが `1` であることをアサートします。
|
||||
|
||||
`assert.fail()` 呼び出しを削除することを忘れないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ヘッドレスブラウザーのリクエストが成功したことをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'browser.success');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
要素 `span#name` 内のテキストが `'Amerigo'` であることをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'browser.text');
|
||||
assert.match(data.assertions[1].args[0], /('|")span#name\1/);
|
||||
assert.match(data.assertions[1].args[1], /('|")Amerigo\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
要素 `span#surname` 内のテキストが `'Vespucci'` であることをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'browser.text');
|
||||
assert.match(data.assertions[2].args[0], /('|")span#surname\1/);
|
||||
assert.match(data.assertions[2].args[1], /('|")Vespucci\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
要素 `span#dates` が存在し、そのカウントが 1 であることをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=6').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[3].method, 'browser.elements');
|
||||
assert.match(data.assertions[3].args[0], /('|")span#dates\1/);
|
||||
assert.equal(data.assertions[3].args[1], 1);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: 587d8250367417b2b2512c5d
|
||||
title: ヘッドレスブラウザーを使用して機能テストを実行する
|
||||
challengeType: 2
|
||||
forumTopicId: 301595
|
||||
dashedName: run-functional-tests-using-a-headless-browser
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
ページに入力フォームがあります。 AJAX リクエストとして、`PUT /travellers` エンドポイントへデータを送信します。
|
||||
|
||||
リクエストが正常に完了すると、クライアントコードは、情報を含む `<div>` を、DOM へのレスポンスに追加します。
|
||||
|
||||
以下は、Zombie.js を使用してフォームとやり取りする方法の例です。
|
||||
|
||||
```js
|
||||
test('Submit the surname "Polo" in the HTML form', function (done) {
|
||||
browser.fill('surname', 'Polo').then(() => {
|
||||
browser.pressButton('submit', () => {
|
||||
browser.assert.success();
|
||||
browser.assert.text('span#name', 'Marco');
|
||||
browser.assert.text('span#surname', 'Polo');
|
||||
browser.assert.elements('span#dates', 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
まず、`browser` オブジェクトの `fill` メソッドにより、フォームの `surname` フィールドに値 `'Polo'` を入力します。 `fill` は promise を返すので、そこから `then` でつなぎます。
|
||||
|
||||
`then` コールバック内で、`browser` オブジェクトの `pressButton` メソッドを使用して、フォームの `submit` イベントリスナーを呼び出します。 `pressButton` メソッドは非同期です。
|
||||
|
||||
その後、AJAX リクエストからレスポンスを受信すると、いくつかのアサーションが実行され、以下が確定します。
|
||||
|
||||
1. レスポンスのステータスは、`200` です。
|
||||
2. `<span id='name'></span>` 要素内のテキストは、`'Marco'` と一致します。
|
||||
3. `<span id='surname'></span>` 要素内のテキストは、`'Polo'` と一致します。
|
||||
4. `1` という内容の `<span id='dates'></span>` 要素があります。
|
||||
|
||||
最後に、`done` コールバックを呼び出します。これは非同期テストのために必要です。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/2_functional-tests.js` 内の `'Submit the surname "Colombo" in the HTML form'` テスト (`// #5`) で、以下を自動化してください。
|
||||
|
||||
1. フォームに姓 `Colombo` を入力します。
|
||||
2. 送信ボタンを押します。
|
||||
|
||||
`pressButton` コールバック内で以下を実行してください。
|
||||
|
||||
1. ステータスが OK `200` であることをアサートします。
|
||||
2. 要素 `span#name` 内のテキストが `'Cristoforo'` であることをアサートします。
|
||||
3. 要素 `span#surname` 内のテキストが `'Colombo'` であることをアサートします。
|
||||
4. 要素 `span#dates` が存在し、そのカウントが `1` であることをアサートします。
|
||||
|
||||
`assert.fail()` 呼び出しを削除することを忘れないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
ヘッドレスブラウザーのリクエストが成功したことをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'browser.success');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
要素 `span#name` 内のテキストが `'Cristoforo'` であることをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'browser.text');
|
||||
assert.match(data.assertions[1].args[0], /('|")span#name\1/);
|
||||
assert.match(data.assertions[1].args[1], /('|")Cristoforo\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
要素 `span#surname` 内のテキストが `'Colombo'` であることをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'browser.text');
|
||||
assert.match(data.assertions[2].args[0], /('|")span#surname\1/);
|
||||
assert.match(data.assertions[2].args[1], /('|")Colombo\1/);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
要素 `span#dates` が存在し、カウントが 1 であることをアサートする必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[3].method, 'browser.elements');
|
||||
assert.match(data.assertions[3].args[0], /('|")span#dates\1/);
|
||||
assert.equal(data.assertions[3].args[1], 1);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,72 @@
|
||||
---
|
||||
id: 587d824f367417b2b2512c5c
|
||||
title: ヘッドレスブラウザーを使用してアクションをシミュレートする
|
||||
challengeType: 2
|
||||
dashedName: simulate-actions-using-a-headless-browser
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
次のチャレンジでは、ヘッドレスブラウザーを使用してページと人間のやり取りをシミュレートします。
|
||||
|
||||
ヘッドレスブラウザーは、GUI を持たないウェブブラウザーです。 通常のブラウザーと同じように、HTML、CSS、および JavaScript をレンダーして解釈することができます。 特にウェブページのテストに役立ちます。
|
||||
|
||||
以降のチャレンジでは、Zombie.js を使用します。これは、追加のバイナリをインストールしなくても動作する軽量のヘッドレスブラウザーです。 この機能により、Replit のような限られた環境で使用できます。 ただし、他にも多くの高機能なヘッドレスブラウザーがあります。
|
||||
|
||||
Mocha では、実際のテストが実行される前にコードを実行できます。 これは、以降のテストで使用するデータベースへのエントリの追加などの操作を行うのに便利です。
|
||||
|
||||
ヘッドレスブラウザーでテストを行う前に、テストを行うページに**アクセス**してください。
|
||||
|
||||
`suiteSetup` フックは、テストスイートの始めに一度だけ実行されます。
|
||||
|
||||
他にも、各テストの前、各テストの後、またはテストスイートの終わりにコードを実行できるいくつかのフックタイプがあります。 詳細については、Mocha のドキュメントを参照してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/2_functional-tests.js` の中の `Browser` 宣言の直後で、変数の `site` プロパティにプロジェクトの URL を追加してください。
|
||||
|
||||
```js
|
||||
Browser.site = 'https://boilerplate-mochachai.your-username.repl.co'; // Your URL here
|
||||
```
|
||||
|
||||
次に、`'Functional Tests with Zombie.js'` スイートのルートレベルで、次のコードを使用して `Browser` オブジェクトの新しいインスタンスを生成してください。
|
||||
|
||||
```js
|
||||
const browser = new Browser();
|
||||
```
|
||||
|
||||
そして、次のコードを使用して、`suiteSetup`フックで `browser` を `/` ルートに移動させてください。
|
||||
|
||||
```js
|
||||
suiteSetup(function(done) {
|
||||
return browser.visit('/', done);
|
||||
});
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,101 @@
|
||||
---
|
||||
id: 587d824b367417b2b2512c49
|
||||
title: 真かどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301596
|
||||
dashedName: test-for-truthiness
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`isTrue()` は、ブール値 `true` かどうかをテストし、`isNotTrue()` は、ブール値 `true` 以外の値である場合にテストにパスします。
|
||||
|
||||
```js
|
||||
assert.isTrue(true, 'This will pass with the boolean value true');
|
||||
assert.isTrue('true', 'This will NOT pass with the string value "true"');
|
||||
assert.isTrue(1, 'This will NOT pass with the number value 1');
|
||||
```
|
||||
|
||||
`isFalse()` と `isNotFalse()` もあり、ブール値 `false` かどうかを調べること以外は対応する true 版のメソッドと同様に動作します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Basic Assertions` スイート内の `#4` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isTrue` または `assert.isNotTrue` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `isTrue` もしくは `isNotTrue` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'isTrue', 'True is true');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isTrue` もしくは `isNotTrue` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'isTrue',
|
||||
'Double negation of a truthy value is true'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isTrue` もしくは `isNotTrue` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[2].method,
|
||||
'isNotTrue',
|
||||
'A truthy object is not true - neither is a false one'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 587d824d367417b2b2512c53
|
||||
title: 文字列に部分文字列が含まれているかどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301597
|
||||
dashedName: test-if-a-string-contains-a-substring
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`include()` と `notInclude()` は、文字列でも動作します! `include()` は、実際の文字列が期待される部分文字列を含んでいることをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Strings` スイート内の `#14` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.include` または `assert.notInclude` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `include` もしくは `notInclude` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'include',
|
||||
"'Arrow' contains 'row'..."
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `include` もしくは `notInclude` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'notInclude',
|
||||
"... a 'dart' doesn't contain a 'queue'"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: 587d824c367417b2b2512c4f
|
||||
title: 値が特定の範囲内かどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301598
|
||||
dashedName: test-if-a-value-falls-within-a-specific-range
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
```javascript
|
||||
.approximately(actual, expected, delta, [message])
|
||||
```
|
||||
|
||||
`actual` が `expected`に等しく +/- `delta` の範囲内であることをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Comparisons` スイート内の `#10` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.approximately` に変更してください。
|
||||
|
||||
テストを常に合格にするには、最小範囲 (3 番目のパラメータ) を選択してください。 範囲は、1 未満でなければなりません。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しい範囲を選ぶ必要があります - `approximately(actual, expected, range)` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'approximately');
|
||||
assert.equal(
|
||||
data.assertions[0].args[2],
|
||||
0.5,
|
||||
"weirdNumbers(0.5) is in the range (0.5, 1.5]. It's within 1 +/- 0.5"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しい範囲を選ぶ必要があります - `approximately(actual, expected, range)` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'approximately');
|
||||
assert.equal(
|
||||
data.assertions[1].args[2],
|
||||
0.8,
|
||||
"weirdNumbers(0.2) is in the range (0.2, 1.2]. It's within 1 +/- 0.8"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,93 @@
|
||||
---
|
||||
id: 587d824d367417b2b2512c52
|
||||
title: 値が文字列かどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301599
|
||||
dashedName: test-if-a-value-is-a-string
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`isString` または `isNotString` は、実際の値が文字列であることをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Strings` スイート内の `#13` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isString` または `assert.isNotString` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `isString` もしくは `isNotString` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'isNotString',
|
||||
'A float number is not a string'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります- `isString` もしくは `isNotString` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'isString',
|
||||
'environment vars are strings (or undefined)'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isString` もしくは `isNotString` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'isString', 'A JSON is a string');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,77 @@
|
||||
---
|
||||
id: 587d824d367417b2b2512c50
|
||||
title: 値が配列かどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301600
|
||||
dashedName: test-if-a-value-is-an-array
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Arrays` スイート内の `#11` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isArray` または `assert.isNotArray` に変更してください。 アサート に渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `isArray` もしくは `isNotArray`です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'isArray',
|
||||
'String.prototype.split() returns an Array'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isArray` もしくは `isNotArray` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'isNotArray',
|
||||
'Array.prototype.indexOf() returns a number'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,133 @@
|
||||
---
|
||||
id: 587d824e367417b2b2512c56
|
||||
title: 値が特定のデータ構造タイプかどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301601
|
||||
dashedName: test-if-a-value-is-of-a-specific-data-structure-type
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`#typeOf` は、値のタイプが、`Object.prototype.toString` で決まるとおりの与えられた文字列であることをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Objects` スイート内の `#17` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.typeOf` または `assert.notTypeOf` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `typeOf` もしくは `notTypeOf` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'typeOf',
|
||||
'myCar is typeOf Object'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `typeOf` もしくは `notTypeOf` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'typeOf',
|
||||
'Car.model is a String'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `typeOf` もしくは `notTypeOf` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[2].method,
|
||||
'notTypeOf',
|
||||
'Plane.wings is a Number (not a String)'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
4 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `typeOf` もしくは `notTypeOf`です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[3].method,
|
||||
'typeOf',
|
||||
'Plane.engines is an Array'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
5 番目のアサーションに対して、正しいメソッドを選択する必要があります - `typeOf` もしくは `notTypeOf` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[4].method,
|
||||
'typeOf',
|
||||
'Car.wheels is a Number'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,95 @@
|
||||
---
|
||||
id: 587d824b367417b2b2512c47
|
||||
title: 変数または関数が定義されているかどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301602
|
||||
dashedName: test-if-a-variable-or-function-is-defined
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Basic Assertions` スイート内の `#2` に分類されたテストにおいて、テストに合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isDefined()` または `assert.isUndefined()` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `isDefined` もしくは `isUndefined` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'isDefined',
|
||||
'Null is not undefined'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isDefined` もしくは `isUndefined` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'isUndefined',
|
||||
'Undefined is undefined'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isDefined` もしくは `isUndefined` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[2].method,
|
||||
'isDefined',
|
||||
'A string is not undefined'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,77 @@
|
||||
---
|
||||
id: 587d824d367417b2b2512c51
|
||||
title: 配列にアイテムが含まれているかどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301603
|
||||
dashedName: test-if-an-array-contains-an-item
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Arrays` スイート内の `#12` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.include` または `assert.notInclude` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `include` もしくは `notInclude` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'notInclude',
|
||||
"It's summer in july..."
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `include` もしくは `notInclude` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'include',
|
||||
'JavaScript is a backend language !!'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,93 @@
|
||||
---
|
||||
id: 587d824e367417b2b2512c55
|
||||
title: オブジェクトにプロパティがあるかどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301604
|
||||
dashedName: test-if-an-object-has-a-property
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`property` は、実際のオブジェクトに、与えられたプロパティがあることをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Objects` スイート内の `#16` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.property` または `assert.notProperty` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `property` もしくは `notProperty` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'notProperty',
|
||||
'A car has not wings'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `property` もしくは `notProperty` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'property',
|
||||
'planes have engines'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `property` もしくは `notProperty` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'property', 'Cars have wheels');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,115 @@
|
||||
---
|
||||
id: 587d824e367417b2b2512c57
|
||||
title: オブジェクトがコンストラクターのインスタンスであるかどうかをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301605
|
||||
dashedName: test-if-an-object-is-an-instance-of-a-constructor
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`#instanceOf` は、オブジェクトがコンストラクターのインスタンスであることをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Objects` スイート内の `#18` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.instanceOf` または `assert.notInstanceOf` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `instanceOf` もしくは `notInstanceOf` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'notInstanceOf',
|
||||
'myCar is not an instance of Plane'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `instanceOf` もしくは `notInstanceOf` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'instanceOf',
|
||||
'airlinePlane is an instance of Plane'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `instanceOf` もしくは `notInstanceOf` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[2].method,
|
||||
'instanceOf',
|
||||
'everything is an Object in JavaScript...'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
4 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `instanceOf` もしくは `notInstanceOf`です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[3].method,
|
||||
'notInstanceOf',
|
||||
'myCar.wheels is not an instance of String'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: 587d824c367417b2b2512c4e
|
||||
title: ある値が、他の値より小さいか、もしくは他の値以上かをテストする
|
||||
challengeType: 2
|
||||
forumTopicId: 301606
|
||||
dashedName: test-if-one-value-is-below-or-at-least-as-large-as-another
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Comparisons` スイート内の `#9` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isBelow` または `assert.isAtLeast` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `isBelow` もしくは `isAtLeast` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'isAtLeast',
|
||||
'5 is at least (>=) 5'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isBelow` もしくは `isAtLeast` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'isAtLeast',
|
||||
'2 * Math.random() is at least 0'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isBelow` もしくは `isAtLeast` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'isBelow', '1 is smaller than 2');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
4 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isBelow` もしくは `isAtLeast` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[3].method,
|
||||
'isBelow',
|
||||
'2/3 (0.6666) is smaller than 1'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: 587d824b367417b2b2512c48
|
||||
title: Assert.isOK と Assert.isNotOK を使用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301607
|
||||
dashedName: use-assert-isok-and-assert-isnotok
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`isOk()` は真値かどうかをテストし、 `isNotOk()` は偽値かどうかをテストします。
|
||||
|
||||
真値と偽値についてさらに学習するには、[Falsy Bouncer](https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-algorithm-scripting/falsy-bouncer) チャレンジに挑戦してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Basic Assertions` スイート内の `#3` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.isOk()` または `assert.isNotOk()` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `isOk` もしくは `isNotOk` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[0].method, 'isNotOk', 'Null is falsy');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isOk` もしくは `isNotOk` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'isOk', 'A string is truthy');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `isOk` もしくは `isNotOk` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[2].method, 'isOk', 'true is truthy');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 587d824d367417b2b2512c54
|
||||
title: 文字列をテストするために正規表現を使用する
|
||||
challengeType: 2
|
||||
forumTopicId: 301608
|
||||
dashedName: use-regular-expressions-to-test-a-string
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`match()` は、実際の値が 2 番目の引数の正規表現に一致することをアサートします。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Strings` スイート内の `#15` に分類されたテストにおいて、テストを合格にする (`true` と評価する必要があります) ために、それぞれの `assert` を `assert.match` または `assert.notMatch` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=14').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `match` もしくは `notMatch` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=14').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'match',
|
||||
"'# name:John Doe, age:35' matches the regex"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `match` もしくは `notMatch` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=14').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'notMatch',
|
||||
"'# name:Paul Smith III, age:twenty-four' does not match the regex (the age must be numeric)"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,111 @@
|
||||
---
|
||||
id: 587d824b367417b2b2512c4a
|
||||
title: ダブルイコールを使用して等価をアサートする
|
||||
challengeType: 2
|
||||
forumTopicId: 301609
|
||||
dashedName: use-the-double-equals-to-assert-equality
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`equal()` は、`==` を使用してオブジェクトを比較します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` の中の、`Equality` スイート内の `#5` に分類されたテストにおいて、テストを合格にする(`true` と評価する必要があります)ために、それぞれの `assert` を `assert.equal` または `assert.notEqual` に変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `equal` もしくは `notEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'equal',
|
||||
'Numbers are coerced into strings with == '
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `equal` もしくは `notEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[1].method,
|
||||
'notEqual',
|
||||
' == compares object references'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `equal` もしくは `notEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[2].method,
|
||||
'equal',
|
||||
"6 * '2' is 12 ! It should be equal to '12'"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
4 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `equal` もしくは `notEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[3].method, 'notEqual', "6 + '2' is '62'...");
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,111 @@
|
||||
---
|
||||
id: 587d824b367417b2b2512c4b
|
||||
title: トリプルイコールを使用して厳密等価をアサートする
|
||||
challengeType: 2
|
||||
forumTopicId: 301610
|
||||
dashedName: use-the-triple-equals-to-assert-strict-equality
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
注意点として、このプロジェクトは [Replit](https://replit.com/github/freeCodeCamp/boilerplate-mochachai) の始動プロジェクト、または [GitHub](https://github.com/freeCodeCamp/boilerplate-mochachai/) からクローンされたプロジェクトに基づいて構築されています。
|
||||
|
||||
`strictEqual()` は、`===` を使用してオブジェクトを比較します。
|
||||
|
||||
# --instructions--
|
||||
|
||||
`tests/1_unit-tests.js` 内で、`Equality` スイートの `#6` に分類されるテストにおいて、(`true` と評価する必要がある) テストに合格にするために、各 `assert` を `assert.strictEqual` または `assert.notStrictEqual` のいずれかに変更してください。 アサートに渡された引数を変更しないでください。
|
||||
|
||||
# --hints--
|
||||
|
||||
すべてのテストに合格する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(data.state, 'passed');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
最初のアサーションに対して、正しいメソッドを選ぶ必要があります - `strictEqual` もしくは `notStrictEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[0].method,
|
||||
'notStrictEqual',
|
||||
'with strictEqual the type must match'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `strictEqual` もしくは `notStrictEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(data.assertions[1].method, 'strictEqual', '3*2 = 6...');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
3 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `strictEqual` もしくは `notStrictEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[2].method,
|
||||
'strictEqual',
|
||||
"6 * '2' is 12. Types match !"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
4 番目のアサーションに対して、正しいメソッドを選ぶ必要があります - `strictEqual` もしくは `notStrictEqual` です。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.assertions[3].method,
|
||||
'notStrictEqual',
|
||||
'Even if they have the same elements, the Arrays are notStrictEqual'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,337 @@
|
||||
---
|
||||
id: 5e601c0d5ac9d0ecd8b94afe
|
||||
title: アメリカ英語とイギリス英語の変換機
|
||||
challengeType: 4
|
||||
forumTopicId: 462358
|
||||
dashedName: american-british-translator
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://american-british-translator.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-american-british-english-translator/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-american-british-english-translator)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
- すべてのロジックを `/components/translator.js` に含めてください。
|
||||
- `/routes/api.js` で `/api/translate` ルートを完成させてください。
|
||||
- `tests/1_unit-tests.js` および `tests/2_functional-tests.js` で、すべてのユニット/機能テストを作成してください。
|
||||
- アプリで変換すべきスペルと用語については、`/components` にある JavaScript ファイルを参照してください。
|
||||
- Replit でテストを実行するには、`.env` ファイル内で引用符を付けずに`NODE_ENV` を `test` に設定してください。
|
||||
- コンソールでテストを実行するには、コマンド `npm run test` を使用してください。 Replit コンソールを開くには、Ctrl+Shift+P (Macの場合はCmd) を押して「open shell」と入力してください。
|
||||
|
||||
`tests/1_unit-tests.js` に以下のテストを記述してください。
|
||||
|
||||
- `Mangoes are my favorite fruit.` をイギリス英語に変換してください
|
||||
- `I ate yogurt for breakfast.` をイギリス英語に変換してください
|
||||
- `We had a party at my friend's condo.` をイギリス英語に変換してください
|
||||
- `Can you toss this in the trashcan for me?` をイギリス英語に変換してください
|
||||
- `The parking lot was full.` をイギリス英語に変換してください
|
||||
- `Like a high tech Rube Goldberg machine.` をイギリス英語に変換してください
|
||||
- `To play hooky means to skip class or work.` をイギリス英語に変換してください
|
||||
- `No Mr. Bond, I expect you to die.` をイギリス英語に変換してください
|
||||
- `Dr. Grosh will see you now.` をイギリス英語に変換してください
|
||||
- `Lunch is at 12:15 today.` をイギリス英語に変換してください
|
||||
- `We watched the footie match for a while.` をイギリス英語に変換してください
|
||||
- `Paracetamol takes up to an hour to work.` をイギリス英語に変換してください
|
||||
- `First, caramelise the onions.` をイギリス英語に変換してください
|
||||
- `I spent the bank holiday at the funfair.` をイギリス英語に変換してください
|
||||
- `I had a bicky then went to the chippy.` をイギリス英語に変換してください
|
||||
- `I've just got bits and bobs in my bum bag.` をイギリス英語に変換してください
|
||||
- `The car boot sale at Boxted Airfield was called off.` をイギリス英語に変換してください
|
||||
- `Have you met Mrs Kalyani?` をイギリス英語に変換してください
|
||||
- `Prof Joyner of King's College, London.` をイギリス英語に変換してください
|
||||
- `Tea time is usually around 4 or 4.30.` をイギリス英語に変換してください
|
||||
- `Mangoes are my favorite fruit.` の変換をハイライト表示してください
|
||||
- `I ate yogurt for breakfast.` の変換をハイライト表示してください
|
||||
- `We watched the footie match for a while.` の変換をハイライト表示してください
|
||||
- `Paracetamol takes up to an hour to work.` の変換をハイライト表示してください
|
||||
|
||||
`tests/2_functional-tests.js` に以下のテストを記述してください。
|
||||
|
||||
- テキストとロケールフィールドの変換: `/api/translate` への POST リクエスト
|
||||
- テキストと無効なロケールフィールドの変換: `/api/translate` への POST リクエスト
|
||||
- 不足しているテキストフィールドの変換: `/api/translate` への POST リクエスト
|
||||
- 不足しているロケールフィールドの変換: `/api/translate` への POST リクエスト
|
||||
- 空のテキストの変換: `/api/translate` への POST リクエスト
|
||||
- 変換不要テキストの変換: `/api/translate` への POST リクエスト
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供することができます。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
assert(
|
||||
!/.*\/american-british-translator\.freecodecamp\.rocks/.test(
|
||||
getUserInput('url')
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
変換するテキストの `text` を含むボディと、`american-to-british` もしくは `british-to-american` のいずれかを含む `locale` を使用して、`/api/translate` へ `POST` できます。 返されるオブジェクトには、送信した `text` と、変換後のテキストを含む `translation` が含まれている必要があります。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/translate` ルートでは、時間をアメリカ英語とイギリス英語で記述する方法を使用する必要があります。 たとえば、10 時 30 分は、イギリス英語では「10.30」、アメリカ英語では「10:30」と記述します。 `span` 要素に時間文字列全体を含める必要があります。たとえば、`<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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/translate` ルートでは、肩書や敬語をアメリカ英語とイギリス英語で略記する方法も使用する必要があります。 たとえば、Doctor Wright は、イギリス英語では「Dr Wright」、アメリカ英語では「Dr. Wright」と 略記します。 アプリで使用すべきさまざまな肩書については、`/components/american-to-british-titles.js` を参照してください。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
変換されたスペルや用語を `<span class="highlight">...</span>` タグで囲み、緑色で表示します。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
1 つ以上の必須フィールドが存在しない場合は、`{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`text` が空の場合は、`{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`locale` が 2 つの指定されたロケールのいずれとも一致しない場合は、`{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`text` が変換を必要としない場合は、`translation` の値として `"Everything looks good to me!"` を返します。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
24 種類のテストがすべて完了し、合格しています。 テストを記述すべき期待される動作については、`/tests/1_unit-tests.js` を参照してください。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
6 種類の機能テストがすべて完了し、合格しています。 テストを記述すべき機能については、`/tests/2_functional-tests.js` を参照してください。
|
||||
|
||||
```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--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,369 @@
|
||||
---
|
||||
id: 587d8249367417b2b2512c42
|
||||
title: 課題トラッカー
|
||||
challengeType: 4
|
||||
forumTopicId: 301569
|
||||
dashedName: issue-tracker
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://issue-tracker.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-issuetracker/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-issuetracker)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
- `/routes/api.js` で、必要なルートを完成させてください。
|
||||
- `tests/2_functional-tests.js` にすべての機能テストを作成してください。
|
||||
- `sample.env` ファイルを `.env` にコピーし、変数を適切に設定してください。
|
||||
- テストを実行するには、`.env` ファイルの `NODE_ENV=test` をコメント解除してください。
|
||||
- コンソールでテストを実行するには、コマンド `npm run test` を使用してください。 Replit コンソールを開くには、Ctrl+Shift+P (Macの場合はCmd) を押して「open shell」と入力してください。
|
||||
|
||||
`tests/2_functional-tests.js` に以下のテストを記述してください。
|
||||
|
||||
- すべてのフィールドについて課題を作成してください: `/api/issues/{project}` への POST リクエスト
|
||||
- 必須フィールドのみについて課題を作成してください: `/api/issues/{project}` への POST リクエスト
|
||||
- 不足している必須フィールドについて課題を作成してください: `/api/issues/{project}` への POST リクエスト
|
||||
- プロジェクトの課題を表示してください: `/api/issues/{project}` への GET リクエスト
|
||||
- 1 つのフィルターでプロジェクトの課題を表示してください: `/api/issues/{project}` への GET リクエスト
|
||||
- 複数のフィルターでプロジェクトの課題を表示してください: `/api/issues/{project}` への GET リクエスト
|
||||
- 課題の 1 つのフィールドを更新してください: `/api/issues/{project}` への PUT リクエスト
|
||||
- 課題の複数フィールドを更新してください: `/api/issues/{project}` への PUT リクエスト
|
||||
- `_id` が不足している課題を更新してください: `/api/issues/{project}` への PUT リクエスト
|
||||
- 更新すべきフィールドがない課題を更新してください: `/api/issues/{project}` への PUT リクエスト
|
||||
- 無効な `_id` の課題を更新してください: `/api/issues/{project}` への PUT リクエスト
|
||||
- 課題を削除してください: `/api/issues/{project}` への DELETE リクエスト
|
||||
- 無効な `_id` の課題を削除してください: `/api/issues/{project}` への DELETE リクエスト
|
||||
- `_id` が不足している課題を削除してください: `/api/issues/{project}` への DELETE リクエスト
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供することができます。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
assert(!/.*\/issue-tracker\.freecodecamp\.rocks/.test(getUserInput('url')));
|
||||
};
|
||||
```
|
||||
|
||||
必須フィールド `issue_title`、`issue_text`、`created_by`、およびオプションフィールド `assigned_to` および `status_text` を含むフォームデータを使用して、`/api/issues/{projectname}` へ `POST` リクエストを送信することができます。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/issues/{projectname}` への `POST` リクエストは、作成されたオブジェクトを返します。また、送信したすべてのフィールドが含まれている必要があります。 除外されたオプションフィールドは空の文字列として返します。 さらに、`created_on` (日付/時間)、`updated_on` (日付/時間)、`open` (ブール値、open の場合はデフォルト値の `true`、closed の場合は `false`) および `_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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/issues/{projectname}` への `POST` リクエストを必須フィールドなしで送信した場合は、エラー `{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
特定の `projectname` に対するすべての課題の配列 (課題ごとにすべてのフィールドが存在します) に対して、`/api/issues/{projectname}` へ `GET` リクエストを送信できます。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/issues/{projectname}` へ `GET` リクエストを送信し、任意のフィールドと値を URL クエリとして渡すことにより、リクエストをフィルターで絞り込むことができます (例: `/api/issues/{project}?open=false`)。 1 つ以上のフィールド/値のペアを一度に渡すことができます。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`_id` と 1 つ 以上の更新すべきフィールドを指定して、`/api/issues/{projectname}` へ `PUT` リクエストを送信することができます。 成功した場合は、`updated_on` フィールドを更新し、 `{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/issues/{projectname}` へ送信した `PUT` リクエストに `_id` が含まれていない場合、戻り値は `{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`/api/issues/{projectname}` へ送信した `PUT` リクエストに更新フィールドが含まれていない 場合、戻り値は `{ error: 'no update field(s) sent', '_id': _id }` です。 それ以外のエラーでは、戻り値は `{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`_id` を指定して `/api/issues/{projectname}` へ `DELETE` リクエストを送信して、課題を削除することができます。 `_id` が送信されていない場合、戻り値は `{ error: 'missing _id' }` です。 成功した場合、戻り値は `{ result: 'successfully deleted', '_id': _id }` です。 失敗した場合、戻り値は `{ 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
14 種類の機能テストがすべて完了し、合格しています。
|
||||
|
||||
```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--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,318 @@
|
||||
---
|
||||
id: 587d8249367417b2b2512c41
|
||||
title: メートル法とヤード・ポンド法の換算機
|
||||
challengeType: 4
|
||||
forumTopicId: 301570
|
||||
dashedName: metric-imperial-converter
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://metric-imperial-converter.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [ GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-metricimpconverter/)をクローンし、ローカル環境でプロジェクトを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-metricimpconverter)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
- `/controllers/convertHandler.js` で、必要な変換ロジックを完成させてください。
|
||||
- `/routes/api.js` で、必要なルートを完成させてください。
|
||||
- `sample.env` ファイルを `.env` にコピーし、変数を適切に設定してください。
|
||||
- テストを実行するには、`.env` ファイルの `NODE_ENV=test` をコメント解除してください。
|
||||
- コンソールでテストを実行するには、コマンド `npm run test` を使用してください。 Replit コンソールを開くには、Ctrl+Shift+P (Macの場合はCmd) を押して「open shell」と入力してください。
|
||||
|
||||
`tests/1_unit-tests.js` に以下のテストを記述してください。
|
||||
|
||||
- `convertHandler` は、整数入力を正しく読み取る必要があります。
|
||||
- `convertHandler` は、小数入力を正しく読み取る必要があります。
|
||||
- `convertHandler` は、分数入力を正しく読み取る必要があります。
|
||||
- `convertHandler` は、小数による分数入力を正しく読み取る必要があります。
|
||||
- `convertHandler` は、二重分数 (`3/2/3` など) の場合にエラーを正しく返す必要があります。
|
||||
- 数字が入力されていない場合、`convertHandler` は、デフォルトで数字 `1` を正しく入力する必要があります。
|
||||
- `convertHandler` は、それぞれの有効な入力単位を正しく読み取る必要があります。
|
||||
- `convertHandler` は、無効な入力単位の場合にエラーを正しく返す必要があります。
|
||||
- `convertHandler` は、有効な入力単位ごとに正しい戻り値単位を返す必要があります。
|
||||
- `convertHandler` は、有効な入力単位ごとに文字列単位を略さずに正しく返す必要があります。
|
||||
- `convertHandler` は、`gal` を `L` に正しく変換する必要があります。
|
||||
- `convertHandler` は、`L` を `gal` に正しく変換する必要があります。
|
||||
- `convertHandler` は、`mi` を `km` に正しく変換する必要があります。
|
||||
- `convertHandler` は、`km` を `mi` に正しく変換する必要があります。
|
||||
- `convertHandler` は、`lbs` を `kg` に正しく変換する必要があります。
|
||||
- `convertHandler` は、`kg` を `lbs` に正しく変換する必要があります。
|
||||
|
||||
`tests/2_functional-tests.js` に以下のテストを記述してください。
|
||||
|
||||
- `10L` などの有効な入力を変換してください: `/api/convert` への `GET` リクエスト
|
||||
- `32g` などの無効な入力を変換してください: `/api/convert` への`GET` リクエスト
|
||||
- `3/7.2/4kg` などの無効な数字を変換してください: `/api/convert` への `GET` リクエスト
|
||||
- `3/7.2/4kilomegagram` などの無効な数字かつ単位を変換してください: `/api/convert` への `GET` リクエスト
|
||||
- `kg` などの数字のない入力を変換してください: `/api/convert` への `GET` リクエスト
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供することができます。
|
||||
|
||||
```js
|
||||
getUserInput => {
|
||||
assert(
|
||||
!/.*\/metric-imperial-converter\.freecodecamp\.rocks/.test(
|
||||
getUserInput('url')
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
受け取った数字と単位を含む単一のパラメータを設定して `/api/convert` への `GET` を実行し、変換を実行することができます。 (ヒント: 単位の始まりを示す最初の文字のインデックスを探して入力を分割してください)
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
`'gal'` を `'L'` に変換できます。その逆も可能です。 (1 gal を 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`'lbs'` を `'kg'` に変換できます。その逆も可能です。 (1 lbs を 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`'mi'` を `'km'` に変換できます。その逆も可能です。 (1 mi を 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
すべての入力単位は大文字と小文字の両方で受け入れられるようにする必要がありますが、小文字の `initUnit` と `returnUnit` で返す必要があります。ただし、liter のみは例外で、大文字の `'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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
尺度の単位が無効の場合は、`'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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
数値が無効の場合は、`'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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
単位と数値の両方が無効な場合は、`'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 になります。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`initNum`、`initUnit`、`returnNum`、`returnUnit` および `string` を返し、単位は `'{initNum} {initUnitString} converts to {returnNum} {returnUnitString}'` という形式でスペルアウトし、結果を小数点 5 桁に丸めます。
|
||||
|
||||
```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 種類のテストがすべて完了し、合格しています。
|
||||
|
||||
```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 種類の機能テストがすべて完了し、合格しています。
|
||||
|
||||
```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--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,220 @@
|
||||
---
|
||||
id: 587d824a367417b2b2512c43
|
||||
title: 個人図書館
|
||||
challengeType: 4
|
||||
forumTopicId: 301571
|
||||
dashedName: personal-library
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://personal-library.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freeCodeCamp/boilerplate-project-library)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-library)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
1. `.env` に、MongoDB 接続文字列を `DB` として引用符を使用せずに追加してください。例: `DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib`
|
||||
2. `.env` ファイルで、引用符を使用せずに `NODE_ENV` に `test` を設定してください。
|
||||
3. `routes/api.js` 内に、すべてのルートを作成する必要があります。
|
||||
4. `tests/2_functional-tests.js` に、すべての機能テストを作成します。
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供することができます。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
assert(
|
||||
!/.*\/personal-library\.freecodecamp\.rocks/.test(getUserInput('url'))
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
フォームデータの一部として `title` を指定して、`/api/books` へ <b>POST</b> リクエストを送信し、ブックを追加することができます。 返されるレスポンスは、`title` と一意の `_id` をキーとして持つオブジェクトになります。 `title` がリクエストに含まれていない場合、返されるレスポンスは文字列 `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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<b>GET</b> リクエストを `/api/books` へ送信し、すべてのブックを表す JSON レスポンスを受け取ることができます。 JSON レスポンスはオブジェクトの配列であり、それぞれのオブジェクト (ブック) に `title`、`_id` および `commentcount` プロパティが含まれます。
|
||||
|
||||
```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' });
|
||||
await 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<b>GET</b> リクエストを `/api/books/{_id}` へ送信して、プロパティ `title`、`_id` および `comments` 配列 (コメントがない場合は、空の配列) を含むブックの単一のオブジェクトを取得できます。 ブックが見つからない場合は、文字列 `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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`comment` を含む フォームボディデータとして指定して、<b>POST</b> リクエストを `/api/books/{_id}` へ送信し、ブックにコメントを追加することができます。 返されるレスポンスは、前述のテストの <b>GET</b> `/api/books/{_id}` リクエストと同様のブックオブジェクトになります。 `comment` がリクエストに含まれていない場合は、文字列 `missing required field comment` を返してください。 ブックが見つからない場合は、文字列 `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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<b>DELETE</b> リクエストを `/api/books/{_id}` へ送信して、コレクションからブックを削除できます。 成功した場合、文字列 `delete successful` のレスポンスを返します。 ブックが見つからない場合、文字列 `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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<b>DELETE</b> リクエストを `/api/books` へ送信して、データベース内のすべてのブックを削除することができます。 成功した場合、文字列 `'complete delete 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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
10 種類の必須の機能テストがすべて完了し、合格しています。
|
||||
|
||||
```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--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
@@ -0,0 +1,379 @@
|
||||
---
|
||||
id: 5e601bf95ac9d0ecd8b94afd
|
||||
title: 数独ソルバー
|
||||
challengeType: 4
|
||||
forumTopicId: 462357
|
||||
dashedName: sudoku-solver
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
<https://sudoku-solver.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。 プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
|
||||
|
||||
- [GitHub レポジトリ](https://github.com/freecodecamp/boilerplate-project-sudoku-solver)をクローンし、ローカル環境でチャレンジを完了させる。
|
||||
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-sudoku-solver)を使用して、プロジェクトを完了させる。
|
||||
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
|
||||
|
||||
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
|
||||
|
||||
# --instructions--
|
||||
|
||||
- すべてのパズルロジックを `/controllers/sudoku-solver.js` に含めてください。
|
||||
- `validate` 関数は、与えられたパズル文字列を受け取り、入力に 81 の有効な文字があるかどうかを確認する必要があります。
|
||||
- `check` 関数は、ボードの*現在の*状態を確認する必要があります。
|
||||
- `solve` 関数は、テスト入力と解答だけでなく、与えられた任意の有効なパズル文字列を解く処理を行う必要があります。 パズルを解くためのロジックを記述することが求められます。
|
||||
- すべてのルーティングロジックを `/routes/api.js` に含めてください。
|
||||
- アプリで解くべきパズルのサンプルについては、`/controllers` の `puzzle-strings.js` ファイルを参照してください。
|
||||
- このページでチャレンジテストを実行するには、`.env` ファイル内で引用符を付けずに `NODE_ENV` を `test` に設定してください。
|
||||
- コンソールでテストを実行するには、コマンド `npm run test` を使用してください。 Replit コンソールを開くには、Ctrl+Shift+P (Macの場合はCmd) を押して「open shell」と入力してください。
|
||||
|
||||
`tests/1_unit-tests.js` に以下のテストを記述してください。
|
||||
|
||||
- ロジックは、81 文字の有効なパズル文字列を処理します。
|
||||
- ロジックは、(1~9 でも `.` でもない) 無効な文字が含まれているパズル文字列を処理します。
|
||||
- ロジックは、81 文字ではないパズル文字列を処理します。
|
||||
- ロジックは、有効な行の配置を処理します。
|
||||
- ロジックは、無効な行の配置を処理します。
|
||||
- ロジックは、有効な列の配置を処理します。
|
||||
- ロジックは、無効な列の配置を処理します。
|
||||
- ロジックは、有効な領域 (3x3 グリッド) の配置を処理します。
|
||||
- ロジックは、無効な領域 (3x3 グリッド) の配置を処理します。
|
||||
- 有効なパズルの文字列は、ソルバーをパスします。
|
||||
- 無効なパズル文字列は、ソルバーをパスしません。
|
||||
- 不完全なパズルの場合、ソルバーは期待される解答を返します。
|
||||
|
||||
`tests/2_functional-tests.js` に以下のテストを記述してください。
|
||||
|
||||
- 有効なパズル文字列のパズルを解いてください: `/api/solve` への POST リクエスト
|
||||
- パズル文字列が不足しているパズルを解いてください: `/api/solve` への POST リクエスト
|
||||
- 無効な文字のパズルを解いてください: `/api/solve` への POST リクエスト
|
||||
- 誤った長さのパズルを解いてください: `/api/solve` への POST リクエスト
|
||||
- 解くことができないパズルを解いてください: `/api/solve` への POST リクエスト
|
||||
- すべてのフィールドのパズル配置を確認してください: `/api/check` への POST リクエスト
|
||||
- 1 つの配置が競合しているパズル配置を確認してください: `/api/check` への POST リクエスト
|
||||
- 複数の配置が競合しているパズル配置を確認してください: `/api/check` への POST リクエスト
|
||||
- すべての配置が競合しているパズルの配置を確認してください: `/api/check` への POST リクエスト
|
||||
- 必須フィールドがないパズル配置を確認してください: `/api/check`への POST リクエスト
|
||||
- 無効な文字のパズル配置を確認してください: `/api/check` への POST リクエスト
|
||||
- 誤った長さのパズル配置を確認してください: `/api/check` への POST リクエスト
|
||||
- 無効な配置座標のパズル配置を確認してください: `/api/check` への POST リクエスト
|
||||
- 無効な配置値のパズル配置を確認してください: `/api/check` への POST リクエスト
|
||||
|
||||
# --hints--
|
||||
|
||||
サンプルの URL ではなく、自分で作成したプロジェクトを提供する必要があります。
|
||||
|
||||
```js
|
||||
(getUserInput) => {
|
||||
const url = getUserInput('url');
|
||||
assert(!/.*\/sudoku-solver\.freecodecamp\.rocks/.test(getUserInput('url')));
|
||||
};
|
||||
```
|
||||
|
||||
数字 (1~9) と空白を表すピリオド `.` の組み合わせを含む文字列である `puzzle` を含むフォームデータを指定して、`POST` `/api/solve` を実行することができます。 返されるオブジェクトには、解いたパズルを含む `solution` プロパティが含まれます。
|
||||
|
||||
```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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/solve` へ送信されたオブジェクトに `puzzle` がない場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/solve` へ送信されたパズルに数字でもピリオドでもない値が含まれている場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/solve` へ送信されたパズルの文字数が 81 文字より多いまたは少ない場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/solve` へ送信されたパズルが無効もしくは解けない場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/check` オブジェクトへの `POST` で、`puzzle`、`coordinate` および `value` を含むオブジェクトを指定できます。`coordinate` は行を示す文字 A~I で、その後に列を示す 1~9 の数字が続きます。`value` は 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/check` への `POST` の戻り値は、`valid` プロパティを含むオブジェクトになります。このプロパティは、指定された座標に数字を配置できる場合は `true` になり、そうでない場合は `false` になります。 false の場合、返されるオブジェクトには `conflict` プロパティも含まれます。このプロパティは、文字列 `"row"`、`"column"`、`"region"` の任意の組み合わせを含む配列になります (どれが配置を無効にしているのかによります)。
|
||||
|
||||
```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');
|
||||
};
|
||||
```
|
||||
|
||||
`/api/check` へ送信された `value` がすでに `puzzle` のその `coordinate` に配置されている場合、戻り値は `valid` プロパティを含むオブジェクトになり、`value` が競合していなければ `true` となります。
|
||||
|
||||
```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 = 'C3';
|
||||
const value = '2';
|
||||
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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/check` へ送信されたパズルに数字でもピリオドでもない値が含まれている場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/check` へ送信されたパズルが 81 文字より多いか少ない場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/check` へ送信されたオブジェクトに `puzzle`、`coordinate` もしくは `value` がない場合、戻り値は、`{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`api/check` へ送信された座標が既存のグリッドセルを指し示していない場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
`/api/check` へ送信された `value` が 1 から 9 の数字でない場合、戻り値は `{ 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);
|
||||
};
|
||||
```
|
||||
|
||||
12 種類のテストがすべて完了し、合格しています。 テストを記述すべき期待される動作については、`/tests/1_unit-tests.js` を参照してください。
|
||||
|
||||
```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);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
14 種類の機能テストがすべて完了し、合格しています。 テストを記述すべき機能については、`/tests/2_functional-tests.js` を参照してください。
|
||||
|
||||
```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--
|
||||
|
||||
```js
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
```
|
Reference in New Issue
Block a user