Merge branch 'staging' into fix/update-config-for-debug-setups
This commit is contained in:
@ -251,7 +251,7 @@ and ensure all tests pass.
|
|||||||
### Squash Your Commits
|
### Squash Your Commits
|
||||||
When you make a pull request, all of your changes need to be in one commit.
|
When you make a pull request, all of your changes need to be in one commit.
|
||||||
|
|
||||||
If you have made more then one commit, then you will need to _squash_ your commits.
|
If you have made more than one commit, then you will need to _squash_ your commits.
|
||||||
|
|
||||||
To do this, see [Squashing Your Commits](http://forum.freecodecamp.com/t/how-to-squash-multiple-commits-into-one-with-git/13231).
|
To do this, see [Squashing Your Commits](http://forum.freecodecamp.com/t/how-to-squash-multiple-commits-into-one-with-git/13231).
|
||||||
|
|
||||||
@ -271,7 +271,7 @@ related issues in our [Contributors chat room](https://gitter.im/FreeCodeCamp/Co
|
|||||||
|
|
||||||
#### Important: ALWAYS EDIT ON A BRANCH
|
#### Important: ALWAYS EDIT ON A BRANCH
|
||||||
|
|
||||||
Take away only one thing from this document, it should be this: Never, **EVER**
|
Take away only one thing from this document: Never, **EVER**
|
||||||
make edits to the `staging` branch. ALWAYS make a new branch BEFORE you edit
|
make edits to the `staging` branch. ALWAYS make a new branch BEFORE you edit
|
||||||
files. This is critical, because if your PR is not accepted, your copy of
|
files. This is critical, because if your PR is not accepted, your copy of
|
||||||
staging will be forever sullied and the only way to fix it is to delete your
|
staging will be forever sullied and the only way to fix it is to delete your
|
||||||
@ -357,7 +357,7 @@ for further information
|
|||||||
5. In the body of your PR include a more detailed summary of the changes you
|
5. In the body of your PR include a more detailed summary of the changes you
|
||||||
made and why.
|
made and why.
|
||||||
|
|
||||||
- If the PR is meant to fix an existing bug/issue, then, at the end of
|
- If the PR is meant to fix an existing bug/issue then, at the end of
|
||||||
your PR's description, append the keyword `closes` and #xxxx (where xxxx
|
your PR's description, append the keyword `closes` and #xxxx (where xxxx
|
||||||
is the issue number). Example: `closes #1337`. This tells GitHub to
|
is the issue number). Example: `closes #1337`. This tells GitHub to
|
||||||
close the existing issue, if the PR is merged.
|
close the existing issue, if the PR is merged.
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
z-index: 1030;
|
||||||
background-color: @modal-backdrop-bg;
|
background-color: @modal-backdrop-bg;
|
||||||
// Fade for backdrop
|
// Fade for backdrop
|
||||||
&.fade { .opacity(0); }
|
&.fade { .opacity(0); }
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
//## Night mode styles
|
//## Night mode styles
|
||||||
@night-body-bg: @gray-dark;
|
@night-body-bg: @gray-dark;
|
||||||
@night-text-color: #999;
|
@night-text-color: #ccc;
|
||||||
|
|
||||||
//== Typography
|
//== Typography
|
||||||
//
|
//
|
||||||
|
@ -1088,7 +1088,7 @@ code {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
a, .input-group-addon, .challenge-instructions a, .challenge-instructions #MDN-links a {
|
a, .input-group-addon, .challenge-instructions a, .challenge-instructions #MDN-links a {
|
||||||
color: #CCC;
|
color: #f8f8f8;
|
||||||
}
|
}
|
||||||
.fa-github-square {
|
.fa-github-square {
|
||||||
color: @brand-primary;
|
color: @brand-primary;
|
||||||
|
@ -49,13 +49,13 @@ const throwForJsHtml = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
name: 'gomix in code',
|
name: 'glitch in code',
|
||||||
description: 'Code with the URL gomix.me ' +
|
description: 'Code with the URL glitch.com or glitch.me' +
|
||||||
'should not be allowed to run',
|
'should not be allowed to run',
|
||||||
detectGomixInCode: /gomix\.me/gi,
|
detectGlitchInCode: /glitch\.(com|me)/gi,
|
||||||
thrower: function checkForGomix({ contents }) {
|
thrower: function checkForGlitch({ contents }) {
|
||||||
if (contents.match(this.detectGomixInCode)) {
|
if (contents.match(this.detectGlitchInCode)) {
|
||||||
throw new Error('Gomix.me should not be in the code');
|
throw new Error('Glitch.com or Glitch.me should not be in the code');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
.max-element-height(up-to) {
|
.max-element-height(up-to) {
|
||||||
max-height: e(%('calc(100vh - %s)', @navbar-total-height));
|
max-height: e(%('calc(100vh - %s)', @navbar-total-height));
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.max-element-height(always) {
|
.max-element-height(always) {
|
||||||
height: e(%('calc(100vh - %s)', @navbar-total-height));
|
height: e(%('calc(100vh - %s)', @navbar-total-height));
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.@{ns}-instructions-panel {
|
.@{ns}-instructions-panel {
|
||||||
|
123
gulpfile.js
123
gulpfile.js
@ -1,9 +1,10 @@
|
|||||||
// enable debug for gulp
|
// enable debug for gulp
|
||||||
|
/* eslint-disable prefer-object-spread/prefer-object-spread */
|
||||||
process.env.DEBUG = process.env.DEBUG || 'fcc:*';
|
process.env.DEBUG = process.env.DEBUG || 'fcc:*';
|
||||||
require('dotenv').load();
|
require('dotenv').load();
|
||||||
|
|
||||||
require('babel-core/register');
|
require('babel-core/register');
|
||||||
var Rx = require('rx'),
|
const Rx = require('rx'),
|
||||||
gulp = require('gulp'),
|
gulp = require('gulp'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
debug = require('debug')('fcc:gulp'),
|
debug = require('debug')('fcc:gulp'),
|
||||||
@ -50,19 +51,28 @@ var Rx = require('rx'),
|
|||||||
tapSpec = require('tap-spec');
|
tapSpec = require('tap-spec');
|
||||||
|
|
||||||
Rx.config.longStackSupport = true;
|
Rx.config.longStackSupport = true;
|
||||||
var sync = browserSync.create('fcc-sync-server');
|
const sync = browserSync.create('fcc-sync-server');
|
||||||
|
|
||||||
|
function resolve(filepath, thisString, withThisString) {
|
||||||
|
const newPath = require.resolve(filepath);
|
||||||
|
if (thisString && withThisString) {
|
||||||
|
return newPath.replace(thisString, withThisString);
|
||||||
|
}
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
|
||||||
// user definable
|
// user definable
|
||||||
var __DEV__ = !yargs.argv.p;
|
const __DEV__ = !yargs.argv.p;
|
||||||
var host = process.env.HOST || 'localhost';
|
const host = process.env.HOST || 'localhost';
|
||||||
var port = yargs.argv.port || process.env.PORT || '3001';
|
const port = yargs.argv.port || process.env.PORT || '3001';
|
||||||
var syncPort = yargs.argv['sync-port'] || process.env.SYNC_PORT || '3000';
|
const syncPort = yargs.argv['sync-port'] || process.env.SYNC_PORT || '3000';
|
||||||
|
|
||||||
// make sure sync ui port does not interfere with proxy port
|
// make sure sync ui port does not interfere with proxy port
|
||||||
var syncUIPort = yargs.argv['sync-ui-port'] ||
|
const syncUIPort = yargs.argv['sync-ui-port'] ||
|
||||||
process.env.SYNC_UI_PORT ||
|
process.env.SYNC_UI_PORT ||
|
||||||
parseInt(syncPort, 10) + 2;
|
parseInt(syncPort, 10) + 2;
|
||||||
|
|
||||||
var paths = {
|
const paths = {
|
||||||
server: './server/server.js',
|
server: './server/server.js',
|
||||||
serverIgnore: [
|
serverIgnore: [
|
||||||
'gulpfile.js',
|
'gulpfile.js',
|
||||||
@ -89,32 +99,36 @@ var paths = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
vendorChallenges: [
|
vendorChallenges: [
|
||||||
require.resolve('jshint').replace('src', 'dist'),
|
resolve('jshint', 'src', 'dist'),
|
||||||
require.resolve('chai').replace('index.js', 'chai.js'),
|
resolve('chai', 'index.js', 'chai.js'),
|
||||||
require.resolve('codemirror'),
|
resolve('codemirror'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'addon/comment/comment.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'addon/comment/comment.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'addon/edit/closebrackets.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'addon/edit/closebrackets.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'addon/edit/matchbrackets.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'addon/edit/matchbrackets.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'addon/lint/lint.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'addon/lint/lint.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'addon/lint/javascript-lint.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'addon/lint/javascript-lint.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'mode/javascript/javascript.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'mode/javascript/javascript.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'mode/xml/xml.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'mode/xml/xml.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'mode/css/css.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'mode/css/css.js'),
|
||||||
require.resolve('codemirror').replace('lib/codemirror.js', 'mode/htmlmixed/htmlmixed.js'),
|
resolve('codemirror', 'lib/codemirror.js', 'mode/htmlmixed/htmlmixed.js'),
|
||||||
require.resolve('emmet-codemirror'),
|
resolve('emmet-codemirror'),
|
||||||
'public/js/lib/loop-protect/loop-protect.js'
|
'public/js/lib/loop-protect/loop-protect.js'
|
||||||
],
|
],
|
||||||
|
|
||||||
vendorMain: [
|
vendorMain: [
|
||||||
require.resolve('jquery').replace('.js', '.min.js'),
|
resolve('jquery', '.js', '.min.js'),
|
||||||
require.resolve('bootstrap').replace('npm.js', 'bootstrap.min.js'),
|
resolve('bootstrap', 'npm.js', 'bootstrap.min.js'),
|
||||||
require.resolve('d3').replace('.js', '.min.js'),
|
resolve('d3', '.js', '.min.js'),
|
||||||
require.resolve('cal-heatmap'),
|
resolve('cal-heatmap'),
|
||||||
require.resolve('moment').replace('.js', '.min.js'),
|
resolve('moment', '.js', '.min.js'),
|
||||||
require.resolve('moment-timezone').replace('index.js', 'builds/moment-timezone-with-data.min.js'),
|
resolve(
|
||||||
require.resolve('mousetrap').replace('.js', '.min.js'),
|
'moment-timezone',
|
||||||
require.resolve('lightbox2').replace('.js', '.min.js'),
|
'index.js',
|
||||||
require.resolve('rx').replace('index.js', 'dist/rx.all.min.js')
|
'builds/moment-timezone-with-data.min.js'
|
||||||
|
),
|
||||||
|
resolve('mousetrap', '.js', '.min.js'),
|
||||||
|
resolve('lightbox2', '.js', '.min.js'),
|
||||||
|
resolve('rx', 'index.js', 'dist/rx.all.min.js')
|
||||||
],
|
],
|
||||||
|
|
||||||
js: [
|
js: [
|
||||||
@ -145,19 +159,18 @@ var paths = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
var webpackOptions = {
|
const webpackOptions = {
|
||||||
devtool: 'inline-source-map'
|
devtool: 'inline-source-map'
|
||||||
};
|
};
|
||||||
|
|
||||||
function errorHandler() {
|
const errorNotifier = notify.onError({
|
||||||
var args = Array.prototype.slice.call(arguments);
|
|
||||||
|
|
||||||
// Send error to notification center with gulp-notify
|
|
||||||
notify.onError({
|
|
||||||
title: 'Compile Error',
|
title: 'Compile Error',
|
||||||
message: '<%= error %>'
|
message: '<%= error %>'
|
||||||
}).apply(this, args);
|
});
|
||||||
|
|
||||||
|
function errorHandler(...args) {
|
||||||
|
// Send error to notification center with gulp-notify
|
||||||
|
errorNotifier.apply(this, args);
|
||||||
// Keep gulp from hanging on this task
|
// Keep gulp from hanging on this task
|
||||||
this.emit('end');
|
this.emit('end');
|
||||||
}
|
}
|
||||||
@ -174,7 +187,7 @@ function delRev(dest, manifestName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gulp.task('serve', function(cb) {
|
gulp.task('serve', function(cb) {
|
||||||
var called = false;
|
let called = false;
|
||||||
nodemon({
|
nodemon({
|
||||||
script: paths.server,
|
script: paths.server,
|
||||||
ext: '.jsx .js .json',
|
ext: '.jsx .js .json',
|
||||||
@ -199,7 +212,7 @@ gulp.task('serve', function(cb) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var syncDepenedents = [
|
const syncDepenedents = [
|
||||||
'serve',
|
'serve',
|
||||||
'js',
|
'js',
|
||||||
'less'
|
'less'
|
||||||
@ -211,7 +224,7 @@ gulp.task('dev-server', syncDepenedents, function() {
|
|||||||
'webpack-hot-middleware/client'
|
'webpack-hot-middleware/client'
|
||||||
].concat(webpackConfig.entry.bundle);
|
].concat(webpackConfig.entry.bundle);
|
||||||
|
|
||||||
var bundler = webpack(webpackConfig);
|
const bundler = webpack(webpackConfig);
|
||||||
sync.init(null, {
|
sync.init(null, {
|
||||||
ui: {
|
ui: {
|
||||||
port: syncUIPort
|
port: syncUIPort
|
||||||
@ -261,14 +274,14 @@ gulp.task('pack-client', function() {
|
|||||||
if (!__DEV__) { console.log('\n\nbundling production\n\n'); }
|
if (!__DEV__) { console.log('\n\nbundling production\n\n'); }
|
||||||
|
|
||||||
function condition(file) {
|
function condition(file) {
|
||||||
var filepath = file.relative;
|
const filepath = file.relative;
|
||||||
return __DEV__ || (/json$/).test('' + filepath);
|
return __DEV__ || (/json$/).test('' + filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
var dest = webpackConfig.output.path;
|
const dest = webpackConfig.output.path;
|
||||||
|
|
||||||
return gulp.src(webpackConfig.entry.bundle)
|
return gulp.src(webpackConfig.entry.bundle)
|
||||||
.pipe(plumber({ errorHandler: errorHandler }))
|
.pipe(plumber({ errorHandler }))
|
||||||
.pipe(webpackStream(Object.assign(
|
.pipe(webpackStream(Object.assign(
|
||||||
{},
|
{},
|
||||||
webpackConfig,
|
webpackConfig,
|
||||||
@ -278,15 +291,15 @@ gulp.task('pack-client', function() {
|
|||||||
.pipe(gulp.dest(dest));
|
.pipe(gulp.dest(dest));
|
||||||
});
|
});
|
||||||
|
|
||||||
var webpackManifestFiles = [ 'react-manifest.json', 'chunk-manifest.json' ];
|
const webpackManifestFiles = [ 'react-manifest.json', 'chunk-manifest.json' ];
|
||||||
gulp.task('move-webpack-manifest', ['pack-client'], function() {
|
gulp.task('move-webpack-manifest', ['pack-client'], function() {
|
||||||
var files = webpackManifestFiles.map(function(filename) {
|
const files = webpackManifestFiles.map(function(filename) {
|
||||||
return path.join(webpackConfig.output.path, filename);
|
return path.join(webpackConfig.output.path, filename);
|
||||||
});
|
});
|
||||||
return gulp.src(files).pipe(gulp.dest(paths.manifest));
|
return gulp.src(files).pipe(gulp.dest(paths.manifest));
|
||||||
});
|
});
|
||||||
|
|
||||||
var cleanDeps = ['pack-client', 'move-webpack-manifest'];
|
const cleanDeps = ['pack-client', 'move-webpack-manifest'];
|
||||||
gulp.task('clean-webpack-manifest', cleanDeps, function() {
|
gulp.task('clean-webpack-manifest', cleanDeps, function() {
|
||||||
return del(webpackManifestFiles.map(function(filename) {
|
return del(webpackManifestFiles.map(function(filename) {
|
||||||
return path.join(webpackConfig.output.path, filename);
|
return path.join(webpackConfig.output.path, filename);
|
||||||
@ -300,10 +313,10 @@ gulp.task('clean-webpack-manifest', cleanDeps, function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('less', function() {
|
gulp.task('less', function() {
|
||||||
var manifestName = 'css-manifest.json';
|
const manifestName = 'css-manifest.json';
|
||||||
var dest = paths.css;
|
const dest = paths.css;
|
||||||
return gulp.src(paths.less)
|
return gulp.src(paths.less)
|
||||||
.pipe(plumber({ errorHandler: errorHandler }))
|
.pipe(plumber({ errorHandler }))
|
||||||
.pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
|
.pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
|
||||||
// compile
|
// compile
|
||||||
.pipe(less({
|
.pipe(less({
|
||||||
@ -343,10 +356,10 @@ function getFilesGlob(files) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gulp.task('js', function() {
|
gulp.task('js', function() {
|
||||||
var manifestName = 'js-manifest.json';
|
const manifestName = 'js-manifest.json';
|
||||||
var dest = paths.publicJs;
|
const dest = paths.publicJs;
|
||||||
|
|
||||||
var jsFiles = merge(
|
const jsFiles = merge(
|
||||||
|
|
||||||
gulp.src(getFilesGlob(paths.vendorMain))
|
gulp.src(getFilesGlob(paths.vendorMain))
|
||||||
.pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
|
.pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
|
||||||
@ -368,7 +381,7 @@ gulp.task('js', function() {
|
|||||||
),
|
),
|
||||||
|
|
||||||
gulp.src(paths.js)
|
gulp.src(paths.js)
|
||||||
.pipe(plumber({ errorHandler: errorHandler }))
|
.pipe(plumber({ errorHandler }))
|
||||||
.pipe(babel())
|
.pipe(babel())
|
||||||
.pipe(__DEV__ ? gutil.noop() : uglify())
|
.pipe(__DEV__ ? gutil.noop() : uglify())
|
||||||
);
|
);
|
||||||
@ -398,7 +411,7 @@ function done(manifest) {
|
|||||||
return sortKeys(manifest);
|
return sortKeys(manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
var buildDependents = [
|
const buildDependents = [
|
||||||
'less',
|
'less',
|
||||||
'js',
|
'js',
|
||||||
'pack-client',
|
'pack-client',
|
||||||
@ -420,7 +433,7 @@ gulp.task('build', [
|
|||||||
'build-manifest'
|
'build-manifest'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var watchDependents = [
|
const watchDependents = [
|
||||||
'less',
|
'less',
|
||||||
'js',
|
'js',
|
||||||
'serve',
|
'serve',
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"lint-resources": "jsonlint-cli server/resources/*.json",
|
"lint-resources": "jsonlint-cli server/resources/*.json",
|
||||||
"lint-utils": "jsonlint-cli server/utils/*.json",
|
"lint-utils": "jsonlint-cli server/utils/*.json",
|
||||||
"prelint-js": "npm run create-rev",
|
"prelint-js": "npm run create-rev",
|
||||||
"lint-js": "eslint --ext=.js,.jsx server/ common/ config/ client/",
|
"lint-js": "eslint --ext=.js,.jsx gulpfile.js server/ common/ config/ client/",
|
||||||
"lint-json": "npm run lint-server && npm run lint-challenges && npm run lint-resources && npm run lint-utils",
|
"lint-json": "npm run lint-server && npm run lint-challenges && npm run lint-resources && npm run lint-utils",
|
||||||
"pretest": "npm run create-rev && npm run lint",
|
"pretest": "npm run create-rev && npm run lint",
|
||||||
"snyk-protect": "snyk protect",
|
"snyk-protect": "snyk protect",
|
||||||
|
@ -658,7 +658,7 @@
|
|||||||
[
|
[
|
||||||
"//i.imgur.com/OmRmLB4.gif",
|
"//i.imgur.com/OmRmLB4.gif",
|
||||||
"A gif showing you how to click the link below to go to our chat room and click the \"sign in with GitHub\" button. Then you can click into the text input field and type a message to your fellow campers.",
|
"A gif showing you how to click the link below to go to our chat room and click the \"sign in with GitHub\" button. Then you can click into the text input field and type a message to your fellow campers.",
|
||||||
"Now that you have a GitHub account, you can join our main chat room by logging in with GitHub. Introduce yourself by saying \"Hello world!\" Tell your fellow campers how you found freeCodeCamp. Also tell us why you want to learn to code.",
|
"Now that you have a GitHub account, you can join our main chat room by logging in with GitHub. Please do not sign in with Twitter. Introduce yourself by saying \"Hello world!\" Tell your fellow campers how you found freeCodeCamp. Also tell us why you want to learn to code.",
|
||||||
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -721,7 +721,7 @@
|
|||||||
[
|
[
|
||||||
"//i.imgur.com/OmRmLB4.gif",
|
"//i.imgur.com/OmRmLB4.gif",
|
||||||
"Ein GIF das dir zeigt wie der unten stehende Link geklickt, auf den Chat weitergeleitet und mit dem \"Melde dich mit GitHub an\" Button man angemeldet wird. Dann kannst du auf das Textfeld klicken und eine Nachricht an andere Camper schicken",
|
"Ein GIF das dir zeigt wie der unten stehende Link geklickt, auf den Chat weitergeleitet und mit dem \"Melde dich mit GitHub an\" Button man angemeldet wird. Dann kannst du auf das Textfeld klicken und eine Nachricht an andere Camper schicken",
|
||||||
"Jetzt, da du einen GitHub Account besitzt, kannst du unserem Hauptchat beitreten indem du dich mit GitHub anmeldest. Stell dich vor indem du \"Hello World\" schreibst. Erzähl den anderen Campern wie du freeCodeCamp gefunden hast. Außerdem könntest du uns erzählen warum du programmieren lernen willst.",
|
"Jetzt, da du einen GitHub Account besitzt, kannst du unserem Hauptchat beitreten indem du dich mit GitHub anmeldest. Bitte melde dich nicht mit Twitter an. Stell dich vor indem du \"Hello World\" schreibst. Erzähl den anderen Campern wie du freeCodeCamp gefunden hast. Außerdem könntest du uns erzählen warum du programmieren lernen willst.",
|
||||||
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -781,7 +781,7 @@
|
|||||||
[
|
[
|
||||||
"//i.imgur.com/OmRmLB4.gif",
|
"//i.imgur.com/OmRmLB4.gif",
|
||||||
"Un gif mostrándote como pulsar en el enlace inferior para ir a nuestra sala de chat y pulsar en el botón de \"iniciar sesión con GitHub\". Entonces puedes pulsar en el próximo campo de texto y escribir un mensaje a tus compañeros campistas.",
|
"Un gif mostrándote como pulsar en el enlace inferior para ir a nuestra sala de chat y pulsar en el botón de \"iniciar sesión con GitHub\". Entonces puedes pulsar en el próximo campo de texto y escribir un mensaje a tus compañeros campistas.",
|
||||||
"Ahora que tienes una cuenta de GitHub, puedes unirte a nuestro chat principal al iniciar sesión con GitHub. Preséntate diciendo \"Hello world!\" Cuéntales a los tus compañeros campistas como encontraste freeCodeCamp. Además cuéntanos porque deseas aprender a programar.",
|
"Ahora que tienes una cuenta de GitHub, puedes unirte a nuestro chat principal al iniciar sesión con GitHub. Por favor no ingrese con Twitter. Preséntate diciendo \"Hello world!\" Cuéntales a los tus compañeros campistas como encontraste freeCodeCamp. Además cuéntanos porque deseas aprender a programar.",
|
||||||
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -840,7 +840,7 @@
|
|||||||
[
|
[
|
||||||
"//i.imgur.com/OmRmLB4.gif",
|
"//i.imgur.com/OmRmLB4.gif",
|
||||||
"Un gif vous montrant comment Cliquez sur le lien ci-dessous pour accéder à notre salle de chat et cliquez sur \"Se connecter avec\" bouton GitHub. Puis vous pouvez cliquer dans le champ de saisie de texte et saisissez un message à vos amis campeurs.",
|
"Un gif vous montrant comment Cliquez sur le lien ci-dessous pour accéder à notre salle de chat et cliquez sur \"Se connecter avec\" bouton GitHub. Puis vous pouvez cliquer dans le champ de saisie de texte et saisissez un message à vos amis campeurs.",
|
||||||
"Maintenant que vous avez une GitHub compte, vous pouvez vous joindre à notre principale salle de chat en vous connectant à GitHub. Présentez-vous en disant \"Hello World!\" avertissez votre amis campeurs comment vous avez trouvé Libre Code Camp. Aussi nous dire pourquoi vous voulez apprendre à code.",
|
"Maintenant que vous avez une GitHub compte, vous pouvez vous joindre à notre principale salle de chat en vous connectant à GitHub. Ne connectez-vous pas avec Twitter. Présentez-vous en disant \"Hello World!\" avertissez votre amis campeurs comment vous avez trouvé Libre Code Camp. Aussi nous dire pourquoi vous voulez apprendre à code.",
|
||||||
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -899,7 +899,7 @@
|
|||||||
[
|
[
|
||||||
"//i.imgur.com/OmRmLB4.gif",
|
"//i.imgur.com/OmRmLB4.gif",
|
||||||
"Una gif che mostra come cliccare sul link qui sotto per andare alla nostra chat room e fare clic sul pulsante \" accedi con GitHub \". Quindi potrai cliccare nel campo di immissione testo e digitare un messaggio agli altri campers.",
|
"Una gif che mostra come cliccare sul link qui sotto per andare alla nostra chat room e fare clic sul pulsante \" accedi con GitHub \". Quindi potrai cliccare nel campo di immissione testo e digitare un messaggio agli altri campers.",
|
||||||
"Ora che disponi di un account GitHub, puoi aggiungerti alla nostra chat principale effettuando il login con GitHub. Presentati dicendo \" Hello World! \"Dì ai campers come hai trovato freeCodeCamp. Dicci anche perché vuoi imparare a programmare. ",
|
"Ora che disponi di un account GitHub, puoi aggiungerti alla nostra chat principale effettuando il login con GitHub. Si prega di non accedere con Twitter. Presentati dicendo \" Hello World! \"Dì ai campers come hai trovato freeCodeCamp. Dicci anche perché vuoi imparare a programmare. ",
|
||||||
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -1017,7 +1017,7 @@
|
|||||||
[
|
[
|
||||||
"//i.imgur.com/OmRmLB4.gif",
|
"//i.imgur.com/OmRmLB4.gif",
|
||||||
"Um gif mostrando como clicar no link abaixo para ir à nossa sala de bate-papo e clicar no botão \"entre com GitHub\". Em seguida, você pode clicar no campo de entrada de texto e digitar uma mensagem para os seus colegas campistas.",
|
"Um gif mostrando como clicar no link abaixo para ir à nossa sala de bate-papo e clicar no botão \"entre com GitHub\". Em seguida, você pode clicar no campo de entrada de texto e digitar uma mensagem para os seus colegas campistas.",
|
||||||
"Agora que você tem uma conta GitHub, você pode se juntar à nossa sala de chat principal, fazendo login com GitHub. Apresente-se, diga \"Hello world! \" e diga a seus colegas campistas como você encontrou o freeCodeCamp. Também diga-nos por que você quer aprender a programar.",
|
"Agora que você tem uma conta GitHub, você pode se juntar à nossa sala de chat principal, fazendo login com GitHub. Por favor, não faça login no Twitter. Apresente-se, diga \"Hello world! \" e diga a seus colegas campistas como você encontrou o freeCodeCamp. Também diga-nos por que você quer aprender a programar.",
|
||||||
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
"https://gitter.im/FreeCodeCamp/FreeCodeCamp"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
@ -1036,41 +1036,15 @@
|
|||||||
"challengeType": 0,
|
"challengeType": 0,
|
||||||
"translations": {}
|
"translations": {}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "587d781d367417b2b2512ac7",
|
|
||||||
"title": "Adjust the Color of an Anchor Tag",
|
|
||||||
"description": [
|
|
||||||
"You can adjust the text color of any text in an element with the <code>color</code> property.",
|
|
||||||
"<hr>",
|
|
||||||
"Change the <code>color</code> property of the anchor (<code>a</code>) tag to <code>#000</code> (black)."
|
|
||||||
],
|
|
||||||
"challengeSeed": [
|
|
||||||
"<style>",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
"</style>",
|
|
||||||
"<a href=\"http://freecatphotoapp.com/\" target=\"_blank\">CatPhotoApp</a>"
|
|
||||||
],
|
|
||||||
"tests": [
|
|
||||||
"assert($('a').css('color') == 'rgb(0, 0, 0)', 'message: The <code>color</code> of the anchor tag text should be black.')"
|
|
||||||
],
|
|
||||||
"solutions": [],
|
|
||||||
"hints": [],
|
|
||||||
"type": "waypoint",
|
|
||||||
"releasedOn": "Feb 17, 2017",
|
|
||||||
"challengeType": 0,
|
|
||||||
"translations": {}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "587d781d367417b2b2512ac8",
|
"id": "587d781d367417b2b2512ac8",
|
||||||
"title": "Adjust the Hover State of an Anchor Tag",
|
"title": "Adjust the Hover State of an Anchor Tag",
|
||||||
"description": [
|
"description": [
|
||||||
"This challenge will touch on the usage of pseudo-classes. A pseudo-class is a keyword that can be added to selectors, in order to select a specific state of the element.",
|
"This challenge will touch on the usage of pseudo-classes. A pseudo-class is a keyword that can be added to selectors, in order to select a specific state of the element.",
|
||||||
"Following from the previous challenge, the styling of the anchor tag can be changed for its hover state using the <code>:hover</code> pseudo-class selector. Here's an example that changes the color of the anchor tag to red during its hover state:",
|
"For example, the styling of an anchor tag can be changed for its hover state using the <code>:hover</code> pseudo-class selector. Here's the CSS to change the <code>color</code> of the anchor tag to red during its hover state:",
|
||||||
"<blockquote>a:hover {<br> color: red;<br>}</blockquote>",
|
"<blockquote>a:hover {<br> color: red;<br>}</blockquote>",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Change the <code>a</code> tag's CSS so that when the user hovers over it, the <code>color</code> is blue."
|
"The code editor has a CSS rule to style all <code>a</code> tags black. Add a rule so that when the user hovers over the <code>a</code> tag, the <code>color</code> is blue."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"<style>",
|
"<style>",
|
||||||
@ -2187,7 +2161,7 @@
|
|||||||
"As shown in the example below, you can push the item downwards then upwards by setting the <code>top</code> property of the <code>50%</code> keyframe to 50px, but having it set to 0px for the first (<code>0%</code>) and the last (<code>100%</code>) keyframe.",
|
"As shown in the example below, you can push the item downwards then upwards by setting the <code>top</code> property of the <code>50%</code> keyframe to 50px, but having it set to 0px for the first (<code>0%</code>) and the last (<code>100%</code>) keyframe.",
|
||||||
"<blockquote>@keyframes rainbow {<br> 0% {<br> background-color: blue;<br> top: 0px;<br> }<br> 50% {<br> background-color: green;<br> top: 50px;<br> }<br> 100% {<br> background-color: yellow;<br> top: 0px;<br> }<br>}</blockquote>",
|
"<blockquote>@keyframes rainbow {<br> 0% {<br> background-color: blue;<br> top: 0px;<br> }<br> 50% {<br> background-color: green;<br> top: 50px;<br> }<br> 100% {<br> background-color: yellow;<br> top: 0px;<br> }<br>}</blockquote>",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Add a horizontal motion to the <code>div</code> animation. Using the <code>left</code> offset property, add to the <code>@keyframes</code> rule so rainbow starts at 0 pixels at <code>0%</code>, moves to 25 pixels at <code>50%</code>, and ends at -25 pixels at <code>100%</code>."
|
"Add a horizontal motion to the <code>div</code> animation. Using the <code>left</code> offset property, add to the <code>@keyframes</code> rule so rainbow starts at 0 pixels at <code>0%</code>, moves to 25 pixels at <code>50%</code>, and ends at -25 pixels at <code>100%</code>. Don't replace the <code>top</code> property in the editor - the animation should have both vertical and horizontal motion."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"<style>",
|
"<style>",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"Cascading Style Sheets (CSS) tell the browser how to display the text and other content that you write in HTML.",
|
"Cascading Style Sheets (CSS) tell the browser how to display the text and other content that you write in HTML.<br><br>Note that CSS is case-sensitive so be careful with your capitalization.",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -87,7 +87,7 @@
|
|||||||
"</main>"
|
"</main>"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert(code.match(/<h2\\s+style=\"color:\\s*rgb\\(255,\\s*0,\\s*0\\)\\;?\">\\s*CatPhotoApp\\s*<\\/h2>/) || code.match(/<h2\\s+style=\"color:\\s*red\\;?\">\\s*CatPhotoApp\\s*<\\/h2>/), 'message: Your <code>h2</code> element should be red.');"
|
"assert(code.match(/<h2\\s+style\\s*=\\s*\"\\s*color\\s*:\\s*(?:rgb\\(\\s*255\\s*,\\s*0\\s*,\\s*0\\s*\\)|rgb\\(\\s*100%\\s*,\\s*0%\\s*,\\s*0%\\s*\\)|red|#ff0000|#f00|hsl\\(\\s*0\\s*,\\s*100%\\s*,\\s*50%\\s*\\))\\s*\\;?\">\\s*CatPhotoApp\\s*<\\/h2>/), 'message: Your <code>h2</code> element should be red.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
"challengeType": 0,
|
"challengeType": 0,
|
||||||
@ -2312,7 +2312,8 @@
|
|||||||
"assert($(\".green-box\").css(\"padding-top\") === \"40px\", 'message: Your <code>green-box</code> class should give the top of elements <code>40px</code> of <code>padding</code>.');",
|
"assert($(\".green-box\").css(\"padding-top\") === \"40px\", 'message: Your <code>green-box</code> class should give the top of elements <code>40px</code> of <code>padding</code>.');",
|
||||||
"assert($(\".green-box\").css(\"padding-right\") === \"20px\", 'message: Your <code>green-box</code> class should give the right of elements <code>20px</code> of <code>padding</code>.');",
|
"assert($(\".green-box\").css(\"padding-right\") === \"20px\", 'message: Your <code>green-box</code> class should give the right of elements <code>20px</code> of <code>padding</code>.');",
|
||||||
"assert($(\".green-box\").css(\"padding-bottom\") === \"20px\", 'message: Your <code>green-box</code> class should give the bottom of elements <code>20px</code> of <code>padding</code>.');",
|
"assert($(\".green-box\").css(\"padding-bottom\") === \"20px\", 'message: Your <code>green-box</code> class should give the bottom of elements <code>20px</code> of <code>padding</code>.');",
|
||||||
"assert($(\".green-box\").css(\"padding-left\") === \"40px\", 'message: Your <code>green-box</code> class should give the left of elements <code>40px</code> of <code>padding</code>.');"
|
"assert($(\".green-box\").css(\"padding-left\") === \"40px\", 'message: Your <code>green-box</code> class should give the left of elements <code>40px</code> of <code>padding</code>.');",
|
||||||
|
"assert(!/padding-top|padding-right|padding-bottom|padding-left/.test(code), 'message: You should use the clockwise notation to set the padding of <code>green-box</code> class.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
"challengeType": 0,
|
"challengeType": 0,
|
||||||
|
@ -640,7 +640,7 @@
|
|||||||
"Here's a diagram of an <code>a</code> element. In this case, the <code>a</code> element is used in the middle of a paragraph element, which means the link will appear in the middle of a sentence.",
|
"Here's a diagram of an <code>a</code> element. In this case, the <code>a</code> element is used in the middle of a paragraph element, which means the link will appear in the middle of a sentence.",
|
||||||
"<a href=\"//i.imgur.com/hviuZwe.png\" target=\"_blank\" data-lightbox=\"img-enlarge\"><img class=\"img-responsive\" title=\"Click to enlarge\" alt=\"a diagram of how anchor tags are composed with the same text as on the following line\" src=\"//i.imgur.com/hviuZwe.png\"></a>",
|
"<a href=\"//i.imgur.com/hviuZwe.png\" target=\"_blank\" data-lightbox=\"img-enlarge\"><img class=\"img-responsive\" title=\"Click to enlarge\" alt=\"a diagram of how anchor tags are composed with the same text as on the following line\" src=\"//i.imgur.com/hviuZwe.png\"></a>",
|
||||||
"Here's an example:",
|
"Here's an example:",
|
||||||
"<code><p>Here's a <a href=\"http://freeCodeCamp.com\"> link to freeCodeCamp</a> for you to follow.</p></code>",
|
"<code><p>Here's a <a href=\"http://freeCodeCamp.com\"> link to freeCodeCamp.com</a> for you to follow.</p></code>",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Create an <code>a</code> element that links to <code>http://freecatphotoapp.com</code> and has \"cat photos\" as its <code>anchor text</code>."
|
"Create an <code>a</code> element that links to <code>http://freecatphotoapp.com</code> and has \"cat photos\" as its <code>anchor text</code>."
|
||||||
],
|
],
|
||||||
@ -1629,7 +1629,8 @@
|
|||||||
"assert($('label > input[type=\"radio\"]:only-child').length > 1, 'message: Each of your two radio button elements should be nested in its own <code>label</code> element.');",
|
"assert($('label > input[type=\"radio\"]:only-child').length > 1, 'message: Each of your two radio button elements should be nested in its own <code>label</code> element.');",
|
||||||
"assert((code.match(/<\\/label>/g) && code.match(/<label/g) && code.match(/<\\/label>/g).length === code.match(/<label/g).length), 'message: Make sure each of your <code>label</code> elements has a closing tag.');",
|
"assert((code.match(/<\\/label>/g) && code.match(/<label/g) && code.match(/<\\/label>/g).length === code.match(/<label/g).length), 'message: Make sure each of your <code>label</code> elements has a closing tag.');",
|
||||||
"assert($(\"label\").text().match(/indoor/gi), 'message: One of your radio buttons should have the label <code>indoor</code>.');",
|
"assert($(\"label\").text().match(/indoor/gi), 'message: One of your radio buttons should have the label <code>indoor</code>.');",
|
||||||
"assert($(\"label\").text().match(/outdoor/gi), 'message: One of your radio buttons should have the label <code>outdoor</code>.');"
|
"assert($(\"label\").text().match(/outdoor/gi), 'message: One of your radio buttons should have the label <code>outdoor</code>.');",
|
||||||
|
"assert($(\"label\").parent().get(0).tagName.match('FORM'), 'message: Each of your radio button elements should be added within the <code>form</code> tag.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
"challengeType": 0,
|
"challengeType": 0,
|
||||||
|
@ -375,6 +375,7 @@
|
|||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert(confirmEnding(\"Bastian\", \"n\") === true, 'message: <code>confirmEnding(\"Bastian\", \"n\")</code> should return true.');",
|
"assert(confirmEnding(\"Bastian\", \"n\") === true, 'message: <code>confirmEnding(\"Bastian\", \"n\")</code> should return true.');",
|
||||||
|
"assert(confirmEnding(\"Congratulation\", \"on\") === true, 'message: <code>confirmEnding(\"Congratulation\", \"on\")</code> should return true.');",
|
||||||
"assert(confirmEnding(\"Connor\", \"n\") === false, 'message: <code>confirmEnding(\"Connor\", \"n\")</code> should return false.');",
|
"assert(confirmEnding(\"Connor\", \"n\") === false, 'message: <code>confirmEnding(\"Connor\", \"n\")</code> should return false.');",
|
||||||
"assert(confirmEnding(\"Walking on water and developing software from a specification are easy if both are frozen\", \"specification\") === false, 'message: <code>confirmEnding(\"Walking on water and developing software from a specification are easy if both are frozen\", \"specification\")</code> should return false.');",
|
"assert(confirmEnding(\"Walking on water and developing software from a specification are easy if both are frozen\", \"specification\") === false, 'message: <code>confirmEnding(\"Walking on water and developing software from a specification are easy if both are frozen\", \"specification\")</code> should return false.');",
|
||||||
"assert(confirmEnding(\"He has to give me a new name\", \"name\") === true, 'message: <code>confirmEnding(\"He has to give me a new name\", \"name\")</code> should return true.');",
|
"assert(confirmEnding(\"He has to give me a new name\", \"name\") === true, 'message: <code>confirmEnding(\"He has to give me a new name\", \"name\")</code> should return true.');",
|
||||||
|
@ -232,7 +232,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "587d7b7a367417b2b2512b12",
|
"id": "587d7b7a367417b2b2512b12",
|
||||||
"title": "Copy an Array with slice()",
|
"title": "Copy Array Items Using slice()",
|
||||||
"description": [
|
"description": [
|
||||||
"The next method we will cover is <code>slice()</code>. <code>slice()</code>, rather than modifying an array, copies, or <em>extracts</em>, a given number of elements to a new array, leaving the array it is called upon untouched. <code>slice()</code> takes only 2 parameters — the first is the index at which to begin extraction, and the second is the index at which to stop extraction (extraction will occur up to, but not including the element at this index). Consider this:",
|
"The next method we will cover is <code>slice()</code>. <code>slice()</code>, rather than modifying an array, copies, or <em>extracts</em>, a given number of elements to a new array, leaving the array it is called upon untouched. <code>slice()</code> takes only 2 parameters — the first is the index at which to begin extraction, and the second is the index at which to stop extraction (extraction will occur up to, but not including the element at this index). Consider this:",
|
||||||
"<blockquote>let weatherConditions = ['rain', 'snow', 'sleet', 'hail', 'clear'];<br><br>let todaysWeather = weatherConditions.slice(1, 3);<br>// todaysWeather equals ['snow', 'sleet'];<br>// weatherConditions still equals ['rain', 'snow', 'sleet', 'hail', 'clear']<br></blockquote>",
|
"<blockquote>let weatherConditions = ['rain', 'snow', 'sleet', 'hail', 'clear'];<br><br>let todaysWeather = weatherConditions.slice(1, 3);<br>// todaysWeather equals ['snow', 'sleet'];<br>// weatherConditions still equals ['rain', 'snow', 'sleet', 'hail', 'clear']<br></blockquote>",
|
||||||
|
@ -129,10 +129,10 @@
|
|||||||
"description": [
|
"description": [
|
||||||
"In JavaScript, you can store a value in a variable with the <dfn>assignment</dfn> operator.",
|
"In JavaScript, you can store a value in a variable with the <dfn>assignment</dfn> operator.",
|
||||||
"<code>myVariable = 5;</code>",
|
"<code>myVariable = 5;</code>",
|
||||||
"Assigns the <code>Number</code> value <code>5</code> to <code>myVariable</code>.",
|
"This assigns the <code>Number</code> value <code>5</code> to <code>myVariable</code>.",
|
||||||
"Assignment always goes from right to left. Everything to the right of the <code>=</code> operator is resolved before the value is assigned to the variable to the left of the operator.",
|
"Assignment always goes from right to left. Everything to the right of the <code>=</code> operator is resolved before the value is assigned to the variable to the left of the operator.",
|
||||||
"<blockquote>myVar = 5;<br>myNum = myVar;</blockquote>",
|
"<blockquote>myVar = 5;<br>myNum = myVar;</blockquote>",
|
||||||
"Assigns <code>5</code> to <code>myVar</code> and then resolves <code>myVar</code> to <code>5</code> again and assigns it to <code>myNum</code>.",
|
"This assigns <code>5</code> to <code>myVar</code> and then resolves <code>myVar</code> to <code>5</code> again and assigns it to <code>myNum</code>.",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Assign the value <code>7</code> to variable <code>a</code>.",
|
"Assign the value <code>7</code> to variable <code>a</code>.",
|
||||||
"Assign the contents of <code>a</code> to variable <code>b</code>."
|
"Assign the contents of <code>a</code> to variable <code>b</code>."
|
||||||
@ -520,7 +520,7 @@
|
|||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert(myVar === 88, 'message: <code>myVar</code> should equal <code>88</code>');",
|
"assert(myVar === 88, 'message: <code>myVar</code> should equal <code>88</code>');",
|
||||||
"assert(/myVar\\s*\\=.*myVar/.test(code) === false, 'message: <code>myVar = myVar + 1;</code> should be changed');",
|
"assert(/myVar\\s*\\=.*myVar\\s*\\+\\s*1/.test(code) === false, 'message: <code>myVar = myVar + 1;</code> should be changed');",
|
||||||
"assert(/[+]{2}\\s*myVar|myVar\\s*[+]{2}/.test(code), 'message: Use the <code>++</code> operator');",
|
"assert(/[+]{2}\\s*myVar|myVar\\s*[+]{2}/.test(code), 'message: Use the <code>++</code> operator');",
|
||||||
"assert(/var myVar = 87;/.test(code), 'message: Do not change code above the line');"
|
"assert(/var myVar = 87;/.test(code), 'message: Do not change code above the line');"
|
||||||
],
|
],
|
||||||
@ -1810,18 +1810,18 @@
|
|||||||
"We can access the data inside arrays using <code>indexes</code>.",
|
"We can access the data inside arrays using <code>indexes</code>.",
|
||||||
"Array indexes are written in the same bracket notation that strings use, except that instead of specifying a character, they are specifying an entry in the array. Like strings, arrays use <dfn>zero-based</dfn> indexing, so the first element in an array is element <code>0</code>.",
|
"Array indexes are written in the same bracket notation that strings use, except that instead of specifying a character, they are specifying an entry in the array. Like strings, arrays use <dfn>zero-based</dfn> indexing, so the first element in an array is element <code>0</code>.",
|
||||||
"<strong>Example</strong>",
|
"<strong>Example</strong>",
|
||||||
"<blockquote>var array = [1,2,3];<br>array[0]; // equals 1<br>var data = array[1]; // equals 2</blockquote>",
|
"<blockquote>var array = [50,60,70];<br>array[0]; // equals 50<br>var data = array[1]; // equals 60</blockquote>",
|
||||||
"<strong>Note</strong><br>There shouldn't be any spaces between the array name and the square brackets, like <code>array [0]</code>. Although JavaScript is able to process this correctly, this may confuse other programmers reading your code.",
|
"<strong>Note</strong><br>There shouldn't be any spaces between the array name and the square brackets, like <code>array [0]</code>. Although JavaScript is able to process this correctly, this may confuse other programmers reading your code.",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Create a variable called <code>myData</code> and set it to equal the first value of <code>myArray</code> using bracket notation."
|
"Create a variable called <code>myData</code> and set it to equal the first value of <code>myArray</code> using bracket notation."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"// Example",
|
"// Example",
|
||||||
"var ourArray = [1,2,3];",
|
"var ourArray = [50,60,70];",
|
||||||
"var ourData = ourArray[0]; // equals 1",
|
"var ourData = ourArray[0]; // equals 50",
|
||||||
"",
|
"",
|
||||||
"// Setup",
|
"// Setup",
|
||||||
"var myArray = [1,2,3];",
|
"var myArray = [50,60,70];",
|
||||||
"",
|
"",
|
||||||
"// Only change code below this line.",
|
"// Only change code below this line.",
|
||||||
""
|
""
|
||||||
@ -1830,7 +1830,7 @@
|
|||||||
"if(typeof myArray !== \"undefined\" && typeof myData !== \"undefined\"){(function(y,z){return 'myArray = ' + JSON.stringify(y) + ', myData = ' + JSON.stringify(z);})(myArray, myData);}"
|
"if(typeof myArray !== \"undefined\" && typeof myData !== \"undefined\"){(function(y,z){return 'myArray = ' + JSON.stringify(y) + ', myData = ' + JSON.stringify(z);})(myArray, myData);}"
|
||||||
],
|
],
|
||||||
"solutions": [
|
"solutions": [
|
||||||
"var myArray = [1,2,3];\nvar myData = myArray[0];"
|
"var myArray = [50,60,70];\nvar myData = myArray[0];"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert((function(){if(typeof myArray !== 'undefined' && typeof myData !== 'undefined' && myArray[0] === myData){return true;}else{return false;}})(), 'message: The variable <code>myData</code> should equal the first value of <code>myArray</code>.');",
|
"assert((function(){if(typeof myArray !== 'undefined' && typeof myData !== 'undefined' && myArray[0] === myData){return true;}else{return false;}})(), 'message: The variable <code>myData</code> should equal the first value of <code>myArray</code>.');",
|
||||||
@ -1860,18 +1860,18 @@
|
|||||||
"description": [
|
"description": [
|
||||||
"Unlike strings, the entries of arrays are <dfn>mutable</dfn> and can be changed freely.",
|
"Unlike strings, the entries of arrays are <dfn>mutable</dfn> and can be changed freely.",
|
||||||
"<strong>Example</strong>",
|
"<strong>Example</strong>",
|
||||||
"<blockquote>var ourArray = [3,2,1];<br>ourArray[0] = 1; // equals [1,2,1]</blockquote>",
|
"<blockquote>var ourArray = [50,40,30];<br>ourArray[0] = 15; // equals [15,40,30]</blockquote>",
|
||||||
"<strong>Note</strong><br>There shouldn't be any spaces between the array name and the square brackets, like <code>array [0]</code>. Although JavaScript is able to process this correctly, this may confuse other programmers reading your code.",
|
"<strong>Note</strong><br>There shouldn't be any spaces between the array name and the square brackets, like <code>array [0]</code>. Although JavaScript is able to process this correctly, this may confuse other programmers reading your code.",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Modify the data stored at index <code>0</code> of <code>myArray</code> to a value of <code>3</code>."
|
"Modify the data stored at index <code>0</code> of <code>myArray</code> to a value of <code>45</code>."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"// Example",
|
"// Example",
|
||||||
"var ourArray = [1,2,3];",
|
"var ourArray = [18,64,99];",
|
||||||
"ourArray[1] = 3; // ourArray now equals [1,3,3].",
|
"ourArray[1] = 45; // ourArray now equals [18,45,99].",
|
||||||
"",
|
"",
|
||||||
"// Setup",
|
"// Setup",
|
||||||
"var myArray = [1,2,3];",
|
"var myArray = [18,64,99];",
|
||||||
"",
|
"",
|
||||||
"// Only change code below this line.",
|
"// Only change code below this line.",
|
||||||
"",
|
"",
|
||||||
@ -1881,10 +1881,10 @@
|
|||||||
"if(typeof myArray !== \"undefined\"){(function(){return myArray;})();}"
|
"if(typeof myArray !== \"undefined\"){(function(){return myArray;})();}"
|
||||||
],
|
],
|
||||||
"solutions": [
|
"solutions": [
|
||||||
"var myArray = [1,2,3];\nmyArray[0] = 3;"
|
"var myArray = [18,64,99];\nmyArray[0] = 45;"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert((function(){if(typeof myArray != 'undefined' && myArray[0] == 3 && myArray[1] == 2 && myArray[2] == 3){return true;}else{return false;}})(), 'message: <code>myArray</code> should now be [3,2,3].');",
|
"assert((function(){if(typeof myArray != 'undefined' && myArray[0] == 45 && myArray[1] == 64 && myArray[2] == 99){return true;}else{return false;}})(), 'message: <code>myArray</code> should now be [45,64,99].');",
|
||||||
"assert((function(){if(code.match(/myArray\\[0\\]\\s*=\\s*/g)){return true;}else{return false;}})(), 'message: You should be using correct index to modify the value in <code>myArray</code>.');"
|
"assert((function(){if(code.match(/myArray\\[0\\]\\s*=\\s*/g)){return true;}else{return false;}})(), 'message: You should be using correct index to modify the value in <code>myArray</code>.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
@ -3253,7 +3253,7 @@
|
|||||||
"id": "56533eb9ac21ba0edf2244d7",
|
"id": "56533eb9ac21ba0edf2244d7",
|
||||||
"title": "Comparison with the Less Than Or Equal To Operator",
|
"title": "Comparison with the Less Than Or Equal To Operator",
|
||||||
"description": [
|
"description": [
|
||||||
"The <code>less than or equal to</code> operator (<code><=</code>) compares the values of two numbers. If the number to the left is less than or equal the number to the right, it returns <code>true</code>. If the number on the left is greater than the number on the right, it returns <code>false</code>. Like the equality operator, <code>less than or equal to</code> converts data types.",
|
"The <code>less than or equal to</code> operator (<code><=</code>) compares the values of two numbers. If the number to the left is less than or equal to the number to the right, it returns <code>true</code>. If the number on the left is greater than the number on the right, it returns <code>false</code>. Like the equality operator, <code>less than or equal to</code> converts data types.",
|
||||||
"<strong>Examples</strong>",
|
"<strong>Examples</strong>",
|
||||||
"<blockquote> 4 <= 5 // true<br>'7' <= 7 // true<br> 5 <= 5 // true<br> 3 <= 2 // false<br>'8' <= 4 // false</blockquote>",
|
"<blockquote> 4 <= 5 // true<br>'7' <= 7 // true<br> 5 <= 5 // true<br> 3 <= 2 // false<br>'8' <= 4 // false</blockquote>",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
@ -3372,6 +3372,7 @@
|
|||||||
"title": "Comparisons with the Logical Or Operator",
|
"title": "Comparisons with the Logical Or Operator",
|
||||||
"description": [
|
"description": [
|
||||||
"The <dfn>logical or</dfn> operator (<code>||</code>) returns <code>true</code> if either of the <dfn>operands</dfn> is <code>true</code>. Otherwise, it returns <code>false</code>.",
|
"The <dfn>logical or</dfn> operator (<code>||</code>) returns <code>true</code> if either of the <dfn>operands</dfn> is <code>true</code>. Otherwise, it returns <code>false</code>.",
|
||||||
|
"The <dfn>logical or</dfn> operator is composed of two pipe symbols (<code>|</code>). This can typically be found between your Backspace and Enter keys.",
|
||||||
"The pattern below should look familiar from prior waypoints:",
|
"The pattern below should look familiar from prior waypoints:",
|
||||||
"<blockquote>if (num > 10) {<br> return \"No\";<br>}<br>if (num < 5) {<br> return \"No\";<br>}<br>return \"Yes\";</blockquote>",
|
"<blockquote>if (num > 10) {<br> return \"No\";<br>}<br>if (num < 5) {<br> return \"No\";<br>}<br>return \"Yes\";</blockquote>",
|
||||||
"will return \"Yes\" only if <code>num</code> is between <code>5</code> and <code>10</code> (5 and 10 included). The same logic can be written as:",
|
"will return \"Yes\" only if <code>num</code> is between <code>5</code> and <code>10</code> (5 and 10 included). The same logic can be written as:",
|
||||||
|
@ -252,26 +252,21 @@
|
|||||||
"description": [
|
"description": [
|
||||||
"Continuing the discussion on calling functions, the next bug to watch out for is when a function's arguments are supplied in the incorrect order. If the arguments are different types, such as a function expecting an array and an integer, this will likely throw a runtime error. If the arguments are the same type (all integers, for example), then the logic of the code won't make sense. Make sure to supply all required arguments, in the proper order to avoid these issues.",
|
"Continuing the discussion on calling functions, the next bug to watch out for is when a function's arguments are supplied in the incorrect order. If the arguments are different types, such as a function expecting an array and an integer, this will likely throw a runtime error. If the arguments are the same type (all integers, for example), then the logic of the code won't make sense. Make sure to supply all required arguments, in the proper order to avoid these issues.",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"The following function <code>positivePowers</code> raises a base to a positive exponent. Unfortunately, it's not called properly - fix the code so the value of <code>power</code> is the expected 8."
|
"The function <code>raiseToPower</code> raises a base to an exponent. Unfortunately, it's not called properly - fix the code so the value of <code>power</code> is the expected 8."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"function positivePowers(b, e) {",
|
"function raiseToPower(b, e) {",
|
||||||
" let result = 1;",
|
" return Math.pow(b, e);",
|
||||||
" if (e <= 0) return result;",
|
|
||||||
" for (let i = 1; i <= e; i++) {",
|
|
||||||
" result *= b;",
|
|
||||||
" }",
|
|
||||||
" return result;",
|
|
||||||
"}",
|
"}",
|
||||||
"",
|
"",
|
||||||
"let base = 2;",
|
"let base = 2;",
|
||||||
"let exp = 3;",
|
"let exp = 3;",
|
||||||
"let power = positivePowers(exp, base);",
|
"let power = raiseToPower(exp, base);",
|
||||||
"console.log(power);"
|
"console.log(power);"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert(power == 8, 'message: Your code should fix the variable <code>power</code> so it equals 2 raised to the 3rd power, not 3 raised to the 2nd power.');",
|
"assert(power == 8, 'message: Your code should fix the variable <code>power</code> so it equals 2 raised to the 3rd power, not 3 raised to the 2nd power.');",
|
||||||
"assert(code.match(/positivePowers\\(\\s*?base\\s*?,\\s*?exp\\s*?\\);/g), 'message: Your code should use the correct order of the arguments for the <code>positivePowers</code> function call.');"
|
"assert(code.match(/raiseToPower\\(\\s*?base\\s*?,\\s*?exp\\s*?\\);/g), 'message: Your code should use the correct order of the arguments for the <code>raiseToPower</code> function call.');"
|
||||||
],
|
],
|
||||||
"solutions": [],
|
"solutions": [],
|
||||||
"hints": [],
|
"hints": [],
|
||||||
|
@ -159,8 +159,8 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
"// Test user did not replace const keyword",
|
"// Test user did not replace const keyword",
|
||||||
"// Test s is const",
|
"// Test s is const",
|
||||||
"// Test s is sorted",
|
"assert.deepEqual(s, [2, 5, 7], 'message: <code>s</code> should be equal to <code>[2, 5, 7]</code>.');",
|
||||||
"// Test s is still mutable, and object freeze was not invoked"
|
"assert(!Object.isFrozen(s), 'message: <code>s</code> should not be frozen.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
"releasedOn": "Feb 17, 2017",
|
"releasedOn": "Feb 17, 2017",
|
||||||
@ -356,9 +356,7 @@
|
|||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"const arr1 = ['JAN', 'FEB', 'MAR', 'APR', 'MAY'];",
|
"const arr1 = ['JAN', 'FEB', 'MAR', 'APR', 'MAY'];",
|
||||||
"const arr2 = [];",
|
"const arr2 = []; // change this line",
|
||||||
"// change code below this line",
|
|
||||||
"// change code above this line",
|
|
||||||
"arr1.push('JUN');",
|
"arr1.push('JUN');",
|
||||||
"console.log(arr2); // arr2 should not be affected"
|
"console.log(arr2); // arr2 should not be affected"
|
||||||
],
|
],
|
||||||
@ -538,41 +536,29 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "587d7b8a367417b2b2512b4e",
|
"id": "587d7b8a367417b2b2512b4e",
|
||||||
"title": "Interpolate a String Using Backquotes",
|
"title": "Create Strings using Template Literals",
|
||||||
"description": [
|
"description": [
|
||||||
"A new feature of ES6 or ES2015, is that it allows you to use string interpolation with back-ticks.",
|
"A new feature of ES6 is the <dfn>template literal</dfn>. This is a special type of string that allows you to use string interpolation features to create strings.",
|
||||||
"Consider the code below",
|
"Consider the code below:",
|
||||||
"<code>const text = 'Hello World';</code>",
|
"<blockquote>const person = {<br> name: \"Zodiac Hasbro\",<br> age: 56<br>};<br><br>// string interpolation<br>const greeting = `Hello, my name is ${person.name}!<br>I am ${person.age} years old.`;<br><br>console.log(greeting); // prints<br>// Hello, my name is Zodiac Hasbro!<br>// I am 56 years old.<br></blockquote>",
|
||||||
"<code>// string interpolation</code>",
|
"A lot of things happened there.",
|
||||||
"<code>const divText = `</code>",
|
"Firstly, the <code>${variable}</code> syntax used above is a place holder. Basically, you won't have to use concatenation with the <code>+</code> operator anymore. To add variables to strings, you just drop the variable in a template string and wrap it with <code>${</code> and <code>}</code>.",
|
||||||
"<code><div></code>",
|
"Secondly, the example uses backticks (<code>`</code>), not quotes (<code>'</code> or <code>\"</code>), to wrap the string. Notice that the string is multi-line.",
|
||||||
"<code> ${text}</code>",
|
"This new way of creating strings gives you more flexibility to create robust strings.",
|
||||||
"<code></div></code>",
|
"<hr>",
|
||||||
"<code>console.log(divText); // prints </code>",
|
"Use template literal syntax with backticks to display each entry of the <code>result</code> object's <code>failure</code> array. Each entry should be wrapped inside an <code>li</code> element with the class attribute <code>text-warning</code>."
|
||||||
"<code>//<div></code>",
|
|
||||||
"<code>// Hello World</code>",
|
|
||||||
"<code>//</div></code>",
|
|
||||||
"A lot of thing happened in there.",
|
|
||||||
"First, the ${variable} syntax. It's a template literal. Basically, you won't have to use concatenation with + anymore. Just drop the variable in your string, wrapped with ${ and }.",
|
|
||||||
"Second, we used backticks, not quotes, to wrap around the string. Notice that the string is multi-line.",
|
|
||||||
"In ES6, back-ticks give you more robust string creation ability.",
|
|
||||||
"Instructions",
|
|
||||||
"Use proper template literal syntax with backticks.",
|
|
||||||
"The expected output is each entry of result object's failure array, wrapped inside an <li> element, with class attribute text-warning.",
|
|
||||||
"If you have trouble finding backticks, it's the ` key, to the left of your 1; and has ~ on it."
|
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"const result = {",
|
"const result = {",
|
||||||
" success: ['max_length', 'no-amd', 'prefer-arrow-functions'],",
|
" success: [\"max-length\", \"no-amd\", \"prefer-arrow-functions\"],",
|
||||||
" failure: ['no-var', 'var-on-top', 'linebreak'],",
|
" failure: [\"no-var\", \"var-on-top\", \"linebreak\"],",
|
||||||
" skipped: ['id-blacklist', 'no-dup-keys']",
|
" skipped: [\"id-blacklist\", \"no-dup-keys\"]",
|
||||||
"}",
|
"};",
|
||||||
"/* Alter code below this line */",
|
"// change code below this line",
|
||||||
"const resultDisplay = undefined;",
|
"const resultDisplay = null;",
|
||||||
"/* Alter code above this line */",
|
"// change code above this line",
|
||||||
"console.log(resultDisplay);",
|
"console.log(resultDisplay);",
|
||||||
"/**",
|
"/**",
|
||||||
" * ",
|
|
||||||
" * should look like this",
|
" * should look like this",
|
||||||
" * <li class=\"text-warning\">no-var</li>",
|
" * <li class=\"text-warning\">no-var</li>",
|
||||||
" * <li class=\"text-warning\">var-on-top</li>",
|
" * <li class=\"text-warning\">var-on-top</li>",
|
||||||
@ -593,32 +579,27 @@
|
|||||||
"id": "587d7b8a367417b2b2512b4f",
|
"id": "587d7b8a367417b2b2512b4f",
|
||||||
"title": "Write Concise Object Literal Declarations Using Simple Fields",
|
"title": "Write Concise Object Literal Declarations Using Simple Fields",
|
||||||
"description": [
|
"description": [
|
||||||
"ES6 adds some nice support for removing boiler-plate from object literals declaration.",
|
"ES6 adds some nice support for easily definining object literals.",
|
||||||
"Consider the following:",
|
"Consider the following code:",
|
||||||
"<code>const getMousePosition = (x, y) => {</code>",
|
"<blockquote>const getMousePosition = (x, y) => ({<br> x: x,<br> y: y<br>});</blockquote>",
|
||||||
"<code> return {</code>",
|
"<code>getMousePosition</code> is a simple function that returns an object containing two fields.",
|
||||||
"<code> x: x,</code>",
|
"ES6 provides the syntactic sugar to eliminate the redundancy of having to write <code>x: x</code>. You can simply write <code>x</code> once, and it will be converted to<code>x: x</code> (or something equivalent) under the hood.",
|
||||||
"<code> y: y</code>",
|
"Here is the same function from above rewritten to use this new syntax:",
|
||||||
"<code> } </code>",
|
"<blockquote>const getMousePosition = (x, y) => ({ x, y });</blockquote>",
|
||||||
"<code>}</code>",
|
"<hr>",
|
||||||
"It's a simple function that returns an object, which has two fields.",
|
"Use simple fields with object literals to create and return a <code>Person</code> object."
|
||||||
"ES6 provides a syntactic sugar to remove the redundancy from having to write x: x. You can simply write x once, and it would convert that to x : x (or some equivalent of it) under the hood.",
|
|
||||||
"The code now becomes:",
|
|
||||||
"<code>const getMousePosition = (x, y) => ({x, y})</code>",
|
|
||||||
"Instructions",
|
|
||||||
"Use object literal simplification to create and return a Person object"
|
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"/* Alter code below this line */",
|
"// change code below this line",
|
||||||
"const createPerson = (name, age, gender) => {",
|
"const createPerson = (name, age, gender) => {",
|
||||||
" return {",
|
" return {",
|
||||||
" name: name,",
|
" name: name,",
|
||||||
" age: age,",
|
" age: age,",
|
||||||
" gender: gender",
|
" gender: gender",
|
||||||
" }",
|
" };",
|
||||||
"}",
|
"};",
|
||||||
"/* Alter code above this line */",
|
"// change code above this line",
|
||||||
"console.log(createPerson('Zodiac Hasbro', 56, 'male')); // returns a proper object"
|
"console.log(createPerson(\"Zodiac Hasbro\", 56, \"male\")); // returns a proper object"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"// Test the output is {name: \"Zodiac Hasbro\", age: 56, gender: \"male\"}",
|
"// Test the output is {name: \"Zodiac Hasbro\", age: 56, gender: \"male\"}",
|
||||||
@ -633,29 +614,24 @@
|
|||||||
"id": "587d7b8b367417b2b2512b50",
|
"id": "587d7b8b367417b2b2512b50",
|
||||||
"title": "Write Concise Declarative Functions with ES6",
|
"title": "Write Concise Declarative Functions with ES6",
|
||||||
"description": [
|
"description": [
|
||||||
"With ES6, it's possible to remove the keyword function as follows, from object literals:",
|
"When defining functions within objects in ES5, we have to use the keyword <code>function</code> as follows:",
|
||||||
"<blockquote>const Container extends Component {<br> render: function() {<br> return {<br> Container<br> }<br> }<br>}</blockquote>",
|
"<blockquote>const person = {<br> name: \"Taylor\",<br> sayHello: function() {<br> return `Hello! My name is ${this.name}.`;<br> }<br>};</blockquote>",
|
||||||
"We can remove the function keyword and colon (:) altogether - and get this:",
|
"With ES6, You can remove the <code>function</code> keyword and colon altogether when defining functions in objects. Here's an example of this syntax:",
|
||||||
"<blockquote>const Container extends Component {<br> render() {<br> return {<br> Container<br> }<br> }<br>}</blockquote>",
|
"<blockquote>const person = {<br> name: \"Taylor\",<br> sayHello() {<br> return `Hello! My name is ${this.name}.`;<br> }<br>};</blockquote>",
|
||||||
"Instructions",
|
"<hr>",
|
||||||
"Use object literal simplification to create and return a Person object"
|
"Refactor the function <code>setGear</code> inside the object <code>bicycle</code> to use the shorthand syntax described above."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"/* Alter code below this line */",
|
"// change code below this line",
|
||||||
"const Person = (name, age, gender) => {",
|
"const bicycle = {",
|
||||||
" return {",
|
" gear: 2,",
|
||||||
" name: name,",
|
" setGear: function(newGear) {",
|
||||||
" age: age,",
|
" this.gear = newGear;",
|
||||||
" gender: gender,",
|
|
||||||
" sendFriendRequest: function(person){",
|
|
||||||
" console.log(`Sending request to ${person.name}`);",
|
|
||||||
" }",
|
" }",
|
||||||
" }",
|
"};",
|
||||||
"}",
|
"// change code above this line",
|
||||||
"/* Alter code above this line */",
|
"bicycle.setGear(3);",
|
||||||
"const zod = Person(\"Zodiac Hasbro\", 56, 'male');",
|
"console.log(bicycle.gear);"
|
||||||
"const yan = Person(\"Yanoshi Mimoto\", 55, 'male');",
|
|
||||||
"zod.sendFriendRequest(yan);"
|
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"// Test the output is Sending request to Yanoshi Mimoto",
|
"// Test the output is Sending request to Yanoshi Mimoto",
|
||||||
|
@ -1277,6 +1277,10 @@
|
|||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"var Person = function(firstAndLast) {",
|
"var Person = function(firstAndLast) {",
|
||||||
|
" // Complete the method below and implement the others similarly",
|
||||||
|
" this.getFullName = function() {",
|
||||||
|
" return \"\";",
|
||||||
|
" };",
|
||||||
" return firstAndLast;",
|
" return firstAndLast;",
|
||||||
"};",
|
"};",
|
||||||
"",
|
"",
|
||||||
@ -1346,7 +1350,7 @@
|
|||||||
"id": "af4afb223120f7348cdfc9fd",
|
"id": "af4afb223120f7348cdfc9fd",
|
||||||
"title": "Map the Debris",
|
"title": "Map the Debris",
|
||||||
"description": [
|
"description": [
|
||||||
"Return a new array that transforms the element's average altitude into their orbital periods.",
|
"Return a new array that transforms the element's average altitude into their orbital periods (in seconds).",
|
||||||
"The array will contain objects in the format <code>{name: 'name', avgAlt: avgAlt}</code>.",
|
"The array will contain objects in the format <code>{name: 'name', avgAlt: avgAlt}</code>.",
|
||||||
"You can read about orbital periods <a href=\"http://en.wikipedia.org/wiki/Orbital_period\" target='_blank'>on Wikipedia</a>.",
|
"You can read about orbital periods <a href=\"http://en.wikipedia.org/wiki/Orbital_period\" target='_blank'>on Wikipedia</a>.",
|
||||||
"The values should be rounded to the nearest whole number. The body being orbited is Earth.",
|
"The values should be rounded to the nearest whole number. The body being orbited is Earth.",
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
"id": "587d7db3367417b2b2512b8f",
|
"id": "587d7db3367417b2b2512b8f",
|
||||||
"title": "Match Literal Strings",
|
"title": "Match Literal Strings",
|
||||||
"description": [
|
"description": [
|
||||||
"In the last challenge, you searched for the word <code>\"the\"</code> using the regular expression <code>/the/</code>. That regex searched for a literal match of the string <code>\"the\"</code>. Here's another example searching for a literal match of the string <code>\"Kevin\"</code>:",
|
"In the last challenge, you searched for the word <code>\"Hello\"</code> using the regular expression <code>/Hello/</code>. That regex searched for a literal match of the string <code>\"Hello\"</code>. Here's another example searching for a literal match of the string <code>\"Kevin\"</code>:",
|
||||||
"<blockquote>let testStr = \"Hello, my name is Kevin.\";<br>let testRegex = /Kevin/;<br>testRegex.test(testStr);<br>// Returns true</blockquote>",
|
"<blockquote>let testStr = \"Hello, my name is Kevin.\";<br>let testRegex = /Kevin/;<br>testRegex.test(testStr);<br>// Returns true</blockquote>",
|
||||||
"Any other forms of <code>\"Kevin\"</code> will not match. For example, the regex <code>/Kevin/</code> will not match <code>\"kevin\"</code> or <code>\"KEVIN\"</code>.",
|
"Any other forms of <code>\"Kevin\"</code> will not match. For example, the regex <code>/Kevin/</code> will not match <code>\"kevin\"</code> or <code>\"KEVIN\"</code>.",
|
||||||
"<blockquote>let wrongRegex = /kevin/;<br>wrongRegex.test(testStr);<br>// Returns false</blockquote>",
|
"<blockquote>let wrongRegex = /kevin/;<br>wrongRegex.test(testStr);<br>// Returns false</blockquote>",
|
||||||
|
@ -1107,7 +1107,7 @@
|
|||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert($(\"p span\") && $(\"p span\").length > 0, 'message: Your <code>span</code> element should be inside your <code>p</code> element.');",
|
"assert($(\"p span\") && $(\"p span\").length > 0, 'message: Your <code>span</code> element should be inside your <code>p</code> element.');",
|
||||||
"assert($(\"p span\") && $(\"p span\").text().match(/love/i), 'message: Your <code>span</code> element should have the text <code>love</code>.');",
|
"assert($(\"p span\") && $(\"p span\").text().match(/love/i) && !$(\"p span\").text().match(/Things cats/i), 'message: Your <code>span</code> element should have just the text <code>love</code>.');",
|
||||||
"assert($(\"span\").hasClass(\"text-danger\"), 'message: Your <code>span</code> element should have class <code>text-danger</code>.');",
|
"assert($(\"span\").hasClass(\"text-danger\"), 'message: Your <code>span</code> element should have class <code>text-danger</code>.');",
|
||||||
"assert(code.match(/<\\/span>/g) && code.match(/<span/g) && code.match(/<\\/span>/g).length === code.match(/<span/g).length, 'message: Make sure your <code>span</code> element has a closing tag.');"
|
"assert(code.match(/<\\/span>/g) && code.match(/<span/g) && code.match(/<\\/span>/g).length === code.match(/<span/g).length, 'message: Make sure your <code>span</code> element has a closing tag.');"
|
||||||
],
|
],
|
||||||
@ -1257,7 +1257,7 @@
|
|||||||
],
|
],
|
||||||
"description": [
|
"description": [
|
||||||
"Font Awesome is a convenient library of icons. These icons are vector graphics, stored in the <code>.svg</code> file format. These icons are treated just like fonts. You can specify their size using pixels, and they will assume the font size of their parent HTML elements.",
|
"Font Awesome is a convenient library of icons. These icons are vector graphics, stored in the <code>.svg</code> file format. These icons are treated just like fonts. You can specify their size using pixels, and they will assume the font size of their parent HTML elements.",
|
||||||
"You can add Font Awesome to any app just by including it by adding the following code to the top of your HTML:",
|
"You can include Font Awesome in any app by adding the following code to the top of your HTML:",
|
||||||
"<code><link rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css\"/></code>",
|
"<code><link rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css\"/></code>",
|
||||||
"In this case, we've already added it for you to this page behind the scenes.",
|
"In this case, we've already added it for you to this page behind the scenes.",
|
||||||
"The <code>i</code> element was originally used to make other elements italic, but is now commonly used for icons. You can add the Font Awesome classes to the <code>i</code> element to turn it into an icon, for example:",
|
"The <code>i</code> element was originally used to make other elements italic, but is now commonly used for icons. You can add the Font Awesome classes to the <code>i</code> element to turn it into an icon, for example:",
|
||||||
|
@ -1327,6 +1327,7 @@
|
|||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert($(\".target:nth-child(2)\").hasClass(\"animated\") && $(\".target:nth-child(2)\").hasClass(\"bounce\"), 'message: The second element in your <code>target</code> elements should bounce.');",
|
"assert($(\".target:nth-child(2)\").hasClass(\"animated\") && $(\".target:nth-child(2)\").hasClass(\"bounce\"), 'message: The second element in your <code>target</code> elements should bounce.');",
|
||||||
|
"assert($(\".animated.bounce\").length === 2, 'message: Only two elements should bounce.');",
|
||||||
"assert(code.match(/\\:nth-child\\(/g), 'message: You should use the <code>:nth-child()</code> function to modify these elements.');",
|
"assert(code.match(/\\:nth-child\\(/g), 'message: You should use the <code>:nth-child()</code> function to modify these elements.');",
|
||||||
"assert(code.match(/<button class=\"btn btn-default target\" id=\"target2\">/g), 'message: Only use jQuery to add these classes to the element.');"
|
"assert(code.match(/<button class=\"btn btn-default target\" id=\"target2\">/g), 'message: Only use jQuery to add these classes to the element.');"
|
||||||
],
|
],
|
||||||
|
@ -30,14 +30,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "587d7fad367417b2b2512be1",
|
"id": "587d7fad367417b2b2512be1",
|
||||||
"title": "Trigger click Events with JavaScript",
|
"title": "Handle Click Events with JavaScript using the onclick property",
|
||||||
"description": [
|
"description": [
|
||||||
"This section covers how to get data from APIs. APIs - or Application Programming Interfaces - are tools that computers use to communicate with one another.",
|
|
||||||
"You'll learn how to update HTML with the data we get from these APIs using a technology called AJAX.",
|
|
||||||
"You want your code to execute only once your page has finished loading. For that purpose, you can attach a JavaScript event to the document called <code>DOMContentLoaded</code>. Here's the code that does this:",
|
"You want your code to execute only once your page has finished loading. For that purpose, you can attach a JavaScript event to the document called <code>DOMContentLoaded</code>. Here's the code that does this:",
|
||||||
"<blockquote>document.addEventListener('DOMContentLoaded',function() {<br><br>});</blockquote>",
|
"<blockquote>document.addEventListener('DOMContentLoaded',function() {<br><br>});</blockquote>",
|
||||||
"Next, you can implement a click event handler that goes inside of the <code>DOMContentLoaded</code> function by adding the following code:",
|
"You can implement event handlers that go inside of the <code>DOMContentLoaded</code> function. You can implement an <code>onclick</code> event handler which triggers when the user clicks on the element with id <code>getMessage</code>, by adding the following code:",
|
||||||
"<code>document.getElementById('getMessage').onclick=function(){};</code>",
|
"<blockquote>document.getElementById('getMessage').onclick=function(){};</blockquote>",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Add a click event handler inside of the <code>DOMContentLoaded</code> function for the element with id of <code>getMessage</code>."
|
"Add a click event handler inside of the <code>DOMContentLoaded</code> function for the element with id of <code>getMessage</code>."
|
||||||
],
|
],
|
||||||
@ -170,7 +168,7 @@
|
|||||||
"title": "Get JSON with the JavaScript XMLHttpRequest Method",
|
"title": "Get JSON with the JavaScript XMLHttpRequest Method",
|
||||||
"description": [
|
"description": [
|
||||||
"You can also request data from an external source. This is where APIs come into play.",
|
"You can also request data from an external source. This is where APIs come into play.",
|
||||||
"Remember that APIs - or Application Programming Interfaces - are tools that computers use to communicate with one another.",
|
"Remember that APIs - or Application Programming Interfaces - are tools that computers use to communicate with one another. You'll learn how to update HTML with the data we get from APIs using a technology called AJAX.",
|
||||||
"Most web APIs transfer data in a format called JSON. JSON stands for JavaScript Object Notation.",
|
"Most web APIs transfer data in a format called JSON. JSON stands for JavaScript Object Notation.",
|
||||||
"JSON syntax looks very similar to JavaScript object literal notation. JSON has object properties and their current values, sandwiched between a <code>{</code> and a <code>}</code>.",
|
"JSON syntax looks very similar to JavaScript object literal notation. JSON has object properties and their current values, sandwiched between a <code>{</code> and a <code>}</code>.",
|
||||||
"These properties and their values are often referred to as \"key-value pairs\".",
|
"These properties and their values are often referred to as \"key-value pairs\".",
|
||||||
|
@ -36,9 +36,9 @@
|
|||||||
"id": "bd7158d8c443edefaeb5bdef",
|
"id": "bd7158d8c443edefaeb5bdef",
|
||||||
"title": "Timestamp Microservice",
|
"title": "Timestamp Microservice",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://curse-arrow.gomix.me/' target='_blank'>https://curse-arrow.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://curse-arrow.glitch.me/' target='_blank'>https://curse-arrow.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-timestamp/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-timestamp/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-timestamp/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-timestamp/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -72,9 +72,9 @@
|
|||||||
"id": "bd7158d8c443edefaeb5bdff",
|
"id": "bd7158d8c443edefaeb5bdff",
|
||||||
"title": "Request Header Parser Microservice",
|
"title": "Request Header Parser Microservice",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://dandelion-roar.gomix.me/' target='_blank'>https://dandelion-roar.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://dandelion-roar.glitch.me/' target='_blank'>https://dandelion-roar.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-headerparser/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-headerparser/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-headerparser/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-headerparser/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -100,9 +100,9 @@
|
|||||||
"id": "bd7158d8c443edefaeb5bd0e",
|
"id": "bd7158d8c443edefaeb5bd0e",
|
||||||
"title": "URL Shortener Microservice",
|
"title": "URL Shortener Microservice",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://thread-paper.gomix.me/' target='_blank'>https://thread-paper.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://thread-paper.glitch.me/' target='_blank'>https://thread-paper.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-urlshortener/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-urlshortener/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-urlshortener/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-urlshortener/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -136,9 +136,9 @@
|
|||||||
"id": "bd7158d8c443edefaeb5bdee",
|
"id": "bd7158d8c443edefaeb5bdee",
|
||||||
"title": "Exercise Tracker",
|
"title": "Exercise Tracker",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://fuschia-custard.gomix.me/' target='_blank'>https://fuschia-custard.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://fuschia-custard.glitch.me/' target='_blank'>https://fuschia-custard.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-exercisetracker/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-exercisetracker/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-exercisetracker/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-exercisetracker/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -180,9 +180,9 @@
|
|||||||
"id": "bd7158d8c443edefaeb5bd0f",
|
"id": "bd7158d8c443edefaeb5bd0f",
|
||||||
"title": "File Metadata Microservice",
|
"title": "File Metadata Microservice",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://purple-paladin.gomix.me/' target='_blank'>https://purple-paladin.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://purple-paladin.glitch.me/' target='_blank'>https://purple-paladin.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-filemetadata/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-filemetadata/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-filemetadata/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-filemetadata/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"Working on these challenges will involve you writing your code on Gomix on our starter project. After completing each challenge you can copy your public gomix url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-express/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-express/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!",
|
"Working on these challenges will involve you writing your code on Glitch on our starter project. After completing each challenge you can copy your public Glitch url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicly visible for our testing.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-express/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-express/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -33,8 +33,8 @@
|
|||||||
"id": "587d7fb0367417b2b2512bed",
|
"id": "587d7fb0367417b2b2512bed",
|
||||||
"title": "Meet the Node console",
|
"title": "Meet the Node console",
|
||||||
"description": [
|
"description": [
|
||||||
"During the development process, is important to be able to check what’s going on in your code. Node is just a javascript environment. Like client side javascript, you can use the console to display useful debug information. On your local machine, you would see the console output in a terminal. On Gomix you can open the logs in the lower part of the screen. You can toggle the log panel if you push the button ‘Logs’ (top-left, under the app name).",
|
"During the development process, it is important to be able to check what’s going on in your code. Node is just a JavaScript environment. Like client side JavaScript, you can use the console to display useful debug information. On your local machine, you would see the console output in a terminal. On Glitch you can open the logs in the lower part of the screen. You can toggle the log panel with the button ‘Logs’ (top-left, under the app name).",
|
||||||
"To get started, just put the classic Hello World in the console. We recommend to keep the log panel open while working at these challenges. Reading the logs you can be aware of the nature of the errors that may occur."
|
"To get started, just print the classic \"Hello World\" in the console. We recommend to keep the log panel open while working at these challenges. Reading the logs you can be aware of the nature of the errors that may occur."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -52,12 +52,12 @@
|
|||||||
"id": "587d7fb0367417b2b2512bee",
|
"id": "587d7fb0367417b2b2512bee",
|
||||||
"title": "Start a Working Express Server",
|
"title": "Start a Working Express Server",
|
||||||
"description": [
|
"description": [
|
||||||
"In the first two lines of the file myApp.js you can see how it’s easy to create an Express app object. This object has several methods, and we will learn many of them in these challenges. One fundamental method is <code>app.listen(<port>)</code>. It tells your server to listen on a given port, putting it in running state. You can see it at the bottom of the file. It is inside comments because for testing reasons we need the app to be running in background. All the code that you may want to add goes between these two fundamental parts. Gomix stores the port number in the environemet variable <code>process.env.PORT</code>. Its value is <code>3000</code>.",
|
"In the first two lines of the file myApp.js you can see how it’s easy to create an Express app object. This object has several methods, and we will learn many of them in these challenges. One fundamental method is <code>app.listen(port)</code>. It tells your server to listen on a given port, putting it in running state. You can see it at the bottom of the file. It is inside comments because for testing reasons we need the app to be running in background. All the code that you may want to add goes between these two fundamental parts. Glitch stores the port number in the environemet variable <code>process.env.PORT</code>. Its value is <code>3000</code>.",
|
||||||
"Let’s serve our first string ! In Express, routes takes the following structure: <code>app.METHOD(PATH, HANDLER)</code>. METHOD is an http method in lowercase. PATH is a relative path on the server (it can be a string, or even a regular expression). HANDLER is a function that Express calls when the route is matched.",
|
"Let’s serve our first string! In Express, routes takes the following structure: <code>app.METHOD(PATH, HANDLER)</code>. METHOD is an http method in lowercase. PATH is a relative path on the server (it can be a string, or even a regular expression). HANDLER is a function that Express calls when the route is matched.",
|
||||||
"Handlers take the form <code>function(req, res) {...}</code>, where req is the request object, and res is the response object. For example, the handler",
|
"Handlers take the form <code>function(req, res) {...}</code>, where req is the request object, and res is the response object. For example, the handler",
|
||||||
"<blockquote>function(req, res) {<br> res.send('Response String');<br>}</blockquote>",
|
"<blockquote>function(req, res) {<br> res.send('Response String');<br>}</blockquote>",
|
||||||
"will serve the string 'Response String'.",
|
"will serve the string 'Response String'.",
|
||||||
"Use the <code>app.get()</code> method to serve the string Hello Express, to GET requests matching the / root path. Be sure that your code works by looking at the logs, then see the results in your browser, clicking the button ‘Show Live’ in the Gomix UI."
|
"Use the <code>app.get()</code> method to serve the string Hello Express, to GET requests matching the / root path. Be sure that your code works by looking at the logs, then see the results in your browser, clicking the button ‘Show Live’ in the Glitch UI."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -75,11 +75,11 @@
|
|||||||
"id": "587d7fb0367417b2b2512bef",
|
"id": "587d7fb0367417b2b2512bef",
|
||||||
"title": "Serve an HTML File",
|
"title": "Serve an HTML File",
|
||||||
"description": [
|
"description": [
|
||||||
"We can respond with a file using the method <code>res.sendFile(<file>)</code>.",
|
"We can respond with a file using the method <code>res.sendFile(path)</code>.",
|
||||||
"You can put it inside the <code>app.get('/', ...)</code> route handler. Behind the scenes this method will set the appropriate headers to instruct your browser on how to handle the file you want to send, according to its type. Then it will read and send the file. This method needs an absolute file-path. We recommend you to use the node global variable <code>__dirname</code> to calculate the path.",
|
"You can put it inside the <code>app.get('/', ...)</code> route handler. Behind the scenes this method will set the appropriate headers to instruct your browser on how to handle the file you want to send, according to its type. Then it will read and send the file. This method needs an absolute file path. We recommend you to use the Node global variable <code>__dirname</code> to calculate the path.",
|
||||||
"e.g. <code>absolutePath = __dirname + relativePath/file.ext</code>.",
|
"e.g. <code>absolutePath = __dirname + relativePath/file.ext</code>.",
|
||||||
"The file to send is <code>/views/index.html</code>. Try to ‘Show Live’ your app, you should see a big HTML heading (and a form that we will use later…), with no style applied.",
|
"The file to send is <code>/views/index.html</code>. Try to ‘Show Live’ your app, you should see a big HTML heading (and a form that we will use later…), with no style applied.",
|
||||||
"Note: You can edit the solution of the preceding challenge, or create a new one. If you create a new solution, keep in mind that Express evaluates the routes from top to bottom. It executes the handler for the first match. You have to comment out the preceding solution, or the server will keep responding with a string."
|
"Note: You can edit the solution of the previous challenge, or create a new one. If you create a new solution, keep in mind that Express evaluates the routes from top to bottom. It executes the handler for the first match. You have to comment out the preceding solution, or the server will keep responding with a string."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -97,9 +97,9 @@
|
|||||||
"id": "587d7fb0367417b2b2512bf0",
|
"id": "587d7fb0367417b2b2512bf0",
|
||||||
"title": "Serve Static Assets",
|
"title": "Serve Static Assets",
|
||||||
"description": [
|
"description": [
|
||||||
"An HTML server usually has one or more directories that are accessible by the user. You can place there the static assets needed by your application (stylesheets, scripts, images). In Express you can put in place this functionality using the middleware <code>express.static(<public-dir-absolute_path>)</code>. If don’t know what a midleware is, don’t worry. We’ll discuss about it later in details. Basically middleware are function that intercept route handlers, adding some kind of information. A middleware needs to be mounted using the method <code>app.use([optional-path], <mware function>)</code>. The first path argument is optional. If you don’t pass it, the middleware will be executed for all the requests.",
|
"An HTML server usually has one or more directories that are accessible by the user. You can place there the static assets needed by your application (stylesheets, scripts, images). In Express you can put in place this functionality using the middleware <code>express.static(path)</code>, where the parameter is the absolute path of the folder containing the assets. If don’t know what a middleware is, don’t worry. We’ll discuss about it later in details. Basically middlewares are functions that intercept route handlers, adding some kind of information. A middleware needs to be mounted using the method <code>app.use(path, middlewareFunction)</code>. The first path argument is optional. If you don’t pass it, the middleware will be executed for all the requests.",
|
||||||
"Mount the <code>express.static()</code> middleware for all the requests with <code>app.use()</code>. The absolute path to the assets folder is <code>__dirname + /public</code>.",
|
"Mount the <code>express.static()</code> middleware for all the requests with <code>app.use()</code>. The absolute path to the assets folder is <code>__dirname + /public</code>.",
|
||||||
"Now your app should be able to serve a CSS stylesheet. From outside the public folder will appear mounted to the root directory. Your front-page should look a little better now"
|
"Now your app should be able to serve a CSS stylesheet. From outside the public folder will appear mounted to the root directory. Your front-page should look a little better now!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -117,9 +117,9 @@
|
|||||||
"id": "587d7fb1367417b2b2512bf1",
|
"id": "587d7fb1367417b2b2512bf1",
|
||||||
"title": "Serve JSON on a Specific Route",
|
"title": "Serve JSON on a Specific Route",
|
||||||
"description": [
|
"description": [
|
||||||
"While an HTML server serves (you guessed it!) HTML, an API serves data. A <dfn>REST</dfn> (representational state transfer) API allows data exchange in a simple way, without the need for clients to know any detail about the server. The client only needs to know where the resource is (the URL), and the action it wants to perform on it (the verb). The GET verb is used when you are fetching some information, without modifying anything. These days, the preferred data format for moving information around the web is JSON. Simply put, JSON is a convenient way to represent a JavaScript object as a string, so it can be easily transmitted.",
|
"While an HTML server serves (you guessed it!) HTML, an API serves data. A <dfn>REST</dfn> (REpresentational State Transfer) API allows data exchange in a simple way, without the need for clients to know any detail about the server. The client only needs to know where the resource is (the URL), and the action it wants to perform on it (the verb). The GET verb is used when you are fetching some information, without modifying anything. These days, the preferred data format for moving information around the web is JSON. Simply put, JSON is a convenient way to represent a JavaScript object as a string, so it can be easily transmitted.",
|
||||||
"Let's create a simple API by creating a route that responds with JSON at the path <code>/json</code>. You can do it as usual, with the <code>app.get()</code> method. Inside the route handler use the method <code>res.json()</code>, passing in an object as an argument. This method closes the request-response loop, returning the data. Behind the scenes it converts a valid Javascript object into a string, then sets the appropriate headers to tell your browser that you are serving JSON, and sends the data back. A valid object has the usual structure <code>{key: data}</code>. Data can ba a number, a string, a nested object or an array. Data can also be a variable or the result of a function call; in which case it will be evaluated before being converted into a string.",
|
"Let's create a simple API by creating a route that responds with JSON at the path <code>/json</code>. You can do it as usual, with the <code>app.get()</code> method. Inside the route handler use the method <code>res.json()</code>, passing in an object as an argument. This method closes the request-response loop, returning the data. Behind the scenes it converts a valid JavaScript object into a string, then sets the appropriate headers to tell your browser that you are serving JSON, and sends the data back. A valid object has the usual structure <code>{key: data}</code>. Data can ba a number, a string, a nested object or an array. Data can also be a variable or the result of a function call; in which case it will be evaluated before being converted into a string.",
|
||||||
"Serve the object <code>{\"message\": \"Hello json\"}</code> as a response in JSON format, to the GET requests to the route <code>/json</code>. Then point your browser to <your-app-url>/json, you should see the message on the screen."
|
"Serve the object <code>{\"message\": \"Hello json\"}</code> as a response in JSON format, to the GET requests to the route <code>/json</code>. Then point your browser to your-app-url/json, you should see the message on the screen."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -157,13 +157,13 @@
|
|||||||
"id": "587d7fb1367417b2b2512bf3",
|
"id": "587d7fb1367417b2b2512bf3",
|
||||||
"title": "Implement a Root-Level Request Logger Middleware",
|
"title": "Implement a Root-Level Request Logger Middleware",
|
||||||
"description": [
|
"description": [
|
||||||
"Before we introduced the <code>express.static()</code> middleware function. Now it’s time to see what middleware is, in more detail. Middleware functions are functions that take 3 arguments: the request object, the response object, and the next function in the application’s request-response cycle. These functions execute some code that can have side effects on the app, and usually add informations to the request or response objects. They can also end the cycle sending the response, when some condition is met. If they don’t send the response, when they are done they start the execution of the next function in the stack. This is triggered calling the 3rd argument <code>next()</code>. More information here in the express documentation.",
|
"Before we introduced the <code>express.static()</code> middleware function. Now it’s time to see what middleware is, in more detail. Middleware functions are functions that take 3 arguments: the request object, the response object, and the next function in the application’s request-response cycle. These functions execute some code that can have side effects on the app, and usually add informations to the request or response objects. They can also end the cycle sending the response, when some condition is met. If they don’t send the response, when they are done they start the execution of the next function in the stack. This is triggered calling the 3rd argument <code>next()</code>. More information in the <a href='http://expressjs.com/en/guide/using-middleware.html' target='_blank'>express documentation</a>.",
|
||||||
"Look at the following example :",
|
"Look at the following example :",
|
||||||
"<blockquote>function(req, res, next) {<br> console.log(\"I'm middleware...\");<br> next();<br>}</blockquote>",
|
"<blockquote>function(req, res, next) {<br> console.log(\"I'm a middleware...\");<br> next();<br>}</blockquote>",
|
||||||
"Let’s suppose we mounted this function on a route. When a request matches the route, it displays the string “I’m middleware…”. Then it executes the next function in the stack.",
|
"Let’s suppose we mounted this function on a route. When a request matches the route, it displays the string “I’m a middleware…”. Then it executes the next function in the stack.",
|
||||||
"In this exercise we are going to build a root-level middleware. As we have seen in challenge 4, to mount a middleware function at root level we can use the method <code>app.use(<mware-function)</code>. In this case the function will be executed for all the request. If you want a function to be executed let’s say only for POST request, you could use <code>app.post(<mware-function>)</code>. Analogous methods exist for all the http verbs (GET, DELETE, PUT, …).",
|
"In this exercise we are going to build a root-level middleware. As we have seen in challenge 4, to mount a middleware function at root level we can use the method <code>app.use(<mware-function>)</code>. In this case the function will be executed for all the requests, but you can also set more specific conditions. For example, if you want a function to be executed only for POST requests, you could use <code>app.post(<mware-function>)</code>. Analogous methods exist for all the http verbs (GET, DELETE, PUT, …).",
|
||||||
"Build a simple logger. For every request, it should log in the console a string taking the following format: <code>method path - ip</code>. An example would look like: <code>GET /json - ::ffff:127.0.0.1</code>. Note that there is a space between <code>method</code> and <code>path</code> and that the dash separating <code>path</code> and <code>ip</code> is surrounded by a space on either side. You can get the request method (http verb), the relative route path, and the caller’s ip from the request object, using <code>req.method</code>, <code>req.path</code> and <code>req.ip</code>. Remember to call <code>next()</code> when you are done, or your server will be stuck forever. Be sure to have the ‘Logs’ opened, and see what happens when some request arrives…",
|
"Build a simple logger. For every request, it should log in the console a string taking the following format: <code>method path - ip</code>. An example would look like: <code>GET /json - ::ffff:127.0.0.1</code>. Note that there is a space between <code>method</code> and <code>path</code> and that the dash separating <code>path</code> and <code>ip</code> is surrounded by a space on either side. You can get the request method (http verb), the relative route path, and the caller’s ip from the request object, using <code>req.method</code>, <code>req.path</code> and <code>req.ip</code>. Remember to call <code>next()</code> when you are done, or your server will be stuck forever. Be sure to have the ‘Logs’ opened, and see what happens when some request arrives…",
|
||||||
"Hint: Express evaluates functions in order. This is true for middleware too. If you want it to work for all the routes, it should be mounted before them."
|
"Hint: Express evaluates functions in the order they appear in the code. This is true for middleware too. If you want it to work for all the routes, it should be mounted before them."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -181,11 +181,11 @@
|
|||||||
"id": "587d7fb1367417b2b2512bf4",
|
"id": "587d7fb1367417b2b2512bf4",
|
||||||
"title": "Chain Middleware to Create a Time Server",
|
"title": "Chain Middleware to Create a Time Server",
|
||||||
"description": [
|
"description": [
|
||||||
"Middleware can be mounted at a specific route using <code>app.METHOD('<path>', mware)</code>. Middleware can also be chained inside route definition.",
|
"Middleware can be mounted at a specific route using <code>app.METHOD(path, middlewareFunction)</code>. Middleware can also be chained inside route definition.",
|
||||||
"Look at the following example :",
|
"Look at the following example:",
|
||||||
"<blockquote>app.get('/user', function(req, res, next){<br> req.user = getTheUserSync(); // Hypotetical Synchronous operation<br> next();<br>}, function(req, res) {<br> res.send(req.user)<br>})</blockquote>",
|
"<blockquote>app.get('/user', function(req, res, next) {<br> req.user = getTheUserSync(); // Hypotetical synchronous operation<br> next();<br>}, function(req, res) {<br> res.send(req.user);<br>})</blockquote>",
|
||||||
"This approach is useful to split the server operations into smaller units. That leads a to a better app structure, and the possibility to reuse code in different places. This approach can be also used to perform some validation on the data. At each point of the middleware stack you can block the execution of the current chain and pass control to functions specifically desinged to handle errors. Or you can pass control to the next matching route, to handle special cases. We will see how in the advanced express section.",
|
"This approach is useful to split the server operations into smaller units. That leads a to a better app structure, and the possibility to reuse code in different places. This approach can also be used to perform some validation on the data. At each point of the middleware stack you can block the execution of the current chain and pass control to functions specifically designed to handle errors. Or you can pass control to the next matching route, to handle special cases. We will see how in the advanced Express section.",
|
||||||
"In the route <code>app.get('/now', ...)</code> chain a middleware function and the final handler. In the middleware function you should add the current time to the request object in the <code>req.time</code> key. You can use new <code>Date().toString()</code>. In the handler, respond with a json object, taking the structure <code>{time: req.time}</code>.",
|
"In the route <code>app.get('/now', ...)</code> chain a middleware function and the final handler. In the middleware function you should add the current time to the request object in the <code>req.time</code> key. You can use <code>new Date().toString()</code>. In the handler, respond with a JSON object, taking the structure <code>{time: req.time}</code>.",
|
||||||
"Hint: The test will not pass if you don’t chain the middleware. If you mount the function somewhere else, the test will fail, even if the output result is correct."
|
"Hint: The test will not pass if you don’t chain the middleware. If you mount the function somewhere else, the test will fail, even if the output result is correct."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -208,9 +208,9 @@
|
|||||||
"id": "587d7fb2367417b2b2512bf5",
|
"id": "587d7fb2367417b2b2512bf5",
|
||||||
"title": "Get Route Parameter Input from the Client",
|
"title": "Get Route Parameter Input from the Client",
|
||||||
"description": [
|
"description": [
|
||||||
"When building an API, we have to allow users to comunicate us what they want to get from our service. For example, if the client is requesting informations about a user stored in the database, she needs a way to let us know which user she is interested in. One possible way to get this result, is using route parameters. Route parameters are named segments of the URL, delimited by slashes (/). Each segment captures the value of the part of the URL which match its position. The captured values are populated in the <code>req.params</code> object.",
|
"When building an API, we have to allow users to comunicate us what they want to get from our service. For example, if the client is requesting information about a user stored in the database, they need a way to let us know which user they're interested in. One possible way to achieve this result is using route parameters. Route parameters are named segments of the URL, delimited by slashes (/). Each segment captures the value of the part of the URL which matches its position. The captured values can be found in the <code>req.params</code> object.",
|
||||||
"<blockquote>route_path: '/user/:userId/book/:bookId'<br>actual_request_URL: '/user/546/book/6754' <br>req.params: {userId: '546', bookId: '6754'}</blockquote>",
|
"<blockquote>route_path: '/user/:userId/book/:bookId'<br>actual_request_URL: '/user/546/book/6754' <br>req.params: {userId: '546', bookId: '6754'}</blockquote>",
|
||||||
"Build an echo server, mounted at the route <code>GET /:word/echo</code>. Respond with a json document, taking the structure <code>{echo: <word>}</code>. You can find the word to be repeated at <code>req.params.word</code>. You can test your route from your browser the address bar, visiting some matching routes, e.g. <your-app-rootpath>/freecodecamp/echo"
|
"Build an echo server, mounted at the route <code>GET /:word/echo</code>. Respond with a JSON object, taking the structure <code>{echo: word}</code>. You can find the word to be repeated at <code>req.params.word</code>. You can test your route from your browser's address bar, visiting some matching routes, e.g. your-app-rootpath/freecodecamp/echo"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -232,10 +232,10 @@
|
|||||||
"id": "587d7fb2367417b2b2512bf6",
|
"id": "587d7fb2367417b2b2512bf6",
|
||||||
"title": "Get Query Parameter Input from the Client",
|
"title": "Get Query Parameter Input from the Client",
|
||||||
"description": [
|
"description": [
|
||||||
"Another common way to get input from the client is by encoding the data after the route path, using a Query string. The query string is delimited by a question mark ?, and includes field=value couples. Each couple is separated by an ampersand &. Express can parse the data from the query string, and populate the object <code>req.query</code>. Some character cannot be in URLs, so the query string needs to be encoded. If you use the API from javascript, you can use specific methods to encode/decode the URL.",
|
"Another common way to get input from the client is by encoding the data after the route path, using a query string. The query string is delimited by a question mark (?), and includes field=value couples. Each couple is separated by an ampersand (&). Express can parse the data from the query string, and populate the object <code>req.query</code>. Some characters cannot be in URLs, they have to be encoded in a <a href='https://en.wikipedia.org/wiki/Percent-encoding' target='_blank'>different format</a> before you can send them. If you use the API from JavaScript, you can use specific methods to encode/decode these characters.",
|
||||||
"<blockquote>route_path: '/library'<br>actual_request_URL: '/library?userId=546&bookId=6754' <br>req.query: {userId: '546', bookId: '6754'}</blockquote>",
|
"<blockquote>route_path: '/library'<br>actual_request_URL: '/library?userId=546&bookId=6754' <br>req.query: {userId: '546', bookId: '6754'}</blockquote>",
|
||||||
"Build an API endpoint, mounted at <code>GET /name</code>. Respond with a json document, taking the structure <code>{ name: '<firstname> <lastname>'}</code>. The first and last name parameters should be encoded in a query string e.g. <code>?first=<firstname>&last=<lastname></code>.",
|
"Build an API endpoint, mounted at <code>GET /name</code>. Respond with a JSON document, taking the structure <code>{ name: 'firstname lastname'}</code>. The first and last name parameters should be encoded in a query string e.g. <code>?first=firstname&last=lastname</code>.",
|
||||||
"TIP: In the following exercise we are going to receive data from a POST request, at the same <code>/name</code> route path. If you want you can use the method <code>app.route(<path>).get(<handler>).post(<handler>)</code>. This syntax allows you to chain different verb handlers on the same path route. You can save a bit of typing, and have cleaner code."
|
"TIP: In the following exercise we are going to receive data from a POST request, at the same <code>/name</code> route path. If you want you can use the method <code>app.route(path).get(handler).post(handler)</code>. This syntax allows you to chain different verb handlers on the same path route. You can save a bit of typing, and have cleaner code."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -258,13 +258,13 @@
|
|||||||
"title": "Use body-parser to Parse POST Requests",
|
"title": "Use body-parser to Parse POST Requests",
|
||||||
"description": [
|
"description": [
|
||||||
"Besides GET there is another common http verb, it is POST. POST is the default method used to send client data with HTML forms. In the REST convention POST is used to send data to create new items in the database (a new user, or a new blog post). We don’t have a database in this project, but we are going to learn how to handle POST requests anyway.",
|
"Besides GET there is another common http verb, it is POST. POST is the default method used to send client data with HTML forms. In the REST convention POST is used to send data to create new items in the database (a new user, or a new blog post). We don’t have a database in this project, but we are going to learn how to handle POST requests anyway.",
|
||||||
"In these kind of request the data doesn’t appear in the URL, it is hidden in the request body. This is a part of the HTML request, also called payload. Since HTML is text based, even if you don’t see the data, it doesn’t mean that they are secret. The raw content of an HTTP Post is shown below:",
|
"In these kind of requests the data doesn’t appear in the URL, it is hidden in the request body. This is a part of the HTML request, also called payload. Since HTML is text based, even if you don’t see the data, it doesn’t mean that they are secret. The raw content of an HTTP POST request is shown below:",
|
||||||
"<blockquote>POST /path/subpath HTTP/1.0<br>From: john@example.com<br>User-Agent: someBrowser/1.0<br>Content-Type: application/x-www-form-urlencoded<br>Content-Length: 20<br>name=John+Doe&age=25</blockquote>",
|
"<blockquote>POST /path/subpath HTTP/1.0<br>From: john@example.com<br>User-Agent: someBrowser/1.0<br>Content-Type: application/x-www-form-urlencoded<br>Content-Length: 20<br>name=John+Doe&age=25</blockquote>",
|
||||||
"As you can see the body is encoded like the query string. This is the default format used by HTML forms. With Ajax we can also use JSON to be able to handle data having a more complex structure. There is also another type of encoding : multipart/form-data. This one is used to upload binary files.",
|
"As you can see the body is encoded like the query string. This is the default format used by HTML forms. With Ajax we can also use JSON to be able to handle data having a more complex structure. There is also another type of encoding: multipart/form-data. This one is used to upload binary files.",
|
||||||
"In this exercise we will use an urlencoded body.",
|
"In this exercise we will use an urlencoded body.",
|
||||||
"To parse the data coming from POST requests, you have to install a package: the body-parser. This package allows you to use a series of middleware, which can decode data in different formats. See the docs <a href=\"https://github.com/expressjs/body-parser\" target=\"_blank\" >here</a>.",
|
"To parse the data coming from POST requests, you have to install a package: the body-parser. This package allows you to use a series of middleware, which can decode data in different formats. See the docs <a href=\"https://github.com/expressjs/body-parser\" target=\"_blank\" >here</a>.",
|
||||||
"Install the body-parser module in your package.json. Then require it at the top of the file. Store it in a variable named bodyParser.",
|
"Install the body-parser module in your package.json. Then require it at the top of the file. Store it in a variable named bodyParser.",
|
||||||
"The middleware to handle url encoded data is returned by <code>bodyParser.urlencoded({extended: false})</code>. <code>extended=false</code> is a configuration options that tells the parser to use the classic encoding. When using it, values can be only strings or arrays. The extended version allows more data flexibility, but is outmatched by JSON. <code>app.use()</code> the function returned by the previous method call. As usual, the middleware must be mounted before all the routes which need it."
|
"The middleware to handle url encoded data is returned by <code>bodyParser.urlencoded({extended: false})</code>. <code>extended=false</code> is a configuration option that tells the parser to use the classic encoding. When using it, values can be only strings or arrays. The extended version allows more data flexibility, but it is outmatched by JSON. Pass to <code>app.use()</code> the function returned by the previous method call. As usual, the middleware must be mounted before all the routes which need it."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -284,7 +284,7 @@
|
|||||||
"description": [
|
"description": [
|
||||||
"Mount a POST handler at the path <code>/name</code>. It’s the same path as before. We have prepared a form in the html frontpage. It will submit the same data of exercise 10 (Query string). If the body-parser is configured correctly, you should find the parameters in the object <code>req.body</code>. Have a look at the usual library example:",
|
"Mount a POST handler at the path <code>/name</code>. It’s the same path as before. We have prepared a form in the html frontpage. It will submit the same data of exercise 10 (Query string). If the body-parser is configured correctly, you should find the parameters in the object <code>req.body</code>. Have a look at the usual library example:",
|
||||||
"<blockquote>route: POST '/library'<br>urlencoded_body: userId=546&bookId=6754 <br>req.body: {userId: '546', bookId: '6754'}</blockquote>",
|
"<blockquote>route: POST '/library'<br>urlencoded_body: userId=546&bookId=6754 <br>req.body: {userId: '546', bookId: '6754'}</blockquote>",
|
||||||
"Respond with the same JSON object as before: <code>{name: '<firstname> <lastname>'}</code>. Test if your endpoint works using the html form we provided in the app frontpage.",
|
"Respond with the same JSON object as before: <code>{name: 'firstname lastname'}</code>. Test if your endpoint works using the html form we provided in the app frontpage.",
|
||||||
"Tip: There are several other http methods other than GET and POST. And by convention there is a corrispondence between the http verb, and the operation you are going to execute on the server. The conventional mapping is:",
|
"Tip: There are several other http methods other than GET and POST. And by convention there is a corrispondence between the http verb, and the operation you are going to execute on the server. The conventional mapping is:",
|
||||||
"POST (sometimes PUT) - Create a new resource using the information sent with the request,",
|
"POST (sometimes PUT) - Create a new resource using the information sent with the request,",
|
||||||
"GET - Read an existing resource without modifying it,",
|
"GET - Read an existing resource without modifying it,",
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"Working on these challenges will involve you writing your code on Gomix on our starter project. After completing each challenge you can copy your public gomix url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-npm'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-npm/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!",
|
"Working on these challenges will involve you writing your code on Glitch on our starter project. After completing each challenge you can copy your public Glitch url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicly visible for our testing.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-npm'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-npm/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -35,11 +35,11 @@
|
|||||||
"description": [
|
"description": [
|
||||||
"The file package.json is the center of any Node.js project or npm package. It stores information about your project just like the <head>-section in a HTML document describes the content of a webpage. The package.json consists of a single JSON-object where information is stored in \"key\": value-pairs. There are only two required fields in a minimal package.json - name and version - but it’s a good practice to provide additional information about your project that could be useful to future users or maintainers.",
|
"The file package.json is the center of any Node.js project or npm package. It stores information about your project just like the <head>-section in a HTML document describes the content of a webpage. The package.json consists of a single JSON-object where information is stored in \"key\": value-pairs. There are only two required fields in a minimal package.json - name and version - but it’s a good practice to provide additional information about your project that could be useful to future users or maintainers.",
|
||||||
"The author-field",
|
"The author-field",
|
||||||
"If you go to the Gomix project that you set up previously and look at on the left side of your screen, you’ll find the file tree where you can see an overview of the various files in your project. Under the file tree’s back-end section, you’ll find package.json - the file that we’ll be improving in the next couple of challenges.",
|
"If you go to the Glitch project that you set up previously and look at on the left side of your screen, you’ll find the file tree where you can see an overview of the various files in your project. Under the file tree’s back-end section, you’ll find package.json - the file that we’ll be improving in the next couple of challenges.",
|
||||||
"One of the most common pieces of information in this file is the author-field that specifies who’s the creator of a project. It can either be a string or an object with contact details. The object is recommended for bigger projects but in our case, a simple string like the following example will do.",
|
"One of the most common pieces of information in this file is the author-field that specifies who’s the creator of a project. It can either be a string or an object with contact details. The object is recommended for bigger projects but in our case, a simple string like the following example will do.",
|
||||||
"<code>\"author\": \"Jane Doe\",</code>",
|
"<code>\"author\": \"Jane Doe\",</code>",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"Add your name to the author-field in the package.json of your Gomix project.",
|
"Add your name to the author-field in the package.json of your Glitch project.",
|
||||||
"Remember that you’re writing JSON.",
|
"Remember that you’re writing JSON.",
|
||||||
"All field-names must use double-quotes (\"), e.g. \"author\"",
|
"All field-names must use double-quotes (\"), e.g. \"author\"",
|
||||||
"All fields must be separated with a comma (,)"
|
"All fields must be separated with a comma (,)"
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"Regardless of what you plan for your project, a description is definitely recommended. Let’s add something similar to this:",
|
"Regardless of what you plan for your project, a description is definitely recommended. Let’s add something similar to this:",
|
||||||
"<code>\"description\": \"A project that does something awesome\",</code>",
|
"<code>\"description\": \"A project that does something awesome\",</code>",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"Add a description to the package.json in your Gomix project.",
|
"Add a description to the package.json in your Glitch project.",
|
||||||
"Remember to use double-quotes for field-names (\") and commas (,) to separate fields."
|
"Remember to use double-quotes for field-names (\") and commas (,) to separate fields."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -89,7 +89,7 @@
|
|||||||
"<code>\"keywords\": [ \"descriptive\", \"related\", \"words\" ],</code>",
|
"<code>\"keywords\": [ \"descriptive\", \"related\", \"words\" ],</code>",
|
||||||
"As you can see, this field is structured as an array of double-quoted strings.",
|
"As you can see, this field is structured as an array of double-quoted strings.",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"Add an array of suitable strings to the keywords-field in the package.json of your Gomix project.",
|
"Add an array of suitable strings to the keywords-field in the package.json of your Glitch project.",
|
||||||
"One of the keywords should be freecodecamp."
|
"One of the keywords should be freecodecamp."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -123,7 +123,7 @@
|
|||||||
"Example",
|
"Example",
|
||||||
"<code>\"license\": \"MIT\",</code>",
|
"<code>\"license\": \"MIT\",</code>",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"Fill the license-field in the package.json of your Gomix project as you find suitable."
|
"Fill the license-field in the package.json of your Glitch project as you find suitable."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -145,7 +145,7 @@
|
|||||||
"Example",
|
"Example",
|
||||||
"<code>\"version\": \"1.2\",</code>",
|
"<code>\"version\": \"1.2\",</code>",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"Add a version to the package.json in your Gomix project."
|
"Add a version to the package.json in your Glitch project."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"Working on these challenges will involve you writing your code on Gomix on our starter project. After completing each challenge you can copy your public gomix url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mongomongoose/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-mongomongoose/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!",
|
"Working on these challenges will involve you writing your code on Glitch on our starter project. After completing each challenge you can copy your public glitch url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mongomongoose/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-mongomongoose/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"more fields, use simple validators like required or unique,",
|
"more fields, use simple validators like required or unique,",
|
||||||
"and set default values. See the <a href='http://mongoosejs.com/docs/guide.html'>mongoose docs</a>.",
|
"and set default values. See the <a href='http://mongoosejs.com/docs/guide.html'>mongoose docs</a>.",
|
||||||
"[C]RUD Part I - CREATE",
|
"[C]RUD Part I - CREATE",
|
||||||
"Note: Gomix is a real server, and in real servers the interactions with the db happen in handler functions. These function are executed when some event happens (e.g. someone hits an endpoint on your API). We’ll follow the same approach in these exercises. The done() function is a callback that tells us that we can proceed after completing an asynchronous operation such as inserting, searching, updating or deleting. It’s following the Node convention and should be called as done(null, data) on success, or done(err) on error.",
|
"Note: Glitch is a real server, and in real servers the interactions with the db happen in handler functions. These function are executed when some event happens (e.g. someone hits an endpoint on your API). We’ll follow the same approach in these exercises. The done() function is a callback that tells us that we can proceed after completing an asynchronous operation such as inserting, searching, updating or deleting. It’s following the Node convention and should be called as done(null, data) on success, or done(err) on error.",
|
||||||
"Warning - When interacting with remote services, errors may occur !",
|
"Warning - When interacting with remote services, errors may occur !",
|
||||||
"<code>/* Example */</code>",
|
"<code>/* Example */</code>",
|
||||||
"<code>var someFunc = function(done) {</code>",
|
"<code>var someFunc = function(done) {</code>",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"<em>Authentication</em> is the process or action of verifying the identity of a user or process. Up to this point you have not been able to create an app utilizing this key concept.<br>The most common and easiest to use authentication middleware for Node.js is <a href='http://passportjs.org/'>Passport</a>. It is easy to learn, light-weight, and extremely flexible allowing for many <em>strategies</em>, which we will talk about in later challenges. In addition to authentication we will also look at template engines which allow for use of <em>Pug</em> and web sockets which allow for real time communication between all your clients and your server. Working on these challenges will involve you writing your code on Gomix on our starter project. After completing each challenge you can copy your public gomix url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!",
|
"<em>Authentication</em> is the process or action of verifying the identity of a user or process. Up to this point you have not been able to create an app utilizing this key concept.<br>The most common and easiest to use authentication middleware for Node.js is <a href='http://passportjs.org/'>Passport</a>. It is easy to learn, light-weight, and extremely flexible allowing for many <em>strategies</em>, which we will talk about in later challenges. In addition to authentication we will also look at template engines which allow for use of <em>Pug</em> and web sockets which allow for real time communication between all your clients and your server. Working on these challenges will involve you writing your code on Glitch on our starter project. After completing each challenge you can copy your public glitch url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"id": "5895f700f9fc0f352b528e63",
|
"id": "5895f700f9fc0f352b528e63",
|
||||||
"title": "Set up a Template Engine",
|
"title": "Set up a Template Engine",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"A template engine enables you to use static template files (such as those written in <em>Pug</em>) in your app. At runtime, the template engine replaces variables in a template file with actual values which can be supplied by your server, and transforms the template into a static HTML file that is then sent to the client. This approach makes it easier to design an HTML page and allows for displaying of variables on the page without needing to make an API call from the client.",
|
"A template engine enables you to use static template files (such as those written in <em>Pug</em>) in your app. At runtime, the template engine replaces variables in a template file with actual values which can be supplied by your server, and transforms the template into a static HTML file that is then sent to the client. This approach makes it easier to design an HTML page and allows for displaying of variables on the page without needing to make an API call from the client.",
|
||||||
"To set up <em>Pug</em> for use in your project, you will need to add it as a dependency first in your package.json. <code>\"pug\": \"^0.1.0\"</code>",
|
"To set up <em>Pug</em> for use in your project, you will need to add it as a dependency first in your package.json. <code>\"pug\": \"^0.1.0\"</code>",
|
||||||
"Now to tell Node/Express to use the templating engine you will have to tell your express <b>app</b> to <b>set</b> 'pug' as the 'view-engine'. <code>app.set('view engine', 'pug')</code>",
|
"Now to tell Node/Express to use the templating engine you will have to tell your express <b>app</b> to <b>set</b> 'pug' as the 'view-engine'. <code>app.set('view engine', 'pug')</code>",
|
||||||
@ -59,7 +59,7 @@
|
|||||||
"id": "5895f70bf9fc0f352b528e64",
|
"id": "5895f70bf9fc0f352b528e64",
|
||||||
"title": "Use a Template Engine's Powers",
|
"title": "Use a Template Engine's Powers",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"One of the greatest features of using a template engine is being able to pass variables from the server to the template file before rendering it to HTML.",
|
"One of the greatest features of using a template engine is being able to pass variables from the server to the template file before rendering it to HTML.",
|
||||||
"In your Pug file, you're about to use a variable by referencing the variable name as <code>#{variable_name}</code> inline with other text on an element or by using an equal side on the element without a space such as <code>p= variable_name</code> which sets that p elements text to equal the variable.",
|
"In your Pug file, you're about to use a variable by referencing the variable name as <code>#{variable_name}</code> inline with other text on an element or by using an equal side on the element without a space such as <code>p= variable_name</code> which sets that p elements text to equal the variable.",
|
||||||
"We strongly recommend looking at the syntax and structure of Pug <a href='https://github.com/pugjs/pug'>here</a> on their Githubs README. Pug is all about using whitespace and tabs to show nested elements and cutting down on the amount of code needed to make a beautiful site.",
|
"We strongly recommend looking at the syntax and structure of Pug <a href='https://github.com/pugjs/pug'>here</a> on their Githubs README. Pug is all about using whitespace and tabs to show nested elements and cutting down on the amount of code needed to make a beautiful site.",
|
||||||
@ -85,7 +85,7 @@
|
|||||||
"id": "5895f70cf9fc0f352b528e65",
|
"id": "5895f70cf9fc0f352b528e65",
|
||||||
"title": "Set up Passport",
|
"title": "Set up Passport",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"It's time to set up <em>Passport</em> so we can finally start allowing a user to register or login to an account! In addition to Passport, we will use Express-session to handle sessions. Using this middleware saves the session id as a cookie in the client and allows us to access the session data using that id on the server. This way we keep personal account information out of the cookie used by the client to verify to our server they are authenticated and just keep the <em>key</em> to access the data stored on the server.",
|
"It's time to set up <em>Passport</em> so we can finally start allowing a user to register or login to an account! In addition to Passport, we will use Express-session to handle sessions. Using this middleware saves the session id as a cookie in the client and allows us to access the session data using that id on the server. This way we keep personal account information out of the cookie used by the client to verify to our server they are authenticated and just keep the <em>key</em> to access the data stored on the server.",
|
||||||
"To set up Passport for use in your project, you will need to add it as a dependency first in your package.json. <code>\"passport\": \"^0.3.2\"</code>",
|
"To set up Passport for use in your project, you will need to add it as a dependency first in your package.json. <code>\"passport\": \"^0.3.2\"</code>",
|
||||||
"In addition, add Express-session as a dependency now as well. Express-session has a ton of advanced features you can use but for now we're just going to use the basics! <code>\"express-session\": \"^1.15.0\"</code>",
|
"In addition, add Express-session as a dependency now as well. Express-session has a ton of advanced features you can use but for now we're just going to use the basics! <code>\"express-session\": \"^1.15.0\"</code>",
|
||||||
@ -124,7 +124,7 @@
|
|||||||
"id": "5895f70cf9fc0f352b528e66",
|
"id": "5895f70cf9fc0f352b528e66",
|
||||||
"title": "Serialization of a User Object",
|
"title": "Serialization of a User Object",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"Serialization and deserialization are important concepts in regards to authentication. To serialize an object means to convert its contents into a small <em>key</em> essentially that can then be deserialized into the original object. This is what allows us to know whos communicated with the server without having to send the authentication data like username and password at each request for a new page.",
|
"Serialization and deserialization are important concepts in regards to authentication. To serialize an object means to convert its contents into a small <em>key</em> essentially that can then be deserialized into the original object. This is what allows us to know whos communicated with the server without having to send the authentication data like username and password at each request for a new page.",
|
||||||
"To set this up properly, we need to have a serialize function and a deserialize function. In passport we create these with <code>passport.serializeUser( OURFUNCTION )</code> and <code>passport.dederializeUser( OURFUNCTION )</code>",
|
"To set this up properly, we need to have a serialize function and a deserialize function. In passport we create these with <code>passport.serializeUser( OURFUNCTION )</code> and <code>passport.dederializeUser( OURFUNCTION )</code>",
|
||||||
"The serializeUser is called with 2 arguments, the full user object and a callback used by passport. Returned in the callback should be a unique key to identify that user- the easiest one to use being the users _id in the object as it should be unique as it generated by MongoDb. Similarly deserializeUser is called with that key and a callback function for passport as well, but this time we have to take that key and return the users full object to the callback. To make a query search for a Mongo _id you will have to create <code>const ObjectID = require('mongodb').ObjectID;</code>, and then to use it you call <code>new ObjectID(THE_ID)</code>. Be sure to add MongoDB as a dependency. You can see this in the examples below:",
|
"The serializeUser is called with 2 arguments, the full user object and a callback used by passport. Returned in the callback should be a unique key to identify that user- the easiest one to use being the users _id in the object as it should be unique as it generated by MongoDb. Similarly deserializeUser is called with that key and a callback function for passport as well, but this time we have to take that key and return the users full object to the callback. To make a query search for a Mongo _id you will have to create <code>const ObjectID = require('mongodb').ObjectID;</code>, and then to use it you call <code>new ObjectID(THE_ID)</code>. Be sure to add MongoDB as a dependency. You can see this in the examples below:",
|
||||||
@ -161,7 +161,7 @@
|
|||||||
"id": "5895f70cf9fc0f352b528e67",
|
"id": "5895f70cf9fc0f352b528e67",
|
||||||
"title": "Implement the Serialization of a Passport User",
|
"title": "Implement the Serialization of a Passport User",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"Right now we're not loading an actually users object since we haven't set up our database. This can be done many different ways, but for our project we will connect to the database once when we start the server and keep a persistent connection for the full life-cycle of the app.",
|
"Right now we're not loading an actually users object since we haven't set up our database. This can be done many different ways, but for our project we will connect to the database once when we start the server and keep a persistent connection for the full life-cycle of the app.",
|
||||||
"To do this, add MongoDB as a dependency and require it in your server. (<code>const mongo = require('mongodb').MongoClient;</code>)",
|
"To do this, add MongoDB as a dependency and require it in your server. (<code>const mongo = require('mongodb').MongoClient;</code>)",
|
||||||
"Now we want to the connect to our database then start listening for requests. The purpose of this is to not allow requests before our database is connected or if there is a database error. To accomplish you will want to encompass your serialization and your app listener in the following:",
|
"Now we want to the connect to our database then start listening for requests. The purpose of this is to not allow requests before our database is connected or if there is a database error. To accomplish you will want to encompass your serialization and your app listener in the following:",
|
||||||
@ -190,7 +190,7 @@
|
|||||||
"id": "5895f70df9fc0f352b528e68",
|
"id": "5895f70df9fc0f352b528e68",
|
||||||
"title": "Authentication Strategies",
|
"title": "Authentication Strategies",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"A strategy is a way of authenticating a user. You can use a strategy for allowing users to authenticate based on locally saved information (if you have them register first) or from a variety of providers such as Google or Github. For this project we will set up a local strategy. To see a list of the 100's of strategies, visit Passports site <a href='http://passportjs.org/'>here</a>.",
|
"A strategy is a way of authenticating a user. You can use a strategy for allowing users to authenticate based on locally saved information (if you have them register first) or from a variety of providers such as Google or Github. For this project we will set up a local strategy. To see a list of the 100's of strategies, visit Passports site <a href='http://passportjs.org/'>here</a>.",
|
||||||
"Add <em>passport-local</em> as a dependency and add it to your server as follows: <code>const LocalStrategy = require('passport-local');</code>",
|
"Add <em>passport-local</em> as a dependency and add it to your server as follows: <code>const LocalStrategy = require('passport-local');</code>",
|
||||||
"Now you will have to tell passport to <b>use</b> an instantiated LocalStartegy object with a few settings defined. Make sure this as well as everything from this point on is encapsulated in the database connection since it relies on it! <pre>passport.use(new LocalStrategy(\n function(username, password, done) {\n db.collection('users').findOne({ username: username }, function (err, user) {\n console.log('User '+ username +' attempted to log in.');\n if (err) { return done(err); }\n if (!user) { return done(null, false); }\n if (password !== user.password) { return done(null, false); }\n return done(null, user);\n });\n }\n));</pre> This is defining the process to take when we try to authenticate someone locally. First it tries to find a user in our database with the username entered, then it checks for the password to match, then finally if no errors have popped up that we checked for, like an incorrect password, the users object is returned and they are authenticated.",
|
"Now you will have to tell passport to <b>use</b> an instantiated LocalStartegy object with a few settings defined. Make sure this as well as everything from this point on is encapsulated in the database connection since it relies on it! <pre>passport.use(new LocalStrategy(\n function(username, password, done) {\n db.collection('users').findOne({ username: username }, function (err, user) {\n console.log('User '+ username +' attempted to log in.');\n if (err) { return done(err); }\n if (!user) { return done(null, false); }\n if (password !== user.password) { return done(null, false); }\n return done(null, user);\n });\n }\n));</pre> This is defining the process to take when we try to authenticate someone locally. First it tries to find a user in our database with the username entered, then it checks for the password to match, then finally if no errors have popped up that we checked for, like an incorrect password, the users object is returned and they are authenticated.",
|
||||||
@ -218,7 +218,7 @@
|
|||||||
"id": "5895f70df9fc0f352b528e69",
|
"id": "5895f70df9fc0f352b528e69",
|
||||||
"title": "How to Use Passport Strategies",
|
"title": "How to Use Passport Strategies",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"In the index.pug file supplied there is actually a login form. It has previously been hidden because of the inline javascript <code>if showLogin</code> with the form indented after it. Before showLogin as a variable was never defined, it never rendered the code block containing the form. Go ahead and on the res.render for that page add a new variable to the object <code>showLogin: true</code>. When you refresh your page, you should then see the form! This form is set up to <b>POST</b> on <em>/login</em> so this is where we should set up to accept the POST and authenticate the user.",
|
"In the index.pug file supplied there is actually a login form. It has previously been hidden because of the inline javascript <code>if showLogin</code> with the form indented after it. Before showLogin as a variable was never defined, it never rendered the code block containing the form. Go ahead and on the res.render for that page add a new variable to the object <code>showLogin: true</code>. When you refresh your page, you should then see the form! This form is set up to <b>POST</b> on <em>/login</em> so this is where we should set up to accept the POST and authenticate the user.",
|
||||||
"For this challenge you should add the route /login to accept a POST request. To authenticate on this route you need to add a middleware to do so before then sending a response. This is done by just passing another argument with the middleware before your <code>function(req,res)</code> with your response! The middleware to use is <code>passport.authenticate('local')</code>.",
|
"For this challenge you should add the route /login to accept a POST request. To authenticate on this route you need to add a middleware to do so before then sending a response. This is done by just passing another argument with the middleware before your <code>function(req,res)</code> with your response! The middleware to use is <code>passport.authenticate('local')</code>.",
|
||||||
"<em>passport.authenticate</em> can also take some options as an argument such as: <code>{ failureRedirect: '/' }</code> which is incredibly useful so be sure to add that in as well. As a response after using the middleware (which will only be called if the authentication middleware passes) should be to redirect the user to <em>/profile</em> and that route should render the view 'profile.pug'.",
|
"<em>passport.authenticate</em> can also take some options as an argument such as: <code>{ failureRedirect: '/' }</code> which is incredibly useful so be sure to add that in as well. As a response after using the middleware (which will only be called if the authentication middleware passes) should be to redirect the user to <em>/profile</em> and that route should render the view 'profile.pug'.",
|
||||||
@ -247,7 +247,7 @@
|
|||||||
"id": "5895f70df9fc0f352b528e6a",
|
"id": "5895f70df9fc0f352b528e6a",
|
||||||
"title": "Create New Middleware",
|
"title": "Create New Middleware",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"As in, any user can just go to /profile whether they authenticated or not by typing in the url. We want to prevent this by checking if the user is authenticated first before rendering the profile page. This is the perfect example of when to create a middleware.",
|
"As in, any user can just go to /profile whether they authenticated or not by typing in the url. We want to prevent this by checking if the user is authenticated first before rendering the profile page. This is the perfect example of when to create a middleware.",
|
||||||
"The challenge here is creating the middleware function <code>ensureAuthenticated(req, res, next)</code>, which will check if a user is authenticated by calling passports isAuthenticated on the <em>request</em> which in turn checks for <em>req.user</em> is to be defined. If it is then <em>next()</em> should be called, otherwise we can just respond to the request with a redirect to our homepage to login. An implementation of this middleware is:",
|
"The challenge here is creating the middleware function <code>ensureAuthenticated(req, res, next)</code>, which will check if a user is authenticated by calling passports isAuthenticated on the <em>request</em> which in turn checks for <em>req.user</em> is to be defined. If it is then <em>next()</em> should be called, otherwise we can just respond to the request with a redirect to our homepage to login. An implementation of this middleware is:",
|
||||||
"<pre>function ensureAuthenticated(req, res, next) {\n if (req.isAuthenticated()) {\n return next();\n }\n res.redirect('/');\n};</pre>",
|
"<pre>function ensureAuthenticated(req, res, next) {\n if (req.isAuthenticated()) {\n return next();\n }\n res.redirect('/');\n};</pre>",
|
||||||
@ -276,7 +276,7 @@
|
|||||||
"id": "5895f70ef9fc0f352b528e6b",
|
"id": "5895f70ef9fc0f352b528e6b",
|
||||||
"title": "How to Put a Profile Together",
|
"title": "How to Put a Profile Together",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"Now that we can ensure the user accessing the <em>/profile</em> is authenticated, we can use the information contained in 'req.user' on our page!",
|
"Now that we can ensure the user accessing the <em>/profile</em> is authenticated, we can use the information contained in 'req.user' on our page!",
|
||||||
"Go ahead and pass the object containing the variable <em>username</em> equaling 'req.user.username' into the render method of the profile view. Then go to your 'profile.pug' view and add the line <code>h2.center#welcome Welcome, #{username}!</code> creating the h2 element with the class 'center' and id 'welcome' containing the text 'Welcome, ' and the username!",
|
"Go ahead and pass the object containing the variable <em>username</em> equaling 'req.user.username' into the render method of the profile view. Then go to your 'profile.pug' view and add the line <code>h2.center#welcome Welcome, #{username}!</code> creating the h2 element with the class 'center' and id 'welcome' containing the text 'Welcome, ' and the username!",
|
||||||
"Also in the profile, add a link to <em>/logout</em>. That route will host the logic to unauthenticate a user. <code>a(href='/logout') Logout</code>",
|
"Also in the profile, add a link to <em>/logout</em>. That route will host the logic to unauthenticate a user. <code>a(href='/logout') Logout</code>",
|
||||||
@ -299,7 +299,7 @@
|
|||||||
"id": "58965611f9fc0f352b528e6c",
|
"id": "58965611f9fc0f352b528e6c",
|
||||||
"title": "Logging a User Out",
|
"title": "Logging a User Out",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"Creating the logout logic is easy. The route should just unauthenticate the user and redirect to the home page instead of rendering any view.",
|
"Creating the logout logic is easy. The route should just unauthenticate the user and redirect to the home page instead of rendering any view.",
|
||||||
"In passport, unauthenticating a user is as easy as just calling <code>req.logout();</code> before redirecting.",
|
"In passport, unauthenticating a user is as easy as just calling <code>req.logout();</code> before redirecting.",
|
||||||
"<pre>app.route('/logout')\n .get((req, res) => {\n req.logout();\n res.redirect('/');\n });</pre>",
|
"<pre>app.route('/logout')\n .get((req, res) => {\n req.logout();\n res.redirect('/');\n });</pre>",
|
||||||
@ -328,7 +328,7 @@
|
|||||||
"id": "58966a17f9fc0f352b528e6d",
|
"id": "58966a17f9fc0f352b528e6d",
|
||||||
"title": "Registration of New Users",
|
"title": "Registration of New Users",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"Now we need to allow a new user on our site to register an account. On the res.render for the home page add a new variable to the object passed along- <code>showRegistration: true</code>. When you refresh your page, you should then see the registration form that was already created in your index.pug file! This form is set up to <b>POST</b> on <em>/register</em> so this is where we should set up to accept the POST and create the user object in the database.",
|
"Now we need to allow a new user on our site to register an account. On the res.render for the home page add a new variable to the object passed along- <code>showRegistration: true</code>. When you refresh your page, you should then see the registration form that was already created in your index.pug file! This form is set up to <b>POST</b> on <em>/register</em> so this is where we should set up to accept the POST and create the user object in the database.",
|
||||||
"The logic of the registration route should be as follows: Register the new user > Authenticate the new user > Redirect to /profile",
|
"The logic of the registration route should be as follows: Register the new user > Authenticate the new user > Redirect to /profile",
|
||||||
"The logic of step 1, registering the new user, should be as follows: Query database with a findOne command > if user is returned then it exists and redirect back to home <em>OR</em> if user is undefined and no error occurs then 'insertOne' into the database with the username and password and as long as no errors occur then call <em>next</em> to go to step 2, authenticating the new user, which we've already written the logic for in our POST /login route.",
|
"The logic of step 1, registering the new user, should be as follows: Query database with a findOne command > if user is returned then it exists and redirect back to home <em>OR</em> if user is undefined and no error occurs then 'insertOne' into the database with the username and password and as long as no errors occur then call <em>next</em> to go to step 2, authenticating the new user, which we've already written the logic for in our POST /login route.",
|
||||||
@ -368,7 +368,7 @@
|
|||||||
"id": "58a25c98f9fc0f352b528e7f",
|
"id": "58a25c98f9fc0f352b528e7f",
|
||||||
"title": "Hashing Your Passwords",
|
"title": "Hashing Your Passwords",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"Going back to the information security section you may remember that storing plaintext passwords is <em>never</em> okay. Now it is time to implement BCrypt to solve this issue.",
|
"Going back to the information security section you may remember that storing plaintext passwords is <em>never</em> okay. Now it is time to implement BCrypt to solve this issue.",
|
||||||
"<hr>Add BCrypt as a dependency and require it in your server. You will need to handle hashing in 2 key areas: where you handle registering/saving a new account and when you check to see that a password is correct on login.",
|
"<hr>Add BCrypt as a dependency and require it in your server. You will need to handle hashing in 2 key areas: where you handle registering/saving a new account and when you check to see that a password is correct on login.",
|
||||||
"Currently on our registeration route, you insert a user's password into the database like the following: <code>password: req.body.password</code>. An easy way to implement saving a hash instead is to add the following before your database logic <code>var hash = bcrypt.hashSync(req.body.password, 8);</code> and replacing the <code>req.body.password</code> in the database saving with just <code>password: hash</code>. (In our small scale app, it is fine to use sync hashing especially with a low cost of 8 as it wont block the thread very much at all)",
|
"Currently on our registeration route, you insert a user's password into the database like the following: <code>password: req.body.password</code>. An easy way to implement saving a hash instead is to add the following before your database logic <code>var hash = bcrypt.hashSync(req.body.password, 8);</code> and replacing the <code>req.body.password</code> in the database saving with just <code>password: hash</code>. (In our small scale app, it is fine to use sync hashing especially with a low cost of 8 as it wont block the thread very much at all)",
|
||||||
@ -396,7 +396,7 @@
|
|||||||
"id": "589690e6f9fc0f352b528e6e",
|
"id": "589690e6f9fc0f352b528e6e",
|
||||||
"title": "Clean Up Your Project with Modules",
|
"title": "Clean Up Your Project with Modules",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a>.",
|
||||||
"Right now everything you have is in your server.js file. This can lead to hard to manage code that isn't very expandable.",
|
"Right now everything you have is in your server.js file. This can lead to hard to manage code that isn't very expandable.",
|
||||||
"Create 2 new files: Routes.js and Auth.js",
|
"Create 2 new files: Routes.js and Auth.js",
|
||||||
"Both should start with the following code: <pre>module.exports = function (app, db) {\n\n\n}</pre>",
|
"Both should start with the following code: <pre>module.exports = function (app, db) {\n\n\n}</pre>",
|
||||||
@ -405,7 +405,7 @@
|
|||||||
"Finally, take all of the routes in your server and paste them into your new files and remove them from your server file. Also take the ensureAuthenticated since we created that middleware function for routing specifically. You will have to now correctly add the dependencies in that are used, such as <code>const passport = require('passport');</code>, at the very top above the export line in your routes.js file.",
|
"Finally, take all of the routes in your server and paste them into your new files and remove them from your server file. Also take the ensureAuthenticated since we created that middleware function for routing specifically. You will have to now correctly add the dependencies in that are used, such as <code>const passport = require('passport');</code>, at the very top above the export line in your routes.js file.",
|
||||||
"Keep adding them until no more errors exist, and your server file no longer has any routing!",
|
"Keep adding them until no more errors exist, and your server file no longer has any routing!",
|
||||||
"Now do the same thing in your auth.js file with all of the things related to authentication such as the serialization and the setting up of the local strategy and erase them from your server file. Be sure to add the dependencies in and call <code>auth(app,db)</code> in the server in the same spot. Be sure to have <code>auth(app, db)</code> before <code>routes(app, db)</code> since our registration route depends on passport being initiated!",
|
"Now do the same thing in your auth.js file with all of the things related to authentication such as the serialization and the setting up of the local strategy and erase them from your server file. Be sure to add the dependencies in and call <code>auth(app,db)</code> in the server in the same spot. Be sure to have <code>auth(app, db)</code> before <code>routes(app, db)</code> since our registration route depends on passport being initiated!",
|
||||||
"Congratulations- you're at the end of this section of Advanced Node and Express and have some beautiful code to show for it! Submit your page when you think you've got it right. If you're running into errors, you can check out an example of the completed project <a href='https://gomix.com/#!/project/delicious-herring'>here</a>."
|
"Congratulations- you're at the end of this section of Advanced Node and Express and have some beautiful code to show for it! Submit your page when you think you've got it right. If you're running into errors, you can check out an example of the completed project <a href='https://glitch.com/#!/project/delicious-herring'>here</a>."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -427,7 +427,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"OAuth 2.0 is the industry-standard authorization and is used across the internet for social login such as letting you login to freeCodeCamp with your Github account.<br>Implementing social login with passport using OAuth is extremely easy to do because of the <a href='http://passportjs.org/'>300+ modules</a> already on npm for adding new strategies to your app. In addition to the ease of installation, it is also easier to use because you do not have to deal with a separate <em>registration</em> or any user input.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe. Be sure to enter your database in the .env file- it can be the same one from last project since you'll be using a new collection!",
|
"OAuth 2.0 is the industry-standard authorization and is used across the internet for social login such as letting you login to freeCodeCamp with your Github account.<br>Implementing social login with passport using OAuth is extremely easy to do because of the <a href='http://passportjs.org/'>300+ modules</a> already on npm for adding new strategies to your app. In addition to the ease of installation, it is also easier to use because you do not have to deal with a separate <em>registration</em> or any user input.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe. Be sure to enter your database in the .env file- it can be the same one from last project since you'll be using a new collection!",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -443,10 +443,10 @@
|
|||||||
"id": "589a69f5f9fc0f352b528e70",
|
"id": "589a69f5f9fc0f352b528e70",
|
||||||
"title": "Implementation of Social Authentication",
|
"title": "Implementation of Social Authentication",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a>.",
|
||||||
"The basic path this kind of authentication will follow in your app is: <ol><li>User clicks a button or link sending them to our route to authenticate using a specific strategy (EG. Github)</li><li>Your route calls <code>passport.authenticate('github')</code> which redirects them to Github.</li><li>The page the user lands on, on Github, allows them to login if they aren't already then asks them to approve access to their profile from our app.</li><li>The user is then returned to our app at a specific callback url with their profile if they approved.</li><li>They are now authenticated and your app should check if it is a returning profile, or save it in your database if it is not.</li></ol>",
|
"The basic path this kind of authentication will follow in your app is: <ol><li>User clicks a button or link sending them to our route to authenticate using a specific strategy (EG. Github)</li><li>Your route calls <code>passport.authenticate('github')</code> which redirects them to Github.</li><li>The page the user lands on, on Github, allows them to login if they aren't already then asks them to approve access to their profile from our app.</li><li>The user is then returned to our app at a specific callback url with their profile if they approved.</li><li>They are now authenticated and your app should check if it is a returning profile, or save it in your database if it is not.</li></ol>",
|
||||||
"Strategies with OAuth require you to have at least a <em>Client ID</em> and a <em>Client Secret</em> which is a way for them to verify who the authentication request is coming from and if it is valid. These are obtained from the site you are trying to implement authentication with, such as Github, and are unique to your app- <b>THEY ARE NOT TO BE SHARED</b> and should never be uploaded to a public repository or written directly in your code. A common practice is to put them in your <em>.env</em> file and reference them like: <code>process.env.GITHUB_CLIENT_ID</code>. For this challenge we're going to use the Github strategy.",
|
"Strategies with OAuth require you to have at least a <em>Client ID</em> and a <em>Client Secret</em> which is a way for them to verify who the authentication request is coming from and if it is valid. These are obtained from the site you are trying to implement authentication with, such as Github, and are unique to your app- <b>THEY ARE NOT TO BE SHARED</b> and should never be uploaded to a public repository or written directly in your code. A common practice is to put them in your <em>.env</em> file and reference them like: <code>process.env.GITHUB_CLIENT_ID</code>. For this challenge we're going to use the Github strategy.",
|
||||||
"Obtaining your <em>Client ID and Secret<em> from Github is done in your account profile settings under 'developer settings', then '<a href='https://github.com/settings/developers'>OAuth applications</a>'. Click 'Register a new application', name your app, paste in the url to your gomix homepage (<b>Not the project code's url</b>), and lastly for the callback url, paste in the same url as the homepage but with '/auth/github/callback' added on. This is where users will be redirected to for us to handle after authenticating on Github. Save the returned information as 'GITHUB_CLIENT_ID' and 'GITHUB_CLIENT_SECRET' in your .env file.",
|
"Obtaining your <em>Client ID and Secret<em> from Github is done in your account profile settings under 'developer settings', then '<a href='https://github.com/settings/developers'>OAuth applications</a>'. Click 'Register a new application', name your app, paste in the url to your glitch homepage (<b>Not the project code's url</b>), and lastly for the callback url, paste in the same url as the homepage but with '/auth/github/callback' added on. This is where users will be redirected to for us to handle after authenticating on Github. Save the returned information as 'GITHUB_CLIENT_ID' and 'GITHUB_CLIENT_SECRET' in your .env file.",
|
||||||
"On your remixed project, create 2 routes accepting GET requests: /auth/github and /auth/github/callback. The first should only call passport to authenticate 'github' and the second should call passport to authenticate 'github' with a failure redirect to '/' and then if that is successful redirect to '/profile' (similar to our last project).",
|
"On your remixed project, create 2 routes accepting GET requests: /auth/github and /auth/github/callback. The first should only call passport to authenticate 'github' and the second should call passport to authenticate 'github' with a failure redirect to '/' and then if that is successful redirect to '/profile' (similar to our last project).",
|
||||||
"An example of how '/auth/github/callback' should look is similar to how we handled a normal login in our last project: <pre>app.route('/login')\n .post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => { \n res.redirect('/profile'); \n });</pre>",
|
"An example of how '/auth/github/callback' should look is similar to how we handled a normal login in our last project: <pre>app.route('/login')\n .post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => { \n res.redirect('/profile'); \n });</pre>",
|
||||||
"Submit your page when you think you've got it right. If you're running into errors, you can check out the project up to this point <a href='https://gist.github.com/JosephLivengood/28ea2cae7e1dc6a53d7f0c42d987313b'>here</a>."
|
"Submit your page when you think you've got it right. If you're running into errors, you can check out the project up to this point <a href='https://gist.github.com/JosephLivengood/28ea2cae7e1dc6a53d7f0c42d987313b'>here</a>."
|
||||||
@ -472,7 +472,7 @@
|
|||||||
"id": "589a69f5f9fc0f352b528e71",
|
"id": "589a69f5f9fc0f352b528e71",
|
||||||
"title": "Implementation of Social Authentication II",
|
"title": "Implementation of Social Authentication II",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a>.",
|
||||||
"The last part of setting up your Github authentication is to create the strategy itself. For this, you will need to add the dependency of 'passport-github' to your project and require it as GithubStrategy like <code>const GitHubStrategy = require('passport-github').Strategy;</code>.",
|
"The last part of setting up your Github authentication is to create the strategy itself. For this, you will need to add the dependency of 'passport-github' to your project and require it as GithubStrategy like <code>const GitHubStrategy = require('passport-github').Strategy;</code>.",
|
||||||
"To set up the Github strategy, you have to tell <b>passport</b> to <b>use</b> an instantiated <b>GithubStrategy</b>, which accepts 2 arguments: An object (containing <em>clientID</em>, <em>clientSecret</em>, and <em>callbackURL</em>) and a function to be called when a user is successfully authenticated which we will determine if the user is new and what fields to save initially in the user's database object. This is common across many strategies but some may require more information as outlined in that specific strategy's github README; for example, Google requires a <em>scope</em> as well which determines what kind of information your request is asking returned and asks the user to approve such access. The current strategy we are implementing has its usage outlined <a>here</a>, but we're going through it all right here on freeCodeCamp!",
|
"To set up the Github strategy, you have to tell <b>passport</b> to <b>use</b> an instantiated <b>GithubStrategy</b>, which accepts 2 arguments: An object (containing <em>clientID</em>, <em>clientSecret</em>, and <em>callbackURL</em>) and a function to be called when a user is successfully authenticated which we will determine if the user is new and what fields to save initially in the user's database object. This is common across many strategies but some may require more information as outlined in that specific strategy's github README; for example, Google requires a <em>scope</em> as well which determines what kind of information your request is asking returned and asks the user to approve such access. The current strategy we are implementing has its usage outlined <a>here</a>, but we're going through it all right here on freeCodeCamp!",
|
||||||
"Here's how your new strategy should look at this point: <pre>passport.use(new GitHubStrategy({\n clientID: process.env.GITHUB_CLIENT_ID,\n clientSecret: process.env.GITHUB_CLIENT_SECRET,\n callbackURL: /*INSERT CALLBACK URL ENTERED INTO GITHUB HERE*/\n },\n function(accessToken, refreshToken, profile, cb) {\n console.log(profile);\n //Database logic here with callback containing our user object\n }\n));</pre>",
|
"Here's how your new strategy should look at this point: <pre>passport.use(new GitHubStrategy({\n clientID: process.env.GITHUB_CLIENT_ID,\n clientSecret: process.env.GITHUB_CLIENT_SECRET,\n callbackURL: /*INSERT CALLBACK URL ENTERED INTO GITHUB HERE*/\n },\n function(accessToken, refreshToken, profile, cb) {\n console.log(profile);\n //Database logic here with callback containing our user object\n }\n));</pre>",
|
||||||
@ -504,11 +504,11 @@
|
|||||||
"id": "589a8eb3f9fc0f352b528e72",
|
"id": "589a8eb3f9fc0f352b528e72",
|
||||||
"title": "Implementation of Social Authentication III",
|
"title": "Implementation of Social Authentication III",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a>.",
|
||||||
"The final part of the strategy is handling the profile returned from Github. We need to load the users database object if it exists or create one if it doesn't and populate the fields from the profile, then return the user's object. Github supplies us a unique <em>id</em> within each profile which we can use to search with to serialize the user with (already implemented). Below is an example implementation you can use in your project- it goes within the function that is the second argument for the new strategy, right below the <code>console.log(profile);</code> currently is:",
|
"The final part of the strategy is handling the profile returned from Github. We need to load the users database object if it exists or create one if it doesn't and populate the fields from the profile, then return the user's object. Github supplies us a unique <em>id</em> within each profile which we can use to search with to serialize the user with (already implemented). Below is an example implementation you can use in your project- it goes within the function that is the second argument for the new strategy, right below the <code>console.log(profile);</code> currently is:",
|
||||||
"<pre>db.collection('socialusers').findAndModify(\n {id: profile.id},\n {},\n {$setOnInsert:{\n id: profile.id,\n name: profile.displayName || 'John Doe',\n photo: profile.photos[0].value || '',\n email: profile.emails[0].value || 'No public email',\n created_on: new Date(),\n provider: profile.provider || ''\n },$set:{\n last_login: new Date()\n },$inc:{\n login_count: 1\n }},\n {upsert:true, new: true},\n (err, doc) => {\n return cb(null, doc.value);\n }\n);</pre>",
|
"<pre>db.collection('socialusers').findAndModify(\n {id: profile.id},\n {},\n {$setOnInsert:{\n id: profile.id,\n name: profile.displayName || 'John Doe',\n photo: profile.photos[0].value || '',\n email: profile.emails[0].value || 'No public email',\n created_on: new Date(),\n provider: profile.provider || ''\n },$set:{\n last_login: new Date()\n },$inc:{\n login_count: 1\n }},\n {upsert:true, new: true},\n (err, doc) => {\n return cb(null, doc.value);\n }\n);</pre>",
|
||||||
"With a findAndModify, it allows you to search for an object and update it, as well as upsert the object if it doesn't exist and receive the new object back each time in our callback function. In this example, we always set the last_login as now, we always increment the login_count by 1, and only when we insert a new object(new user) do we populate the majority of the fields. Something to notice also is the use of default values. Sometimes a profile returned won't have all the information filled out or it will have been chosen by the user to remain private; so in this case we have to handle it to prevent an error.",
|
"With a findAndModify, it allows you to search for an object and update it, as well as upsert the object if it doesn't exist and receive the new object back each time in our callback function. In this example, we always set the last_login as now, we always increment the login_count by 1, and only when we insert a new object(new user) do we populate the majority of the fields. Something to notice also is the use of default values. Sometimes a profile returned won't have all the information filled out or it will have been chosen by the user to remain private; so in this case we have to handle it to prevent an error.",
|
||||||
"You should be able to login to your app now- try it! Submit your page when you think you've got it right. If you're running into errors, you can check out an example of this mini-project's finished code <a href='https://gomix.com/#!/project/guttural-birch'>here</a>."
|
"You should be able to login to your app now- try it! Submit your page when you think you've got it right. If you're running into errors, you can check out an example of this mini-project's finished code <a href='https://glitch.com/#!/project/guttural-birch'>here</a>."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -530,7 +530,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"<dfn>Socket.IO</dfn> enables real-time, reliable, speedy communication between your server and clients from all devices and browsers. It listens for connects on your server that come from the client which connects with a single javascript statement. The whole library is based on emitting, broadcasting, and recieving events that contain an event name and some data which can include things like strings, objects, arrays, and even blobs like files or video. This is used for all sorts of purposes including instant messaging online, real-time analytics, streaming, and document collaboration.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe.",
|
"<dfn>Socket.IO</dfn> enables real-time, reliable, speedy communication between your server and clients from all devices and browsers. It listens for connects on your server that come from the client which connects with a single javascript statement. The whole library is based on emitting, broadcasting, and recieving events that contain an event name and some data which can include things like strings, objects, arrays, and even blobs like files or video. This is used for all sorts of purposes including instant messaging online, real-time analytics, streaming, and document collaboration.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe.",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -569,7 +569,7 @@
|
|||||||
"id": "589fc830f9fc0f352b528e74",
|
"id": "589fc830f9fc0f352b528e74",
|
||||||
"title": "Set up the Enviroment",
|
"title": "Set up the Enviroment",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
||||||
"Add Socket.IO as a dependency and require/instanciate it in your server defined as 'io' with the http server as an argument. <code>const io = require('socket.io')(http);</code>",
|
"Add Socket.IO as a dependency and require/instanciate it in your server defined as 'io' with the http server as an argument. <code>const io = require('socket.io')(http);</code>",
|
||||||
"The first thing needing to be handled is listening for a new connection from the client. The <dfn>on</dfn> keyword does just that- listen for a specific event. It requires 2 arguments: a string containing the title of the event thats emitted, and a function with which the data is passed though. In the case of our connection listener, we use <em>socket</em> to define the data in the second argument. A socket is an individual client who is connected.",
|
"The first thing needing to be handled is listening for a new connection from the client. The <dfn>on</dfn> keyword does just that- listen for a specific event. It requires 2 arguments: a string containing the title of the event thats emitted, and a function with which the data is passed though. In the case of our connection listener, we use <em>socket</em> to define the data in the second argument. A socket is an individual client who is connected.",
|
||||||
"For listening for connections on our server, add the following between the comments in your project:<pre>io.on('connection', socket => {\n console.log('A user has connected');\n});</pre>",
|
"For listening for connections on our server, add the following between the comments in your project:<pre>io.on('connection', socket => {\n console.log('A user has connected');\n});</pre>",
|
||||||
@ -607,7 +607,7 @@
|
|||||||
"id": "589fc831f9fc0f352b528e75",
|
"id": "589fc831f9fc0f352b528e75",
|
||||||
"title": "Communicate by Emitting",
|
"title": "Communicate by Emitting",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
||||||
"<dfn>Emit</dfn> is the most common way of communicating you will use. When you emit something from the server to 'io', you send an event's name and data to all the connected sockets. A good example of this concept would be emiting the current count of connected users each time a new user connects!",
|
"<dfn>Emit</dfn> is the most common way of communicating you will use. When you emit something from the server to 'io', you send an event's name and data to all the connected sockets. A good example of this concept would be emiting the current count of connected users each time a new user connects!",
|
||||||
"<hr>Start by adding a variable to keep track of the users just before where you are currently listening for connections. <code>var currentUsers = 0;</code>",
|
"<hr>Start by adding a variable to keep track of the users just before where you are currently listening for connections. <code>var currentUsers = 0;</code>",
|
||||||
"Now when someone connects you should increment the count before emiting the count so you will want to add the incrementer within the connection listener. <code>++currentUsers;</code>",
|
"Now when someone connects you should increment the count before emiting the count so you will want to add the incrementer within the connection listener. <code>++currentUsers;</code>",
|
||||||
@ -641,7 +641,7 @@
|
|||||||
"id": "589fc831f9fc0f352b528e76",
|
"id": "589fc831f9fc0f352b528e76",
|
||||||
"title": "Handle a Disconnect",
|
"title": "Handle a Disconnect",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
||||||
"You may notice that up to now you have only been increasing the user count. Handling a user disconnecting is just as easy as handling the initial connect except the difference is you have to listen for it on each socket versus on the whole server.",
|
"You may notice that up to now you have only been increasing the user count. Handling a user disconnecting is just as easy as handling the initial connect except the difference is you have to listen for it on each socket versus on the whole server.",
|
||||||
"<hr>To do this, add in to your existing connect listener a listener that listens for 'disconnect' on the socket with no data passed through. You can test this functionality by just logging to the console a user has disconnected. <code>socket.on('disconnect', () => { /*anything you want to do on disconnect*/ });</code>",
|
"<hr>To do this, add in to your existing connect listener a listener that listens for 'disconnect' on the socket with no data passed through. You can test this functionality by just logging to the console a user has disconnected. <code>socket.on('disconnect', () => { /*anything you want to do on disconnect*/ });</code>",
|
||||||
"To make sure clients continuously have the updated count of current users, you should decrease the currentUsers by 1 when the disconnect happens then emit the 'user count' event with the updated count!",
|
"To make sure clients continuously have the updated count of current users, you should decrease the currentUsers by 1 when the disconnect happens then emit the 'user count' event with the updated count!",
|
||||||
@ -669,7 +669,7 @@
|
|||||||
"id": "589fc831f9fc0f352b528e77",
|
"id": "589fc831f9fc0f352b528e77",
|
||||||
"title": "Authentication with Socket.IO",
|
"title": "Authentication with Socket.IO",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
||||||
"Currently, you cannot determine who is connected to your web socket. While 'req.user' containers the user object, thats only when your user interacts with the web server and with web sockets you have no req (request) and therefor no user data. One way to solve the problem of knowing who is connected to your web socket is by parsing and decoding the cookie that contains the passport session then deserializing it to obtain the user object. Luckily, there is a package on NPM just for this that turns a once complex task into something simple!",
|
"Currently, you cannot determine who is connected to your web socket. While 'req.user' containers the user object, thats only when your user interacts with the web server and with web sockets you have no req (request) and therefor no user data. One way to solve the problem of knowing who is connected to your web socket is by parsing and decoding the cookie that contains the passport session then deserializing it to obtain the user object. Luckily, there is a package on NPM just for this that turns a once complex task into something simple!",
|
||||||
"<hr>Add 'passport.socketio' as a dependency and require it as 'passportSocketIo'.",
|
"<hr>Add 'passport.socketio' as a dependency and require it as 'passportSocketIo'.",
|
||||||
"Now we just have to tell Socket.IO to use it and set the options. Be sure this is added before the existing socket code and not in the existing connection listener. For your server it should look as follows:<pre>io.use(passportSocketIo.authorize({\n cookieParser: cookieParser,\n key: 'express.sid',\n secret: process.env.SESSION_SECRET,\n store: sessionStore\n}));</pre>You can also optionally pass 'success' and 'fail' with a function that will be called after the authentication process completes when a client trys to connect.",
|
"Now we just have to tell Socket.IO to use it and set the options. Be sure this is added before the existing socket code and not in the existing connection listener. For your server it should look as follows:<pre>io.use(passportSocketIo.authorize({\n cookieParser: cookieParser,\n key: 'express.sid',\n secret: process.env.SESSION_SECRET,\n store: sessionStore\n}));</pre>You can also optionally pass 'success' and 'fail' with a function that will be called after the authentication process completes when a client trys to connect.",
|
||||||
@ -701,7 +701,7 @@
|
|||||||
"id": "589fc832f9fc0f352b528e78",
|
"id": "589fc832f9fc0f352b528e78",
|
||||||
"title": "Announce New Users",
|
"title": "Announce New Users",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
||||||
"Many chat rooms are able to annouce when a user connects or disconnects and then display that to all of the connected users in the chat. Seeing as though you already are emitting an event on connect and disconnect, you will just have to modify this event to support such feature. The most logical way of doing so is sending 3 pieces of data with the event: name of the user connected/disconnected, the current user count, and if that name connected or disconnected.",
|
"Many chat rooms are able to annouce when a user connects or disconnects and then display that to all of the connected users in the chat. Seeing as though you already are emitting an event on connect and disconnect, you will just have to modify this event to support such feature. The most logical way of doing so is sending 3 pieces of data with the event: name of the user connected/disconnected, the current user count, and if that name connected or disconnected.",
|
||||||
"<hr>Change the event name to 'user' and as the data pass an object along containing fields 'name', 'currentUsers', and boolean 'connected' (to be true if connection, or false for disconnection of the user sent). Be sure to make the change to both points we had the 'user count' event and set the disconnect one to sent false for field 'connected' instead of true like the event emitted on connect. <code>io.emit('user', {name: socket.request.user.name, currentUsers, connected: true});</code>",
|
"<hr>Change the event name to 'user' and as the data pass an object along containing fields 'name', 'currentUsers', and boolean 'connected' (to be true if connection, or false for disconnection of the user sent). Be sure to make the change to both points we had the 'user count' event and set the disconnect one to sent false for field 'connected' instead of true like the event emitted on connect. <code>io.emit('user', {name: socket.request.user.name, currentUsers, connected: true});</code>",
|
||||||
"Now your client will have all the nesesary information to correctly display the current user count and annouce when a user connects or disconnects! To handle this event on the client side we should listen for 'user' and then update the current user count by using jQuery to change the text of <code>#num-users</code> to '{NUMBER} users online', as well as append a <code><li></code> to the unordered list with id 'messages' with '{NAME} has {joined/left} the chat.'.",
|
"Now your client will have all the nesesary information to correctly display the current user count and annouce when a user connects or disconnects! To handle this event on the client side we should listen for 'user' and then update the current user count by using jQuery to change the text of <code>#num-users</code> to '{NUMBER} users online', as well as append a <code><li></code> to the unordered list with id 'messages' with '{NAME} has {joined/left} the chat.'.",
|
||||||
@ -729,7 +729,7 @@
|
|||||||
"id": "589fc832f9fc0f352b528e79",
|
"id": "589fc832f9fc0f352b528e79",
|
||||||
"title": "Send and Display Chat Messages",
|
"title": "Send and Display Chat Messages",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-socketio/'>GitHub</a>.",
|
||||||
"It's time you start allowing clients to send a chat message to the server to emit to all the clients! Already in your client.js file you should see there is already a block of code handling when the messgae form is submitted! (<code>$('form').submit(function(){ /*logic*/ });</code>)",
|
"It's time you start allowing clients to send a chat message to the server to emit to all the clients! Already in your client.js file you should see there is already a block of code handling when the messgae form is submitted! (<code>$('form').submit(function(){ /*logic*/ });</code>)",
|
||||||
"<hr>Within the code you're handling the form submit you should emit an event after you define 'messageToSend' but before you clear the text box <code>#m</code>. The event should be named 'chat message' and the data should just be 'messageToSend'. <code>socket.emit('chat message', messageToSend);</code>",
|
"<hr>Within the code you're handling the form submit you should emit an event after you define 'messageToSend' but before you clear the text box <code>#m</code>. The event should be named 'chat message' and the data should just be 'messageToSend'. <code>socket.emit('chat message', messageToSend);</code>",
|
||||||
"Now on your server you should be listening to the socket for the event 'chat message' with the data being named 'message'. Once the event is recieved it should then emit the event 'chat message' to all sockets <code>io.emit</code> with the data being an object containing 'name' and 'message'.",
|
"Now on your server you should be listening to the socket for the event 'chat message' with the data being named 'message'. Once the event is recieved it should then emit the event 'chat message' to all sockets <code>io.emit</code> with the data being an object containing 'name' and 'message'.",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"Working on these challenges will involve you writing your code on Gomix on our starter project. After completing each challenge you can copy your public gomix url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!",
|
"Working on these challenges will involve you writing your code on Glitch on our starter project. After completing each challenge you can copy your public glitch url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"id": "587d8247367417b2b2512c36",
|
"id": "587d8247367417b2b2512c36",
|
||||||
"title": "Install and Require Helmet",
|
"title": "Install and Require Helmet",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"Helmet helps you secure your Express apps by setting various HTTP headers. Install the package, then require it."
|
"Helmet helps you secure your Express apps by setting various HTTP headers. Install the package, then require it."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -48,7 +48,7 @@
|
|||||||
"id": "587d8247367417b2b2512c37",
|
"id": "587d8247367417b2b2512c37",
|
||||||
"title": "Hide Potentially Dangerous Information Using helmet.hidePoweredBy()",
|
"title": "Hide Potentially Dangerous Information Using helmet.hidePoweredBy()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"Hackers can exploit known vulnerabilities in Express/Node if they see that your site is powered by Express. X-Powered-By: Express is sent in every request coming from Express by default. The helmet.hidePoweredBy() middleware will remove the X-Powered-By header. You can also explicitly set the header to something else, to throw people off. e.g. app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }))"
|
"Hackers can exploit known vulnerabilities in Express/Node if they see that your site is powered by Express. X-Powered-By: Express is sent in every request coming from Express by default. The helmet.hidePoweredBy() middleware will remove the X-Powered-By header. You can also explicitly set the header to something else, to throw people off. e.g. app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }))"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -69,7 +69,7 @@
|
|||||||
"id": "587d8247367417b2b2512c38",
|
"id": "587d8247367417b2b2512c38",
|
||||||
"title": "Mitigate the Risk of Clickjacking with helmet.frameguard()",
|
"title": "Mitigate the Risk of Clickjacking with helmet.frameguard()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"Your page could be put in a <frame> or <iframe> without your consent. This can result in clickjacking attacks, among other things. Clickjacking is a technique of tricking a user into interacting with a page different from what the user thinks it is. This can be obtained executing your page in a malicious context, by mean of iframing. In that context a hacker can put a hidden layer over your page. Hidden buttons can be used to run bad scripts. This middleware sets the X-Frame-Options header. It restricts who can put your site in a frame. It has three modes: DENY, SAMEORIGIN, and ALLOW-FROM.",
|
"Your page could be put in a <frame> or <iframe> without your consent. This can result in clickjacking attacks, among other things. Clickjacking is a technique of tricking a user into interacting with a page different from what the user thinks it is. This can be obtained executing your page in a malicious context, by mean of iframing. In that context a hacker can put a hidden layer over your page. Hidden buttons can be used to run bad scripts. This middleware sets the X-Frame-Options header. It restricts who can put your site in a frame. It has three modes: DENY, SAMEORIGIN, and ALLOW-FROM.",
|
||||||
"We don’t need our app to be framed. You should use helmet.frameguard() passing with the configuration object {action: 'deny'}."
|
"We don’t need our app to be framed. You should use helmet.frameguard() passing with the configuration object {action: 'deny'}."
|
||||||
],
|
],
|
||||||
@ -95,7 +95,7 @@
|
|||||||
"id": "587d8247367417b2b2512c39",
|
"id": "587d8247367417b2b2512c39",
|
||||||
"title": "Mitigate the Risk of Cross Site Scripting (XSS) Attacks with helmet.xssFilter()",
|
"title": "Mitigate the Risk of Cross Site Scripting (XSS) Attacks with helmet.xssFilter()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"Cross-site scripting (XSS) is a frequent type of attack where malicious scripts are injected into vulnerable pages, with the purpose of stealing sensitive data like session cookies, or passwords.",
|
"Cross-site scripting (XSS) is a frequent type of attack where malicious scripts are injected into vulnerable pages, with the purpose of stealing sensitive data like session cookies, or passwords.",
|
||||||
"The basic rule to lower the risk of an XSS attack is simple: “Never trust user’s input”. As a developer you should always sanitize all the input coming from the outside. This includes data coming from forms, GET query urls, and even from POST bodies. Sanitizing means that you should find and encode the characters that may be dangerous e.g. <, >.",
|
"The basic rule to lower the risk of an XSS attack is simple: “Never trust user’s input”. As a developer you should always sanitize all the input coming from the outside. This includes data coming from forms, GET query urls, and even from POST bodies. Sanitizing means that you should find and encode the characters that may be dangerous e.g. <, >.",
|
||||||
"Modern browsers can help mitigating the risk by adopting better software strategies. Often these are configurable via http headers.",
|
"Modern browsers can help mitigating the risk by adopting better software strategies. Often these are configurable via http headers.",
|
||||||
@ -120,7 +120,7 @@
|
|||||||
"id": "587d8248367417b2b2512c3a",
|
"id": "587d8248367417b2b2512c3a",
|
||||||
"title": "Avoid Inferring the Response MIME Type with helmet.noSniff()",
|
"title": "Avoid Inferring the Response MIME Type with helmet.noSniff()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"Browsers can use content or MIME sniffing to adapt to different datatypes coming from a response. They override the Content-Type headers to guess and process the data. While this can be convenient in some scenarios, it can also lead to some dangerous attacks. This middleware sets the X-Content-Type-Options header to nosniff. This instructs the browser to not bypass the provided Content-Type."
|
"Browsers can use content or MIME sniffing to adapt to different datatypes coming from a response. They override the Content-Type headers to guess and process the data. While this can be convenient in some scenarios, it can also lead to some dangerous attacks. This middleware sets the X-Content-Type-Options header to nosniff. This instructs the browser to not bypass the provided Content-Type."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -141,7 +141,7 @@
|
|||||||
"id": "587d8248367417b2b2512c3b",
|
"id": "587d8248367417b2b2512c3b",
|
||||||
"title": "Prevent IE from Opening Untrusted HTML with helmet.ieNoOpen()",
|
"title": "Prevent IE from Opening Untrusted HTML with helmet.ieNoOpen()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"Some web applications will serve untrusted HTML for download. Some versions of Internet Explorer by default open those HTML files in the context of your site. This means that an untrusted HTML page could start doing bad things in the context of your pages. This middleware sets the X-Download-Options header to noopen. This will prevent IE users from executing downloads in the trusted site’s context."
|
"Some web applications will serve untrusted HTML for download. Some versions of Internet Explorer by default open those HTML files in the context of your site. This means that an untrusted HTML page could start doing bad things in the context of your pages. This middleware sets the X-Download-Options header to noopen. This will prevent IE users from executing downloads in the trusted site’s context."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -162,9 +162,9 @@
|
|||||||
"id": "587d8248367417b2b2512c3c",
|
"id": "587d8248367417b2b2512c3c",
|
||||||
"title": "Ask Browsers to Access Your Site via HTTPS Only with helmet.hsts()",
|
"title": "Ask Browsers to Access Your Site via HTTPS Only with helmet.hsts()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"HTTP Strict Transport Security (HSTS) is a web security policy which helps to protect websites against protocol downgrade attacks and cookie hijacking. If your website can be accessed via HTTPS you can ask user’s browsers to avoid using insecure HTTP. By setting the header Strict-Transport-Security, you tell the browsers to use HTTPS for the future requests in a specified amount of time. This will work for the requests coming after the initial request.",
|
"HTTP Strict Transport Security (HSTS) is a web security policy which helps to protect websites against protocol downgrade attacks and cookie hijacking. If your website can be accessed via HTTPS you can ask user’s browsers to avoid using insecure HTTP. By setting the header Strict-Transport-Security, you tell the browsers to use HTTPS for the future requests in a specified amount of time. This will work for the requests coming after the initial request.",
|
||||||
"Configure helmet.hsts() to use HTTPS for the next 90 days. Pass the config object {maxAge: timeInMilliseconds, force: true}. Gomix already has hsts enabled. To override its settings you need to set the field \"force\" to true in the config object. We will intercept and restore the Gomix header, after inspecting it for testing.",
|
"Configure helmet.hsts() to use HTTPS for the next 90 days. Pass the config object {maxAge: timeInMilliseconds, force: true}. Glitch already has hsts enabled. To override its settings you need to set the field \"force\" to true in the config object. We will intercept and restore the Glitch header, after inspecting it for testing.",
|
||||||
"Note: Configuring HTTPS on a custom website requires the acquisition of a domain, and a SSL/TSL Certificate."
|
"Note: Configuring HTTPS on a custom website requires the acquisition of a domain, and a SSL/TSL Certificate."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -189,7 +189,7 @@
|
|||||||
"id": "587d8248367417b2b2512c3d",
|
"id": "587d8248367417b2b2512c3d",
|
||||||
"title": "Disable DNS Prefetching with helmet.dnsPrefetchControl()",
|
"title": "Disable DNS Prefetching with helmet.dnsPrefetchControl()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"To improve performance, most browsers prefetch DNS records for the links in a page. In that way the destination ip is already known when the user clicks on a link. This may lead to over-use of the DNS service (if you own a big website, visited by millions people…), privacy issues (one eavesdropper could infer that you are on a certain page), or page statistics alteration (some links may appear visited even if they are not). If you have high security needs you can disable DNS prefetching, at the cost of a performance penalty."
|
"To improve performance, most browsers prefetch DNS records for the links in a page. In that way the destination ip is already known when the user clicks on a link. This may lead to over-use of the DNS service (if you own a big website, visited by millions people…), privacy issues (one eavesdropper could infer that you are on a certain page), or page statistics alteration (some links may appear visited even if they are not). If you have high security needs you can disable DNS prefetching, at the cost of a performance penalty."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -210,7 +210,7 @@
|
|||||||
"id": "587d8249367417b2b2512c3e",
|
"id": "587d8249367417b2b2512c3e",
|
||||||
"title": "Disable Client-Side Caching with helmet.noCache()",
|
"title": "Disable Client-Side Caching with helmet.noCache()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"If you are releasing an update for your website, and you want the users to always download the newer version, you can (try to) disable caching on client’s browser. It can be useful in development too. Caching has performance benefits, which you will lose, so only use this option when there is a real need."
|
"If you are releasing an update for your website, and you want the users to always download the newer version, you can (try to) disable caching on client’s browser. It can be useful in development too. Caching has performance benefits, which you will lose, so only use this option when there is a real need."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -231,7 +231,7 @@
|
|||||||
"id": "587d8249367417b2b2512c3f",
|
"id": "587d8249367417b2b2512c3f",
|
||||||
"title": "Set a Content Security Policy with helmet.contentSecurityPolicy()",
|
"title": "Set a Content Security Policy with helmet.contentSecurityPolicy()",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"This challenge highlights one promising new defense that can significantly reduce the risk and impact of many type of attacks in modern browsers. By setting and configuring a Content Security Policy you can prevent the injection of anything unintended into your page. This will protect your app from XSS vulnerabilities, undesired tracking, malicious frames, and much more. CSP works by defining a whitelist of content sources which are trusted. You can configure them for each kind of resource a web page may need (scripts, stylesheets, fonts, frames, media, and so on…). There are multiple directives available, so a website owner can have a granular control. See HTML 5 Rocks, KeyCDN for more details. Unfortunately CSP in unsupported by older browser.",
|
"This challenge highlights one promising new defense that can significantly reduce the risk and impact of many type of attacks in modern browsers. By setting and configuring a Content Security Policy you can prevent the injection of anything unintended into your page. This will protect your app from XSS vulnerabilities, undesired tracking, malicious frames, and much more. CSP works by defining a whitelist of content sources which are trusted. You can configure them for each kind of resource a web page may need (scripts, stylesheets, fonts, frames, media, and so on…). There are multiple directives available, so a website owner can have a granular control. See HTML 5 Rocks, KeyCDN for more details. Unfortunately CSP in unsupported by older browser.",
|
||||||
"By default, directives are wide open, so it’s important to set the defaultSrc directive as a fallback. Helmet supports both defaultSrc and default-src naming styles. The fallback applies for most of the unspecified directives. In this exercise, use helmet.contentSecurityPolicy(), and configure it setting the defaultSrc directive to [\"self\"] (the list of allowed sources must be in an array), in order to trust only your website address by default. Set also the scriptSrc directive so that you will allow scripts to be downloaded from your website, and from the domain 'trusted-cdn.com'.",
|
"By default, directives are wide open, so it’s important to set the defaultSrc directive as a fallback. Helmet supports both defaultSrc and default-src naming styles. The fallback applies for most of the unspecified directives. In this exercise, use helmet.contentSecurityPolicy(), and configure it setting the defaultSrc directive to [\"self\"] (the list of allowed sources must be in an array), in order to trust only your website address by default. Set also the scriptSrc directive so that you will allow scripts to be downloaded from your website, and from the domain 'trusted-cdn.com'.",
|
||||||
"Hint: in the \"'self'\" keyword, the single quotes are part of the keyword itself, so it needs to be enclosed in double quotes to be working."
|
"Hint: in the \"'self'\" keyword, the single quotes are part of the keyword itself, so it needs to be enclosed in double quotes to be working."
|
||||||
@ -258,7 +258,7 @@
|
|||||||
"id": "587d8249367417b2b2512c40",
|
"id": "587d8249367417b2b2512c40",
|
||||||
"title": "Configure Helmet Using the ‘parent’ helmet() Middleware",
|
"title": "Configure Helmet Using the ‘parent’ helmet() Middleware",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a>.",
|
||||||
"app.use(helmet()) will automatically include all the middleware introduced above, except noCache(), and contentSecurityPolicy(), but these can be enabled if necessary. You can also disable or configure any other middleware individually, using a configuration object.",
|
"app.use(helmet()) will automatically include all the middleware introduced above, except noCache(), and contentSecurityPolicy(), but these can be enabled if necessary. You can also disable or configure any other middleware individually, using a configuration object.",
|
||||||
"// Example",
|
"// Example",
|
||||||
"<code>app.use(helmet({</code>",
|
"<code>app.use(helmet({</code>",
|
||||||
@ -296,7 +296,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"The safest way to protect a password is to never even store it- even encrypted. The solution to this security problem is hashing. Unlike encryptions, hashes cannot be transformed back into the original data. So how do you use a hash? A hash is used to verify data like a password again at a later point in time without actually knowing what it is in the future by hashing the entered password in the same manner as the original and comparing the results; if they match then you can be sure it is the same data. A widely used library for this is <em>BCrypt</em>. With how easy BCrypt is to implement into your web applications, you should never have any excuse to store a plain text password in your databases.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe.",
|
"The safest way to protect a password is to never even store it- even encrypted. The solution to this security problem is hashing. Unlike encryptions, hashes cannot be transformed back into the original data. So how do you use a hash? A hash is used to verify data like a password again at a later point in time without actually knowing what it is in the future by hashing the entered password in the same manner as the original and comparing the results; if they match then you can be sure it is the same data. A widely used library for this is <em>BCrypt</em>. With how easy BCrypt is to implement into your web applications, you should never have any excuse to store a plain text password in your databases.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe.",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -323,7 +323,7 @@
|
|||||||
"id": "58a25bcef9fc0f352b528e7c",
|
"id": "58a25bcef9fc0f352b528e7c",
|
||||||
"title": "Understand BCrypt Hashes",
|
"title": "Understand BCrypt Hashes",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>GitHub</a>.",
|
||||||
"BCrypt hashes are very secure. A hash is basically a fingerprint of the original data- always unique. This is accomplished by feeding the original data into a algorithm and having returned a fixed length result. To further complicate this process and make it more secure, you can also <em>salt</em> your hash. Salting your hash involves adding random data to the original data before the hashing process which makes it even harder to crack the hash.",
|
"BCrypt hashes are very secure. A hash is basically a fingerprint of the original data- always unique. This is accomplished by feeding the original data into a algorithm and having returned a fixed length result. To further complicate this process and make it more secure, you can also <em>salt</em> your hash. Salting your hash involves adding random data to the original data before the hashing process which makes it even harder to crack the hash.",
|
||||||
"BCrypt hashes will always looks like <code>$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code> which does have a structure. The first small bit of data <code>$2a</code> is defining what kind of hash algorithm was used. The next portion <code>$13</code> defines the <em>cost</em>. Cost is about how much power it takes to compute the hash. It is on a logarithmic scale of 2^cost and determines how many times the data is put through the hashing algorithm. For example, at a cost of 10 you are able to hash 10 passwords a second on an average computer, however at a cost of 15 it takes 3 seconds per hash... and to take it further, at a cost of 31 it would takes multiple days to complete a hash. A cost of 12 is considered very secure at this time. The last portion of your hash <code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>, looks like 1 large string of numbers, periods, and letters but it is actually 2 seperate peices of infomation. The first 22 characters is the salt in plain text, and the rest is the hashed password!",
|
"BCrypt hashes will always looks like <code>$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code> which does have a structure. The first small bit of data <code>$2a</code> is defining what kind of hash algorithm was used. The next portion <code>$13</code> defines the <em>cost</em>. Cost is about how much power it takes to compute the hash. It is on a logarithmic scale of 2^cost and determines how many times the data is put through the hashing algorithm. For example, at a cost of 10 you are able to hash 10 passwords a second on an average computer, however at a cost of 15 it takes 3 seconds per hash... and to take it further, at a cost of 31 it would takes multiple days to complete a hash. A cost of 12 is considered very secure at this time. The last portion of your hash <code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>, looks like 1 large string of numbers, periods, and letters but it is actually 2 seperate peices of infomation. The first 22 characters is the salt in plain text, and the rest is the hashed password!",
|
||||||
"<hr>To begin using BCrypt, add it as a dependency in your project and require it as 'bcrypt' in your server.",
|
"<hr>To begin using BCrypt, add it as a dependency in your project and require it as 'bcrypt' in your server.",
|
||||||
@ -351,7 +351,7 @@
|
|||||||
"id": "58a25bcff9fc0f352b528e7d",
|
"id": "58a25bcff9fc0f352b528e7d",
|
||||||
"title": "Hash and Compare Passwords Asynchronously",
|
"title": "Hash and Compare Passwords Asynchronously",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>GitHub</a>.",
|
||||||
"As hashing is designed to be computationally intensive, it is recommended to do so asyncronously on your server as to avoid blocking incoming connections while you hash. All you have to do to hash a password asynchronous is call <code>bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => { /*Store hash in your db*/ });</code>",
|
"As hashing is designed to be computationally intensive, it is recommended to do so asyncronously on your server as to avoid blocking incoming connections while you hash. All you have to do to hash a password asynchronous is call <code>bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => { /*Store hash in your db*/ });</code>",
|
||||||
"<hr>Add this hashing function to your server(we've already defined the variables used in the function for you to use) and log it to the console for you to see! At this point you would normally save the hash to your database.",
|
"<hr>Add this hashing function to your server(we've already defined the variables used in the function for you to use) and log it to the console for you to see! At this point you would normally save the hash to your database.",
|
||||||
"Now when you need to figure out if a new input is the same data as the hash you would just use the compare function <code>bcrypt.compare(myPlaintextPassword, hash, (err, res) => { /*res == true or false*/ });</code>. Add this into your existing hash function(since you need to wait for the hash to complete before calling the compare function) after you log the completed hash and log 'res' to the console within the compare. You should see in the console a hash then 'true' is printed! If you change 'myPlaintextPassword' in the compare function to 'someOtherPlaintextPassword' then it should say false.",
|
"Now when you need to figure out if a new input is the same data as the hash you would just use the compare function <code>bcrypt.compare(myPlaintextPassword, hash, (err, res) => { /*res == true or false*/ });</code>. Add this into your existing hash function(since you need to wait for the hash to complete before calling the compare function) after you log the completed hash and log 'res' to the console within the compare. You should see in the console a hash then 'true' is printed! If you change 'myPlaintextPassword' in the compare function to 'someOtherPlaintextPassword' then it should say false.",
|
||||||
@ -376,7 +376,7 @@
|
|||||||
"id": "58a25bcff9fc0f352b528e7e",
|
"id": "58a25bcff9fc0f352b528e7e",
|
||||||
"title": "Hash and Compare Passwords Synchronously",
|
"title": "Hash and Compare Passwords Synchronously",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'>GitHub</a>.",
|
||||||
"Hashing synchronously is just as easy to do but can cause lag if using it server side with a high cost or with hashing done very often. Hashing with this method is as easy as calling <code>var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);</code>",
|
"Hashing synchronously is just as easy to do but can cause lag if using it server side with a high cost or with hashing done very often. Hashing with this method is as easy as calling <code>var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);</code>",
|
||||||
"<hr>Add this method of hashing to your code and then log the result to the console. Again, the variables used are already defined in the server so you wont need to adjust them. You may notice even though you are hashing the same password as in the async function, the result in the console is different- this is due to the salt being randomly generated each time as seen by the first 22 characters in the third string of the hash.",
|
"<hr>Add this method of hashing to your code and then log the result to the console. Again, the variables used are already defined in the server so you wont need to adjust them. You may notice even though you are hashing the same password as in the async function, the result in the console is different- this is due to the salt being randomly generated each time as seen by the first 22 characters in the third string of the hash.",
|
||||||
"Now to compare a password input with the new sync hash, you would use the compareSync method: <code>var result = bcrypt.compareSync(myPlaintextPassword, hash);</code> with the result being a boolean true or false. Add this function in and log to the console the result to see it working.",
|
"Now to compare a password input with the new sync hash, you would use the compareSync method: <code>var result = bcrypt.compareSync(myPlaintextPassword, hash);</code> with the result being a boolean true or false. Add this function in and log to the console the result to see it working.",
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
"id": "587d8249367417b2b2512c41",
|
"id": "587d8249367417b2b2512c41",
|
||||||
"title": "Metric-Imperial Converter",
|
"title": "Metric-Imperial Converter",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://hard-twilight.gomix.me/' target='_blank'>https://hard-twilight.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://hard-twilight.glitch.me/' target='_blank'>https://hard-twilight.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-metricimpconverter/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-metricimpconverter/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-metricimpconverter/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-metricimpconverter/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -79,9 +79,9 @@
|
|||||||
"id": "587d8249367417b2b2512c42",
|
"id": "587d8249367417b2b2512c42",
|
||||||
"title": "Issue Tracker",
|
"title": "Issue Tracker",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://protective-garage.gomix.me/' target='_blank'>https://protective-garage.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://protective-garage.glitch.me/' target='_blank'>https://protective-garage.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-issuetracker/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-issuetracker/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-issuetracker/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-issuetracker/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -130,9 +130,9 @@
|
|||||||
"id": "587d824a367417b2b2512c43",
|
"id": "587d824a367417b2b2512c43",
|
||||||
"title": "Personal Library",
|
"title": "Personal Library",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://spark-cathedral.gomix.me/' target='_blank'>https://spark-cathedral.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://spark-cathedral.glitch.me/' target='_blank'>https://spark-cathedral.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-library/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-library/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-library/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-library/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -189,9 +189,9 @@
|
|||||||
"id": "587d824a367417b2b2512c44",
|
"id": "587d824a367417b2b2512c44",
|
||||||
"title": "Stock Price Checker",
|
"title": "Stock Price Checker",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://giant-chronometer.gomix.me/' target='_blank'>https://giant-chronometer.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://giant-chronometer.glitch.me/' target='_blank'>https://giant-chronometer.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-stockchecker/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-stockchecker/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-stockchecker/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-stockchecker/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -236,9 +236,9 @@
|
|||||||
"id": "587d824a367417b2b2512c45",
|
"id": "587d824a367417b2b2512c45",
|
||||||
"title": "Anonymous Message Board",
|
"title": "Anonymous Message Board",
|
||||||
"description": [
|
"description": [
|
||||||
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://horn-celery.gomix.me/' target='_blank'>https://horn-celery.gomix.me/</a>.",
|
"Build a full stack JavaScript app that is functionally similar to this: <a href='https://horn-celery.glitch.me/' target='_blank'>https://horn-celery.glitch.me/</a>.",
|
||||||
"Working on this project will involve you writing your code on Gomix on our starter project. After completing this project you can copy your public gomix url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
"Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.",
|
||||||
"Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-project-messageboard/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-messageboard/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!"
|
"Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-messageboard/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-messageboard/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"Working on these challenges will involve you writing your code on Gomix on our starter project. After completing each challenge you can copy your public gomix url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Gomix using <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>this repository</a> on GitHub! If you use Gomix, remember to save the link to your project somewhere safe!",
|
"Working on these challenges will involve you writing your code on Glitch on our starter project. After completing each challenge you can copy your public glitch url (to the homepage of your app) into the challenge screen to test it! Optionally you may choose to write your project on another platform but it must be publicaly visible for our testing.<br>Start this project on Glitch using <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!",
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"id": "587d824a367417b2b2512c46",
|
"id": "587d824a367417b2b2512c46",
|
||||||
"title": "Learn How JavaScript Assertions Work",
|
"title": "Learn How JavaScript Assertions Work",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"Use assert.isNull() or assert.isNotNull() to make the tests pass."
|
"Use assert.isNull() or assert.isNotNull() to make the tests pass."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -56,7 +56,7 @@
|
|||||||
"id": "587d824b367417b2b2512c47",
|
"id": "587d824b367417b2b2512c47",
|
||||||
"title": "Test if a Variable or Function is Defined",
|
"title": "Test if a Variable or Function is Defined",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"Use assert.isDefined() or assert.isUndefined() to make the tests pass"
|
"Use assert.isDefined() or assert.isUndefined() to make the tests pass"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -89,7 +89,7 @@
|
|||||||
"id": "587d824b367417b2b2512c48",
|
"id": "587d824b367417b2b2512c48",
|
||||||
"title": "Use Assert.isOK and Assert.isNotOK",
|
"title": "Use Assert.isOK and Assert.isNotOK",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"Use assert.isOk() or assert.isNotOk() to make the tests pass.",
|
"Use assert.isOk() or assert.isNotOk() to make the tests pass.",
|
||||||
".isOk(truthy) and .isNotOk(falsey) will pass."
|
".isOk(truthy) and .isNotOk(falsey) will pass."
|
||||||
],
|
],
|
||||||
@ -123,7 +123,7 @@
|
|||||||
"id": "587d824b367417b2b2512c49",
|
"id": "587d824b367417b2b2512c49",
|
||||||
"title": "Test for Truthiness",
|
"title": "Test for Truthiness",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"Use assert.isTrue() or assert.isNotTrue() to make the tests pass.",
|
"Use assert.isTrue() or assert.isNotTrue() to make the tests pass.",
|
||||||
".isTrue(true) and .isNotTrue(everything else) will pass.",
|
".isTrue(true) and .isNotTrue(everything else) will pass.",
|
||||||
".isFalse() and .isNotFalse() also exist."
|
".isFalse() and .isNotFalse() also exist."
|
||||||
@ -158,7 +158,7 @@
|
|||||||
"id": "587d824b367417b2b2512c4a",
|
"id": "587d824b367417b2b2512c4a",
|
||||||
"title": "Use the Double Equals to Assert Equality",
|
"title": "Use the Double Equals to Assert Equality",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
".equal(), .notEqual()",
|
".equal(), .notEqual()",
|
||||||
".equal() compares objects using '=='"
|
".equal() compares objects using '=='"
|
||||||
],
|
],
|
||||||
@ -196,7 +196,7 @@
|
|||||||
"id": "587d824b367417b2b2512c4b",
|
"id": "587d824b367417b2b2512c4b",
|
||||||
"title": "Use the Triple Equals to Assert Strict Equality",
|
"title": "Use the Triple Equals to Assert Strict Equality",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
".strictEqual(), .notStrictEqual()",
|
".strictEqual(), .notStrictEqual()",
|
||||||
".strictEqual() compares objects using '==='"
|
".strictEqual() compares objects using '==='"
|
||||||
],
|
],
|
||||||
@ -234,7 +234,7 @@
|
|||||||
"id": "587d824c367417b2b2512c4c",
|
"id": "587d824c367417b2b2512c4c",
|
||||||
"title": "Assert Deep Equality with .deepEqual and .notDeepEqual",
|
"title": "Assert Deep Equality with .deepEqual and .notDeepEqual",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
".deepEqual(), .notDeepEqual()",
|
".deepEqual(), .notDeepEqual()",
|
||||||
".deepEqual() asserts that two object are deep equal"
|
".deepEqual() asserts that two object are deep equal"
|
||||||
],
|
],
|
||||||
@ -264,7 +264,7 @@
|
|||||||
"id": "587d824c367417b2b2512c4d",
|
"id": "587d824c367417b2b2512c4d",
|
||||||
"title": "Compare the Properties of Two Elements",
|
"title": "Compare the Properties of Two Elements",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
".isAbove() => a > b , .isAtMost() => a <= b"
|
".isAbove() => a > b , .isAtMost() => a <= b"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -301,7 +301,7 @@
|
|||||||
"id": "587d824c367417b2b2512c4e",
|
"id": "587d824c367417b2b2512c4e",
|
||||||
"title": "Test if One Value is Below or At Least as Large as Another",
|
"title": "Test if One Value is Below or At Least as Large as Another",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
".isBelow() => a < b , .isAtLeast => a >= b"
|
".isBelow() => a < b , .isAtLeast => a >= b"
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -338,7 +338,7 @@
|
|||||||
"id": "587d824c367417b2b2512c4f",
|
"id": "587d824c367417b2b2512c4f",
|
||||||
"title": "Test if a Value Falls within a Specific Range",
|
"title": "Test if a Value Falls within a Specific Range",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
".approximately",
|
".approximately",
|
||||||
".approximately(actual, expected, range, [message])",
|
".approximately(actual, expected, range, [message])",
|
||||||
"actual = expected +/- range",
|
"actual = expected +/- range",
|
||||||
@ -371,7 +371,7 @@
|
|||||||
"id": "587d824d367417b2b2512c50",
|
"id": "587d824d367417b2b2512c50",
|
||||||
"title": "Test if a Value is an Array",
|
"title": "Test if a Value is an Array",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>."
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -399,7 +399,7 @@
|
|||||||
"id": "587d824d367417b2b2512c51",
|
"id": "587d824d367417b2b2512c51",
|
||||||
"title": "Test if an Array Contains an Item",
|
"title": "Test if an Array Contains an Item",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>."
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -427,7 +427,7 @@
|
|||||||
"id": "587d824d367417b2b2512c52",
|
"id": "587d824d367417b2b2512c52",
|
||||||
"title": "Test if a Value is a String",
|
"title": "Test if a Value is a String",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"#isString asserts that the actual value is a string."
|
"#isString asserts that the actual value is a string."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -460,7 +460,7 @@
|
|||||||
"id": "587d824d367417b2b2512c53",
|
"id": "587d824d367417b2b2512c53",
|
||||||
"title": "Test if a String Contains a Substring",
|
"title": "Test if a String Contains a Substring",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"#include (on #notInclude ) works for strings too !!",
|
"#include (on #notInclude ) works for strings too !!",
|
||||||
"It asserts that the actual string contains the expected substring"
|
"It asserts that the actual string contains the expected substring"
|
||||||
],
|
],
|
||||||
@ -490,7 +490,7 @@
|
|||||||
"id": "587d824d367417b2b2512c54",
|
"id": "587d824d367417b2b2512c54",
|
||||||
"title": "Use Regular Expressions to Test a String",
|
"title": "Use Regular Expressions to Test a String",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"#match Asserts that the actual value",
|
"#match Asserts that the actual value",
|
||||||
"matches the second argument regular expression."
|
"matches the second argument regular expression."
|
||||||
],
|
],
|
||||||
@ -520,7 +520,7 @@
|
|||||||
"id": "587d824e367417b2b2512c55",
|
"id": "587d824e367417b2b2512c55",
|
||||||
"title": "Test if an Object has a Property",
|
"title": "Test if an Object has a Property",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"#property asserts that the actual object has a given property.",
|
"#property asserts that the actual object has a given property.",
|
||||||
"Use #property or #notProperty where appropriate"
|
"Use #property or #notProperty where appropriate"
|
||||||
],
|
],
|
||||||
@ -554,7 +554,7 @@
|
|||||||
"id": "587d824e367417b2b2512c56",
|
"id": "587d824e367417b2b2512c56",
|
||||||
"title": "Test if a Value is of a Specific Data Structure Type",
|
"title": "Test if a Value is of a Specific Data Structure Type",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"#typeOf asserts that value’s type is the given string, as determined by Object.prototype.toString.",
|
"#typeOf asserts that value’s type is the given string, as determined by Object.prototype.toString.",
|
||||||
"Use #typeOf or #notTypeOf where appropriate"
|
"Use #typeOf or #notTypeOf where appropriate"
|
||||||
],
|
],
|
||||||
@ -596,7 +596,7 @@
|
|||||||
"id": "587d824e367417b2b2512c57",
|
"id": "587d824e367417b2b2512c57",
|
||||||
"title": "Test if an Object is an Instance of a Constructor",
|
"title": "Test if an Object is an Instance of a Constructor",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"#instanceOf asserts that an object is an instance of a constructor.",
|
"#instanceOf asserts that an object is an instance of a constructor.",
|
||||||
"Use #instanceOf or #notInstanceOf where appropriate"
|
"Use #instanceOf or #notInstanceOf where appropriate"
|
||||||
],
|
],
|
||||||
@ -634,7 +634,7 @@
|
|||||||
"id": "587d824e367417b2b2512c58",
|
"id": "587d824e367417b2b2512c58",
|
||||||
"title": "Run Functional Tests on API Endpoints using Chai-HTTP",
|
"title": "Run Functional Tests on API Endpoints using Chai-HTTP",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"Replace assert.fail(). Test the status and the text.response. Make the test pass.",
|
"Replace assert.fail(). Test the status and the text.response. Make the test pass.",
|
||||||
"Don't send a name in the query, the endpoint with responds with 'hello Guest'."
|
"Don't send a name in the query, the endpoint with responds with 'hello Guest'."
|
||||||
],
|
],
|
||||||
@ -664,7 +664,7 @@
|
|||||||
"id": "587d824f367417b2b2512c59",
|
"id": "587d824f367417b2b2512c59",
|
||||||
"title": "Run Functional Tests on API Endpoints using Chai-HTTP II",
|
"title": "Run Functional Tests on API Endpoints using Chai-HTTP II",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"Replace assert.fail(). Test the status and the text.response. Make the test pass.",
|
"Replace assert.fail(). Test the status and the text.response. Make the test pass.",
|
||||||
"Send you name in the query appending ?name=<your_name>, the endpoint with responds with 'hello <your_name>'."
|
"Send you name in the query appending ?name=<your_name>, the endpoint with responds with 'hello <your_name>'."
|
||||||
],
|
],
|
||||||
@ -694,7 +694,7 @@
|
|||||||
"id": "587d824f367417b2b2512c5a",
|
"id": "587d824f367417b2b2512c5a",
|
||||||
"title": "Run Functional Tests on an API Response using Chai-HTTP III - PUT method",
|
"title": "Run Functional Tests on an API Response using Chai-HTTP III - PUT method",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"In the next example we'll see how to send data in a request payload (body).",
|
"In the next example we'll see how to send data in a request payload (body).",
|
||||||
"We are going to test a PUT request. The '/travellers' endpoint accepts",
|
"We are going to test a PUT request. The '/travellers' endpoint accepts",
|
||||||
"a JSON object taking the structure :",
|
"a JSON object taking the structure :",
|
||||||
@ -740,7 +740,7 @@
|
|||||||
"id": "587d824f367417b2b2512c5b",
|
"id": "587d824f367417b2b2512c5b",
|
||||||
"title": "Run Functional Tests on an API Response using Chai-HTTP IV - PUT method redux",
|
"title": "Run Functional Tests on an API Response using Chai-HTTP IV - PUT method redux",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"This exercise is similar to the preceding. Look at it for the details.",
|
"This exercise is similar to the preceding. Look at it for the details.",
|
||||||
"Send {surname: 'da Verrazzano'}. Replace assert.fail() and make the test pass.",
|
"Send {surname: 'da Verrazzano'}. Replace assert.fail() and make the test pass.",
|
||||||
"Check for 1) status, 2) type, 3) body.name, 4) body.surname",
|
"Check for 1) status, 2) type, 3) body.name, 4) body.surname",
|
||||||
@ -780,10 +780,10 @@
|
|||||||
"id": "587d824f367417b2b2512c5c",
|
"id": "587d824f367417b2b2512c5c",
|
||||||
"title": "Run Functional Tests using a Headless Browser",
|
"title": "Run Functional Tests using a Headless Browser",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"In the next challenges we are going to simulate the human interaction with a page using a device called 'Headless Browser'.",
|
"In the next challenges we are going to simulate the human interaction with a page using a device called 'Headless Browser'.",
|
||||||
"A headless browser is a web browser without a graphical user interface. These kind of tools are particularly useful for testing web pages as they are able to render and understand HTML, CSS, and JavaScript the same way a browser would.",
|
"A headless browser is a web browser without a graphical user interface. These kind of tools are particularly useful for testing web pages as they are able to render and understand HTML, CSS, and JavaScript the same way a browser would.",
|
||||||
"For these challenges we are using Zombie.JS. It's a lightweight browser which is totally based on JS, without relying on additional binaries to be installed. This feature makes it usable in an environment such as Gomix. There are many other (more powerful) options.<br>",
|
"For these challenges we are using Zombie.JS. It's a lightweight browser which is totally based on JS, without relying on additional binaries to be installed. This feature makes it usable in an environment such as Glitch. There are many other (more powerful) options.<br>",
|
||||||
"Look at the examples in the code for the exercise directions Follow the assertions order, We rely on it."
|
"Look at the examples in the code for the exercise directions Follow the assertions order, We rely on it."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
@ -820,7 +820,7 @@
|
|||||||
"id": "587d8250367417b2b2512c5d",
|
"id": "587d8250367417b2b2512c5d",
|
||||||
"title": "Run Functional Tests using a Headless Browser II",
|
"title": "Run Functional Tests using a Headless Browser II",
|
||||||
"description": [
|
"description": [
|
||||||
"As a reminder, this project is being built upon the following starter project on <a href='https://gomix.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Gomix</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
"As a reminder, this project is being built upon the following starter project on <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/'>Glitch</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.",
|
||||||
"This exercise is similar to the preceding.",
|
"This exercise is similar to the preceding.",
|
||||||
"Look at the code for directions. Follow the assertions order, We rely on it."
|
"Look at the code for directions. Follow the assertions order, We rely on it."
|
||||||
],
|
],
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
" return true;",
|
" return true;",
|
||||||
"}",
|
"}",
|
||||||
"",
|
"",
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"telephoneCheck(\"555-555-5555\");"
|
"telephoneCheck(\"555-555-5555\");"
|
||||||
],
|
],
|
||||||
"solutions": [
|
"solutions": [
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
"<blockquote>i8View[0] = 42;<br>console.log(i8View); // Returns [42, 0, 0]</blockquote>",
|
"<blockquote>i8View[0] = 42;<br>console.log(i8View); // Returns [42, 0, 0]</blockquote>",
|
||||||
"<strong>Note</strong><br>Typed arrays do not have some of the methods traditional arrays have such as <code>.pop()</code> or <code>.push()</code>. Typed arrays also fail <code>Array.isArray()</code> that checks if something is an array. Although simpler, this can be an advantage for less-sophisticated JavaScript engines to implement them.",
|
"<strong>Note</strong><br>Typed arrays do not have some of the methods traditional arrays have such as <code>.pop()</code> or <code>.push()</code>. Typed arrays also fail <code>Array.isArray()</code> that checks if something is an array. Although simpler, this can be an advantage for less-sophisticated JavaScript engines to implement them.",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"First create a <code>buffer</code> that is 64-bytes. Then create a <code>Int32Array</code> typed array with a view of it called <code>int32View</code>."
|
"First create a <code>buffer</code> that is 64-bytes. Then create a <code>Int32Array</code> typed array with a view of it called <code>i32View</code>."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"var buffer;",
|
"var buffer;",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"This data storage scheme is called a <dfn>Stack</dfn>. In particular, we would have to implement the <code>push()</code> method that pushes JavaScript objects at the top of the stack; and <code>pop()</code> method, that removes the JavaScript object that's at the top of the stack at the current moment.",
|
"This data storage scheme is called a <dfn>Stack</dfn>. In particular, we would have to implement the <code>push()</code> method that pushes JavaScript objects at the top of the stack; and <code>pop()</code> method, that removes the JavaScript object that's at the top of the stack at the current moment.",
|
||||||
"<hr>",
|
"<hr>",
|
||||||
"Here we have a stack of homework assignments represented as an array: <code>\"BIO12\"</code> is at the base, and <code>\"PSY44\"</code> is at the top of the stack.",
|
"Here we have a stack of homework assignments represented as an array: <code>\"BIO12\"</code> is at the base, and <code>\"PSY44\"</code> is at the top of the stack.",
|
||||||
"Modify the given array and treat it like a <code>stack</code> using the JavaScript methods mentioned above. Remove the top element <code>\"BIO12\"</code> from the stack. Then add <code>\"CS50\"</code> to be the new top element of the stack."
|
"Modify the given array and treat it like a <code>stack</code> using the JavaScript methods mentioned above. Remove the top element <code>\"PSY44\"</code> from the stack. Then add <code>\"CS50\"</code> to be the new top element of the stack."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"var homeworkStack = [\"BIO12\",\"HIS80\",\"MAT122\",\"PSY44\"];",
|
"var homeworkStack = [\"BIO12\",\"HIS80\",\"MAT122\",\"PSY44\"];",
|
||||||
@ -161,7 +161,7 @@
|
|||||||
"<code>[[’kitten’, 2], [‘dog’, 2], [‘rabbit’, 2]]</code>",
|
"<code>[[’kitten’, 2], [‘dog’, 2], [‘rabbit’, 2]]</code>",
|
||||||
"Here the second value (an integer) represents item priority. If we enqueue <code>[‘human’, 1]</code> with a priority of <code>1</code> (assuming lower priorities are given precedence) it would then be the first item to be dequeued. The collection would like this:",
|
"Here the second value (an integer) represents item priority. If we enqueue <code>[‘human’, 1]</code> with a priority of <code>1</code> (assuming lower priorities are given precedence) it would then be the first item to be dequeued. The collection would like this:",
|
||||||
"<code>[[‘human’, 1], [’kitten’, 2], [‘dog’, 2], [‘rabbit’, 2]]</code>.",
|
"<code>[[‘human’, 1], [’kitten’, 2], [‘dog’, 2], [‘rabbit’, 2]]</code>.",
|
||||||
"We’ve starting writing a <code>PriorityQueue</code> in the code editor. You will need to add an <code>enqueue</code> method for adding items with a priority, a <code>dequeue</code> method for removing items, a <code>size</code> method to return the number of items in the queue, and finally an <code>isEmpty</code> method that will return <code>true</code> or <code>false</code> if the queue is empty.",
|
"We’ve started writing a <code>PriorityQueue</code> in the code editor. You will need to add an <code>enqueue</code> method for adding items with a priority, a <code>dequeue</code> method for removing items, a <code>size</code> method to return the number of items in the queue, a <code>front</code> method to return the element at the front of the queue, and finally an <code>isEmpty</code> method that will return <code>true</code> if the queue is empty or <code>false</code> if it is not.",
|
||||||
"The <code>enqueue</code> should accept items with the format shown above (<code>['human', 1]</code>) where <code>1</code> represents the priority. The <code>dequeue</code> should return only the current item, not its priority."
|
"The <code>enqueue</code> should accept items with the format shown above (<code>['human', 1]</code>) where <code>1</code> represents the priority. The <code>dequeue</code> should return only the current item, not its priority."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
@ -187,7 +187,7 @@
|
|||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
"releasedOn": "Feb 17, 2017",
|
"releasedOn": "Feb 17, 2017",
|
||||||
"solutions": [
|
"solutions": [
|
||||||
"function PriorityQueue () { \n this.collection = []; \n this.printCollection = function(){ \n console.log(this.collection); \n }; \n this.size = function() { \n return this.collection.length; \n }; \n this.isEmpty = function() { \n return this.size() > 0 ? false : true; \n }; \n this.enqueue = function(newItem) { \n if (this.isEmpty()) { \n this.collection.push(newItem) \n } else { \n let inserted = false; \n var updated = this.collection.reduce((newCollection, item, index, current) => { \n if (newItem[1] < item[1] && !inserted) { \n newCollection.push(newItem); \n inserted = true; \n } \n newCollection.push(item); \n if (newItem[1] === item[1] && index < current.length - 1 && newItem[1] !== current[index + 1][1] && !inserted) { \n newCollection.push(newItem); \n inserted = true; \n } \n return newCollection; \n }, []); \n if (!inserted) { \n updated.push(newItem); \n } \n this.collection = updated; \n }; \n }; \n this.dequeue = function() { \n if (!this.isEmpty()) { \n return this.collection.shift()[0]; \n } else { \n return 'The queue is empty.' \n } \n }; \n }"
|
"function PriorityQueue () { \n this.collection = []; \n this.printCollection = function(){ \n console.log(this.collection); \n }; \n this.size = function() { \n return this.collection.length; \n }; \n this.isEmpty = function() { \n return this.size() > 0 ? false : true; \n }; \n this.enqueue = function (newitem) {\n if (this.isEmpty()) {\n return this.collection.push(newitem);\n }\n\n this.collection = this.collection.reverse();\n var found_index = this.collection.findIndex(function (item) {\n return newitem[1] >= item[1];\n });\n if (found_index === -1) {\n this.collection.push(newitem);\n } else {\n this.collection.splice(found_index, 0, newitem);\n }\n this.collection = this.collection.reverse();\n}; \n this.dequeue = function() { \n if (!this.isEmpty()) { \n return this.collection.shift()[0]; \n } else { \n return 'The queue is empty.' \n } \n }; \n }"
|
||||||
],
|
],
|
||||||
"challengeType": 1,
|
"challengeType": 1,
|
||||||
"translations": {}
|
"translations": {}
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
"<strong>User Story:</strong> I can see the weather in my current location.",
|
"<strong>User Story:</strong> I can see the weather in my current location.",
|
||||||
"<strong>User Story:</strong> I can see a different icon or background image (e.g. snowy mountain, hot desert) depending on the weather.",
|
"<strong>User Story:</strong> I can see a different icon or background image (e.g. snowy mountain, hot desert) depending on the weather.",
|
||||||
"<strong>User Story:</strong> I can push a button to toggle between Fahrenheit and Celsius.",
|
"<strong>User Story:</strong> I can push a button to toggle between Fahrenheit and Celsius.",
|
||||||
"We recommend using the <a href='https://openweathermap.org/current#geo' target='_blank'>Open Weather API</a>. This will require creating a free API key. Normally you want to avoid exposing API keys on CodePen, but we haven't been able to find a keyless API for weather.",
|
"<strong>Note:</strong> Many internet browsers now require an HTTP Secure (<code>https://</code>) connection to obtain a user's locale via HTML5 Geolocation. For this reason, you will need to choose one of two paths to complete this project:<br/><br/>Use HTML5 Geolocation to get user location and then use the <a href='https://darksky.net/dev/docs/forecast' target='_blank'>Dark Sky API</a> which uses an HTTP Secure connection for the weather. Also, be sure to connect to <a href='https://codepen.io' target='_blank'>CodePen.io</a> via <code>https://</code>.<br/><br/>Or:<br/><br/>Use an Internet Protocol API to get user location (ex. <a href='http://ip-api.com/docs/api:json'>IP-API</a>) and then use the <a href='http://openweathermap.org/current#geo' target='_blank'>OpenWeatherMap API</a> for the weather. Be sure to connect to <a href='http://codepen.io' target='_blank'>CodePen.io</a> via <code>http://</code>.",
|
||||||
|
"Either weather API service will require creating a free API key. Normally you want to avoid exposing API keys on CodePen, but we haven't been able to find a keyless API for weather.",
|
||||||
"Remember to use <a href='http://forum.freeCodeCamp.com/t/how-to-get-help-when-you-are-stuck/19514' target='_blank'>Read-Search-Ask</a> if you get stuck.",
|
"Remember to use <a href='http://forum.freeCodeCamp.com/t/how-to-get-help-when-you-are-stuck/19514' target='_blank'>Read-Search-Ask</a> if you get stuck.",
|
||||||
"When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen.",
|
"When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen.",
|
||||||
"You can get feedback on your project by sharing it with your friends on Facebook."
|
"You can get feedback on your project by sharing it with your friends on Facebook."
|
||||||
@ -500,7 +501,7 @@
|
|||||||
"id": "bd7150d8c442eddfafb5bd1c",
|
"id": "bd7150d8c442eddfafb5bd1c",
|
||||||
"title": "P2P Video Chat Application",
|
"title": "P2P Video Chat Application",
|
||||||
"description": [
|
"description": [
|
||||||
"<strong>Objective:</strong> Build a <a href='https://gomix.com' target='_blank'>Gomix</a> app that is functionally similar to this: <a href='https://unequaled-cement.gomix.me' target='_blank'>https://unequaled-cement.gomix.me</a>.",
|
"<strong>Objective:</strong> Build a <a href='https://glitch.com' target='_blank'>Glitch</a> app that is functionally similar to this: <a href='https://unequaled-cement.glitch.me' target='_blank'>https://unequaled-cement.glitch.me</a>.",
|
||||||
"Fulfill the below <a href='https://en.wikipedia.org/wiki/User_story' target='_blank'>user stories</a>. Use whichever libraries or APIs you need. Give it your own personal style.",
|
"Fulfill the below <a href='https://en.wikipedia.org/wiki/User_story' target='_blank'>user stories</a>. Use whichever libraries or APIs you need. Give it your own personal style.",
|
||||||
"<strong>User Story:</strong> Upon arriving, the browser will prompt me to access my camera and microphone. ",
|
"<strong>User Story:</strong> Upon arriving, the browser will prompt me to access my camera and microphone. ",
|
||||||
"<strong>User Story:</strong> After I give it permission, I am prompted to type in a room name.",
|
"<strong>User Story:</strong> After I give it permission, I am prompted to type in a room name.",
|
||||||
@ -512,7 +513,7 @@
|
|||||||
"<strong>User Story:</strong> When I choose to cancel the room name input step, or if I type in no name, or just spaces, it should again ask me again to type in a valid room name. ",
|
"<strong>User Story:</strong> When I choose to cancel the room name input step, or if I type in no name, or just spaces, it should again ask me again to type in a valid room name. ",
|
||||||
"<strong>User Story:</strong> If one of the two people in the room get disconnected, they can reconnect to the same room and continue chatting.",
|
"<strong>User Story:</strong> If one of the two people in the room get disconnected, they can reconnect to the same room and continue chatting.",
|
||||||
"Remember to use <a href='http://forum.freeCodeCamp.com/t/how-to-get-help-when-you-are-stuck/19514' target='_blank'>Read-Search-Ask</a> if you get stuck.",
|
"Remember to use <a href='http://forum.freeCodeCamp.com/t/how-to-get-help-when-you-are-stuck/19514' target='_blank'>Read-Search-Ask</a> if you get stuck.",
|
||||||
"When you are finished, click the \"I've completed this challenge\" button and include a link to your Gomix App.",
|
"When you are finished, click the \"I've completed this challenge\" button and include a link to your Glitch App.",
|
||||||
"You can get feedback on your project by sharing it with your friends on Facebook."
|
"You can get feedback on your project by sharing it with your friends on Facebook."
|
||||||
],
|
],
|
||||||
"challengeSeed": [],
|
"challengeSeed": [],
|
||||||
|
@ -22,9 +22,9 @@ export default function csp() {
|
|||||||
'https://*.optimizely.com'
|
'https://*.optimizely.com'
|
||||||
]),
|
]),
|
||||||
connectSrc: trusted.concat([
|
connectSrc: trusted.concat([
|
||||||
'https://gomix.com',
|
'https://glitch.com',
|
||||||
'https://*.gomix.com',
|
'https://*.glitch.com',
|
||||||
'https://*.gomix.me',
|
'https://*.glitch.me',
|
||||||
'https://*.cloudflare.com'
|
'https://*.cloudflare.com'
|
||||||
]),
|
]),
|
||||||
scriptSrc: [
|
scriptSrc: [
|
||||||
|
Reference in New Issue
Block a user