From 12e5e03512b220aee2f6f06c0c4e4a1f71b44ec1 Mon Sep 17 00:00:00 2001 From: isumizumi Date: Mon, 13 Mar 2017 15:32:52 +0700 Subject: [PATCH 1/6] Testing Get (Green: Success passing) --- .gitignore | 1 + README.md | 4 +++- app.js | 18 +++++++++++++++ controllers/controllerArticle.js | 33 +++++++++++++++++++++++++++ models/article.js | 13 +++++++++++ package.json | 34 ++++++++++++++++++++++++++++ routes/users.js | 11 +++++++++ test/testing.js | 38 ++++++++++++++++++++++++++++++++ 8 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 app.js create mode 100644 controllers/controllerArticle.js create mode 100644 models/article.js create mode 100644 package.json create mode 100644 routes/users.js create mode 100644 test/testing.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md index 4ac03ba..d1a9bef 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# blog-tdd \ No newline at end of file +# blog-tdd + +Access web via http://localhost:3000/users diff --git a/app.js b/app.js new file mode 100644 index 0000000..6dbccb2 --- /dev/null +++ b/app.js @@ -0,0 +1,18 @@ +var express = require('express'); +var bodyParser = require('body-parser'); +var morgan = require('morgan') + +var users = require('./routes/users'); + +var mongoose = require('mongoose'); +mongoose.connect('mongodb://localhost/blog') + +var app = express(); + +app.use(morgan()) +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); + +app.use('/users', users); + +app.listen(3000) diff --git a/controllers/controllerArticle.js b/controllers/controllerArticle.js new file mode 100644 index 0000000..6c0bcf9 --- /dev/null +++ b/controllers/controllerArticle.js @@ -0,0 +1,33 @@ +'use strict' +var Article = require('../models/article.js'); +var slug = require('slug') + +module.exports = { + createArticle : (req, res, next) => { + var newArticle = Article( + { + title: req.body.title, + content: req.body.content, + category: req.body.category, + slug: slug(req.body.title) + }) + + newArticle.save((err) => { + if (err) { + res.send(err) + } else { + res.send(`${req.body.title} has been created`) + } + }) + }, + + getArticle : (req, res, next) => { + Article.find({}, (err, articles) => { + if (err) { + res.send(err) + } else { + res.status(200).json(articles); + } + }) + } +} diff --git a/models/article.js b/models/article.js new file mode 100644 index 0000000..d5097d6 --- /dev/null +++ b/models/article.js @@ -0,0 +1,13 @@ +'use strict' +var mongoose = require('mongoose'); + +var articleSchema = mongoose.Schema({ + title: String, + content: String, + category: String, + slug: String +}) + +var article = mongoose.model('Article', articleSchema) + +module.exports = article diff --git a/package.json b/package.json new file mode 100644 index 0000000..2377978 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "blog-tdd", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "nodemon app.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/isumizumi/blog-tdd.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/isumizumi/blog-tdd/issues" + }, + "homepage": "https://github.com/isumizumi/blog-tdd#readme", + "dependencies": { + "body-parser": "^1.17.1", + "express": "^4.15.2", + "mongodb": "^2.2.24", + "mongoose": "^4.8.7", + "morgan": "^1.8.1", + "slug": "^0.9.1" + }, + "devDependencies": { + "chai": "^3.5.0", + "chai-http": "^3.0.0", + "mocha": "^3.2.0", + "nodemon": "^1.11.0" + } +} diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..289218e --- /dev/null +++ b/routes/users.js @@ -0,0 +1,11 @@ +const express = require('express'); +const router = express.Router(); +const controllerArticle = require('../controllers/controllerArticle') + + +/* GET users listing. */ +router.get('/api/article', controllerArticle.getArticle); + +router.post('/api/article', controllerArticle.createArticle); + +module.exports = router; diff --git a/test/testing.js b/test/testing.js new file mode 100644 index 0000000..310d548 --- /dev/null +++ b/test/testing.js @@ -0,0 +1,38 @@ +const chai = require('chai') +const should = chai.should() +const chaiHttp = require('chai-http') + +chai.use(chaiHttp); + +describe('Posting new article', () => { + it('should store a new article to the database and return data', (done) => { + chai.request('http://localhost:3000/users') + .post('/api/article') + .send({ + title: 'Hello World!', + content: 'Lorem ipsum dolor sit amet', + category: 'Learning' + }) + .end((err,res) => { + res.should.be.json; + res.should.have.status(200); + res.body.title.should.equal('Hello World!'); + res.body.content.should.equal('Lorem ipsum dolor sit amet'); + res.body.category.should.equal('Learning'); + res.body.slug.should.equal('hello-world'); + done(); + }) + }) +}) + +describe('Getting list articles', () => { + it('should show list articles', (done) => { + chai.request('http://localhost:3000/users') + .get('/api/article') + .end((err,res) => { + res.should.be.json; + res.should.have.status(200); + done(); + }) + }) +}) From 40d9b6d4980f5a62697310bbd853b2d42dcc9e57 Mon Sep 17 00:00:00 2001 From: isumizumi Date: Mon, 13 Mar 2017 16:21:39 +0700 Subject: [PATCH 2/6] Testing POST & PUT (Green: Success Passing) --- app.js | 12 +- controllers/controllerArticle.js | 35 ++- public/css/style.css | 416 +++++++++++++++++++++++++++++++ routes/users.js | 4 +- test/testing.js | 63 +++-- views/index.html | 196 +++++++++++++++ 6 files changed, 692 insertions(+), 34 deletions(-) create mode 100644 public/css/style.css create mode 100644 views/index.html diff --git a/app.js b/app.js index 6dbccb2..bc0eae8 100644 --- a/app.js +++ b/app.js @@ -1,13 +1,13 @@ -var express = require('express'); -var bodyParser = require('body-parser'); -var morgan = require('morgan') +var express = require('express'); +var bodyParser = require('body-parser'); +var morgan = require('morgan') -var users = require('./routes/users'); +var users = require('./routes/users'); -var mongoose = require('mongoose'); +var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/blog') -var app = express(); +var app = express(); app.use(morgan()) app.use(bodyParser.json()); diff --git a/controllers/controllerArticle.js b/controllers/controllerArticle.js index 6c0bcf9..93737bf 100644 --- a/controllers/controllerArticle.js +++ b/controllers/controllerArticle.js @@ -3,31 +3,44 @@ var Article = require('../models/article.js'); var slug = require('slug') module.exports = { + getArticle : (req, res, next) => { + Article.find({}, (err, articles) => { + if (err) { + res.send(err) + } else { + res.status(200).json(articles); + } + }) + }, + createArticle : (req, res, next) => { var newArticle = Article( { title: req.body.title, content: req.body.content, category: req.body.category, - slug: slug(req.body.title) + slug: slug(req.body.title).toLowerCase() }) - newArticle.save((err) => { + newArticle.save((err, data) => { if (err) { res.send(err) } else { - res.send(`${req.body.title} has been created`) + res.send(data) } }) }, - getArticle : (req, res, next) => { - Article.find({}, (err, articles) => { - if (err) { - res.send(err) - } else { - res.status(200).json(articles); - } - }) + updateArticle : (req, res, next) => { + Article.findOneAndUpdate( + { + slug: slug(req.body.title).toLowerCase() + }, req.body, {new: true}, (err, articles) => { + if (err) { + res.send(err) + } else { + res.send(articles); + } + }) } } diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..3f8ccdb --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,416 @@ +/* ================================= + Base Element Styles +==================================== */ + +* { + box-sizing: border-box; +} + +body { + color: #000305; + font-size: 14px; + font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; + line-height: 1.5; + margin: 0; + padding: 0; + text-align: left; +} + + +/* Headings */ +h1 {font-size: 23px} /* 23px */ +h2 {font-size: 22px} /* 22px */ +h3 {font-size: 20px} /* 20px */ +h4 {font-size: 18px} /* 18px */ +h5 {font-size: 16px} /* 16px */ +h6 {font-size: 14px} /* 14px */ + +h1, h2, h3, h4, h5, h6 { + font-weight: 400; + line-height: 1; + margin-bottom: .8em; +} + +/* Paragraphs */ +p {margin-bottom: 1em;} +* p:last-child {margin-bottom: 0;} + +strong, b {font-weight: bold;} +em, i {font-style: italic;} + +::-moz-selection {background: #F6CF74; color: #fff;} +::selection {background: #F6CF74; color: #fff;} + +/* Anchors */ +a {outline: 0;} +a img {border: 0px; text-decoration: none;} +a:link, a:visited { + color: #C74350; + padding: 0 1px; + text-decoration: underline; +} +a:hover, a:active { + background-color: #C74350; + color: #fff; + text-decoration: none; + text-shadow: 1px 1px 1px #333; +} + +img { + max-width: 30%; +} + +#logo{ + max-width: 5%; +} +/* Lists */ +ul { + list-style: outside disc; + margin: 1em 0 1.5em 1.5em; +} + +ol { + list-style: outside decimal; + margin: 1em 0 1.5em 1.5em; +} + +/* HTML5 tags */ +header, section, footer, +aside, nav, article, figure { + display: block; +} + +/* ================================= + Base Layout Styles +==================================== */ + +/***** Layout *****/ +.body {clear: both; margin: 0 auto;} +img.right figure.right {float: right; margin: 0 0 2em 2em;} +img.left, figure.left {float: right; margin: 0 0 2em 2em;} + +/* + Header +*****************/ +#banner { + margin: 0 auto; + padding: 2.5em 0 0 0; +} + +/* Banner */ +#banner h1 {font-size: 3em; line-height: .6;} +#banner h1 a:link, #banner h1 a:visited { + color: #000305; + display: block; + font-weight: bold; + margin: 0 0 .6em .2em; + text-decoration: none; + width: 100%; +} +#banner h1 a:hover, #banner h1 a:active { + background: none; + color: #C74350; + text-shadow: none; +} + +#banner h1 strong {font-size: 0.36em; font-weight: normal;} + +/* Main Nav */ +#banner nav { + background: #000305; + font-size: 1em; + height: 35px; + line-height: 30px; + margin: 0 auto 2em auto; + padding: 0; + text-align: center; + width: 100%; + + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +#banner nav ul {list-style: none; margin: 0 auto;} +#banner nav li {float: left; display: inline; margin: 0;} + +#banner nav a:link, #banner nav a:visited { + color: #fff; + display: inline-block; + height: 35px; + padding: 5px 1.5em; + text-decoration: none; +} +#banner nav a:hover, #banner nav a:active, +#banner nav .active a:link, #banner nav .active a:visited { + background: #C74451; + color: #fff; + text-shadow: none !important; +} + +#banner nav li:first-child a { + border-top-left-radius: 5px; + -moz-border-radius-topleft: 5px; + -webkit-border-top-left-radius: 5px; + + border-bottom-left-radius: 5px; + -moz-border-radius-bottomleft: 5px; + -webkit-border-bottom-left-radius: 5px; +} + +/* +Search +*****************/ +#mysearch { + margin: 0px 0px 0px 17%; + padding: 0px; + text-align: center; + width: 20%; + } + +input[type=search] { + width: 20%; + -webkit-transition: width 0.4s ease-in-out; + transition: width 0.4s ease-in-out; + text-align: center; + height: 35px; + margin: 0 auto; + padding: 0px; + font-family: Georgia; /* Overridden by font: -webkit-small-control; */ + border: 1px solid #C74451; /* Overridden by border: 1px inset; */ + background: #fff; /* Overridden by background-color: white; */ +} + +input[type=search]:focus { + width: 40%; +} + + /* + Featured +*****************/ +#featured { + background: #fff; + margin-bottom: 2em; + overflow: hidden; + padding: 20px; + width: 100%; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +#featured figure { + border: 0px solid #eee; + float: right; + margin: 0em 3em 0 3em; + width: 50%; +} +#featured figure img {display: block; float: right;} + +#featured h2 {color: #C74451; font-size: 1.7em; margin-bottom: 0;} +#featured h3 {font-size: 1.5em; margin-bottom: .5em;} + +#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;} +#featured h3 a:hover, #featured h3 a:active {color: #fff;} + +/* + Body +*****************/ +#content { + background: #fff; + margin-bottom: 2em; + overflow: hidden; + padding: 20px 20px; + width: 100%; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +/* + Pagination +*****************/ +#page { + margin: 0 30%; + padding: 0px; +} + +.pagination { + display: inline-block; +} + +.pagination a { + color: black; + float: left; + padding: 8px 16px; + text-decoration: none; +} + +/* + Extras +*****************/ +#extras {margin: 0 auto 3em auto; overflow: hidden;} + +#extras ul {list-style: none; margin: 0;} +#extras li {border-bottom: 1px solid #fff;} +#extras h2 { + color: #C74350; + font-size: 1.5em; + margin-bottom: .25em; + padding: 0 3px; +} + +#extras a:link, #extras a:visited { + color: #444; + display: block; + border-bottom: 1px solid #F4E3E3; + text-decoration: none; + padding: .3em .25em; +} + +/* Blogroll */ +#extras .blogroll { + float: left; + width: 40%; +} + +#extras .blogroll li {float: left; margin: 0 20px 0 0; width: 40%;} + +/* Social */ +#extras .social { + float: right; + width: 50%; +} + +#extras li:last-child, /* last
  • */ +#extras li:last-child a /* of last
  • */ + {border: 0} + +#extras .blogroll li:nth-last-child(2), +#extras .blogroll li:nth-last-child(3), +#extras .blogroll li:nth-last-child(2) a, +#extras .blogroll li:nth-last-child(3) a {border: 0;} + +#extras div[class='social'] a { + background-repeat: no-repeat; + background-position: 3px 6px; + padding-left: 25px; +} + +/* Icons */ +.social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');} +.social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');} + +/* + About +*****************/ +#about { + background: #fff; + font-style: normal; + margin-bottom: 2em; + overflow: hidden; + padding: 20px; + text-align: left; + width: 100%; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +#about .primary {float: left; width: 50%;} +#about .primary strong {color: #C64350; display: block; font-size: 1.2em;} +#about .photo {float: left; margin: 0;} + +#about .url:link, #about .url:visited {text-decoration: none;} + +#about .bio {float: right; width: 43%;} + +/* + Footer +*****************/ +#contentinfo {padding-bottom: 2em; text-align: right;} + +/* Blog */ +.hentry { + border-bottom: 1px solid #eee; + padding: 1.5em 0; +} +li:last-child .hentry, #content > .hentry {border: 0; margin: 0;} +#content > .hentry {padding: 1em 0;} + +.entry-title {font-size: 1.5em; margin-bottom: 0;} +.entry-title a:link, .entry-title a:visited {text-decoration: none;} + +.hentry .post-info * {font-style: normal;} + +/* Content */ +.hentry footer {margin-bottom: 2em;} +.hentry footer address {display: inline;} +#posts-list footer address {display: block;} + +/* Blog Index */ +#posts-list {list-style: none; margin: 0;} +#posts-list .hentry {padding-left: 200px; position: relative;} +#posts-list footer { + left: 10px; + position: absolute; + top: 1.5em; + width: 100%; +} + +#posts-list .hentry:hover { + background: #C64350; + color: #fff; +} +#posts-list .hentry:hover a:link, #posts-list .hentry:hover a:visited { + color: #F6CF74; + text-shadow: 1px 1px 1px #333; +} + +/* ================================= + Media Queries +==================================== */ + +/* For mobile phones: */ +[class*="col-"] { + width: 100%; +} +@media only screen and (min-width: 600px) { + /* For tablets: */ + .col-m-1 {width: 8.33%;} + .col-m-2 {width: 16.66%;} + .col-m-3 {width: 25%;} + .col-m-4 {width: 33.33%;} + .col-m-5 {width: 41.66%;} + .col-m-6 {width: 50%;} + .col-m-7 {width: 58.33%;} + .col-m-8 {width: 66.66%;} + .col-m-9 {width: 75%;} + .col-m-10 {width: 83.33%;} + .col-m-11 {width: 91.66%;} + .col-m-12 {width: 100%;} + body { + background-color: #ffeaf2; + } +} +@media only screen and (min-width: 768px) { + /* For desktop: */ + .col-1 {width: 8.33%;} + .col-2 {width: 16.66%;} + .col-3 {width: 25%;} + .col-4 {width: 33.33%;} + .col-5 {width: 41.66%;} + .col-6 {width: 50%;} + .col-7 {width: 58.33%;} + .col-8 {width: 66.66%;} + .col-9 {width: 75%;} + .col-10 {width: 83.33%;} + .col-11 {width: 91.66%;} + .col-12 {width: 100%;} + body { + background-color: #ffeaf2; + } +} diff --git a/routes/users.js b/routes/users.js index 289218e..62c7401 100644 --- a/routes/users.js +++ b/routes/users.js @@ -4,8 +4,10 @@ const controllerArticle = require('../controllers/controllerArticle') /* GET users listing. */ -router.get('/api/article', controllerArticle.getArticle); +router.get('/api/article', controllerArticle.getArticle); router.post('/api/article', controllerArticle.createArticle); +router.put('/api/article', controllerArticle.updateArticle); + module.exports = router; diff --git a/test/testing.js b/test/testing.js index 310d548..225e950 100644 --- a/test/testing.js +++ b/test/testing.js @@ -4,35 +4,66 @@ const chaiHttp = require('chai-http') chai.use(chaiHttp); +describe('Getting list articles', () => { + it('should show list articles', (done) => { + chai.request('http://localhost:3000') + .get('/users/api/article') + .end((err,res) => { + res.should.be.json; + res.should.have.status(200); + done(); + }) + }) +}) + describe('Posting new article', () => { it('should store a new article to the database and return data', (done) => { - chai.request('http://localhost:3000/users') - .post('/api/article') + chai.request('http://localhost:3000') + .post('/users/api/article') .send({ title: 'Hello World!', content: 'Lorem ipsum dolor sit amet', category: 'Learning' }) .end((err,res) => { - res.should.be.json; - res.should.have.status(200); - res.body.title.should.equal('Hello World!'); - res.body.content.should.equal('Lorem ipsum dolor sit amet'); - res.body.category.should.equal('Learning'); - res.body.slug.should.equal('hello-world'); - done(); + // console.log(res.body); + if(err){ + done(err); + } else { + // console.log(res); + res.should.be.json; + res.should.have.status(200); + res.body.title.should.equal('Hello World!'); + res.body.content.should.equal('Lorem ipsum dolor sit amet'); + res.body.category.should.equal('Learning'); + res.body.slug.should.equal('hello-world'); + done(); + } }) }) }) -describe('Getting list articles', () => { - it('should show list articles', (done) => { - chai.request('http://localhost:3000/users') - .get('/api/article') +describe('Editing an article', () => { + it('should store an edited article to the database and return data', (done) => { + chai.request('http://localhost:3000') + .put('/users/api/article?slug=hello-world') + .send({ + title: 'Hello World!', + content: 'Lorem ipsum dolor sit amet', + category: 'Learning' + }) .end((err,res) => { - res.should.be.json; - res.should.have.status(200); - done(); + if(err){ + done(err); + } else { + res.should.be.json; + res.should.have.status(200); + res.body.title.should.equal('Hello World!'); + res.body.content.should.equal('Lorem ipsum dolor sit amet'); + res.body.category.should.equal('Learning'); + res.body.slug.should.equal('hello-world'); + done(); + } }) }) }) diff --git a/views/index.html b/views/index.html new file mode 100644 index 0000000..055be99 --- /dev/null +++ b/views/index.html @@ -0,0 +1,196 @@ + + + + + My CSS Framework! + + + + + +
    + + + + + + + + +

    + +
    +
      +
    1. +
      +
      +

      + This be the title +

      +
      +
      + +
      + By author +
      +
      +
      +

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque venenatis nunc vitae libero iaculis elementum. Nullam et justo non sapien dapibus blandit nec et leo. Ut ut malesuada tellus.

      +
      +
      +
    2. + +
    3. +
      +
      +

      + This be the title +

      +
      +
      + +
      + By author +
      +
      +
      +

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque venenatis nunc vitae libero iaculis elementum. Nullam et justo non sapien dapibus blandit nec et leo. Ut ut malesuada tellus.

      +
      +
      +
    4. + +
    5. +
      +
      +

      + This be the title +

      +
      +
      + +
      + By author +
      +
      +
      +

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque venenatis nunc vitae libero iaculis elementum. Nullam et justo non sapien dapibus blandit nec et leo. Ut ut malesuada tellus.

      +
      +
      +
    6. + +
    +
    + +
    + +
    + +
    + + + +
    + + +
    +
    + + My CSS + Amazing CSS + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque venenatis nunc vitae libero iaculis elementum. Nullam et justo non sapien dapibus blandit nec et leo. Ut ut malesuada tellus. +
    + + +
    +

    "Make it as simple as possible, but not simpler."

    +
    + +

    @2017 Isumi Karina

    +
    + +
    + + From 2d1ba1aac41a0d7cd3d3d4da06d9ff289280d26a Mon Sep 17 00:00:00 2001 From: isumizumi Date: Mon, 13 Mar 2017 16:29:42 +0700 Subject: [PATCH 3/6] Revisi Testing PUT --- controllers/controllerArticle.js | 2 +- routes/users.js | 2 +- test/testing.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/controllerArticle.js b/controllers/controllerArticle.js index 93737bf..4d13efc 100644 --- a/controllers/controllerArticle.js +++ b/controllers/controllerArticle.js @@ -34,7 +34,7 @@ module.exports = { updateArticle : (req, res, next) => { Article.findOneAndUpdate( { - slug: slug(req.body.title).toLowerCase() + slug: req.params.slug }, req.body, {new: true}, (err, articles) => { if (err) { res.send(err) diff --git a/routes/users.js b/routes/users.js index 62c7401..af2bdf0 100644 --- a/routes/users.js +++ b/routes/users.js @@ -8,6 +8,6 @@ router.get('/api/article', controllerArticle.getArticle); router.post('/api/article', controllerArticle.createArticle); -router.put('/api/article', controllerArticle.updateArticle); +router.put('/api/article/:slug', controllerArticle.updateArticle); module.exports = router; diff --git a/test/testing.js b/test/testing.js index 225e950..ae2a163 100644 --- a/test/testing.js +++ b/test/testing.js @@ -46,7 +46,7 @@ describe('Posting new article', () => { describe('Editing an article', () => { it('should store an edited article to the database and return data', (done) => { chai.request('http://localhost:3000') - .put('/users/api/article?slug=hello-world') + .put('/users/api/article/hello-world') .send({ title: 'Hello World!', content: 'Lorem ipsum dolor sit amet', From b241971829cf3e3c38d477de00c00227a36ad343 Mon Sep 17 00:00:00 2001 From: isumizumi Date: Mon, 13 Mar 2017 17:04:48 +0700 Subject: [PATCH 4/6] Testing Delete & Get One Data (Green: Success Passing) --- controllers/controllerArticle.js | 25 +++++++++++++++++++++++++ routes/users.js | 4 ++++ test/testing.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/controllers/controllerArticle.js b/controllers/controllerArticle.js index 4d13efc..da65ee9 100644 --- a/controllers/controllerArticle.js +++ b/controllers/controllerArticle.js @@ -42,5 +42,30 @@ module.exports = { res.send(articles); } }) + }, + + deleteArticle : (req, res, next) => { + Article.findOneAndRemove( + { + slug: req.params.slug + }, (err, data) => { + if (err) { + res.send(err) + } else { + res.send(data); + } + }) + }, + + getOneArticle : (req, res, next) => { + Article.find({ + slug: req.params.slug + }, (err, article) => { + if (err) { + res.send(err) + } else { + res.status(200).json(article); + } + }) } } diff --git a/routes/users.js b/routes/users.js index af2bdf0..bac2d07 100644 --- a/routes/users.js +++ b/routes/users.js @@ -10,4 +10,8 @@ router.post('/api/article', controllerArticle.createArticle); router.put('/api/article/:slug', controllerArticle.updateArticle); +router.delete('/api/article/:slug', controllerArticle.deleteArticle); + +router.get('/api/article/:slug', controllerArticle.getOneArticle); + module.exports = router; diff --git a/test/testing.js b/test/testing.js index ae2a163..0fac7a1 100644 --- a/test/testing.js +++ b/test/testing.js @@ -67,3 +67,35 @@ describe('Editing an article', () => { }) }) }) + +describe('Deleting an article', () => { + it('should delete an article from the database', (done) => { + chai.request('http://localhost:3000') + .delete('/users/api/article/hello-world') + .end((err,res) => { + if(err){ + done(err); + } else { + res.should.be.json; + res.should.have.status(200); + res.body.title.should.equal('Hello World!'); + res.body.content.should.equal('Lorem ipsum dolor sit amet'); + res.body.category.should.equal('Learning'); + res.body.slug.should.equal('hello-world'); + done(); + } + }) + }) +}) + +describe('Getting an article', () => { + it('should show one article', (done) => { + chai.request('http://localhost:3000') + .get('/users/api/article/hello-world') + .end((err,res) => { + res.should.be.json; + res.should.have.status(200); + done(); + }) + }) +}) From c02e32e37eeb24fd168d86b504f7b2f2dff4102b Mon Sep 17 00:00:00 2001 From: isumizumi Date: Tue, 14 Mar 2017 17:34:09 +0700 Subject: [PATCH 5/6] Finish Testing Article & User --- README.md | 36 +- app.js | 36 +- controllers/controllerArticle.js | 49 +- controllers/controllerUser.js | 74 +++ models/user.js | 14 + package.json | 6 + public/css/bootstrap.less | 731 ++++++++++++++++++++++++ public/img/city-logo.svg | 22 + public/img/css-logo.png | Bin 0 -> 13308 bytes routes/article.js | 17 + routes/index.js | 9 + routes/user.js | 20 + routes/users.js | 17 - test/{testing.js => testing_article.js} | 96 ++-- test/testing_user.js | 110 ++++ views/article.html | 157 +++++ views/index.html | 6 +- views/signin.html | 131 +++++ views/signup.html | 123 ++++ 19 files changed, 1548 insertions(+), 106 deletions(-) create mode 100644 controllers/controllerUser.js create mode 100644 models/user.js create mode 100644 public/css/bootstrap.less create mode 100644 public/img/city-logo.svg create mode 100644 public/img/css-logo.png create mode 100644 routes/article.js create mode 100644 routes/index.js create mode 100644 routes/user.js delete mode 100644 routes/users.js rename test/{testing.js => testing_article.js} (63%) create mode 100644 test/testing_user.js create mode 100644 views/article.html create mode 100644 views/signin.html create mode 100644 views/signup.html diff --git a/README.md b/README.md index d1a9bef..a33a50a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,37 @@ # blog-tdd -Access web via http://localhost:3000/users +#### List of article routes: + +**Route** | **HTTP** | **Description** +---------------------|---------------|------------------------ +/article | GET | get all article +/article | POST | create an article +/article/:slug | GET | get one data article +/article/:slug | PUT | update data article +/article/:slug | DELETE | delete data article + +#### List of user routes: + +**Route** | **HTTP** | **Description** +---------------------|---------------|------------------------ +/user/signup | POST | create new user +/user/signin | POST | sign in user +/user | GET | get all user +/user/:username | GET | get one data user +/user/:username | PUT | update data user +/user/:username | DELETE | delete data user + +### **USAGE** +#### With only npm: + +>npm install express nodemon chai chai-http cors dotenv cors
    +>mongodb mongoose passport passport-local passport-local-mongoose slug
    + +>npm start
    +>npm run dev
    + +#### Running mongod: +> sudo service mongod start
    +> check connection @robomongo
    + +Access the website via http://localhost:3000/ diff --git a/app.js b/app.js index bc0eae8..510f92a 100644 --- a/app.js +++ b/app.js @@ -1,18 +1,40 @@ -var express = require('express'); -var bodyParser = require('body-parser'); -var morgan = require('morgan') +var express = require('express'); +var bodyParser = require('body-parser'); +var morgan = require('morgan') +var cors = require('cors'); -var users = require('./routes/users'); +var User = require('./models/user'); -var mongoose = require('mongoose'); +var passport = require('passport'); +var LocalStrategy = require('passport-local').Strategy; + +var index = require('./routes/index'); +var article = require('./routes/article'); +var user = require('./routes/user'); + +var mongoose = require('mongoose'); +mongoose.Promise = global.Promise; mongoose.connect('mongodb://localhost/blog') -var app = express(); +var app = express(); app.use(morgan()) app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); -app.use('/users', users); +//LOCAL STRATEGY +passport.use(new LocalStrategy(User.authenticate())); + +app.use(passport.initialize()); +app.use(passport.session()); + +app.use(cors()); + +app.use('/', index); +app.use('/article', article); +app.use('/user', user); + +passport.serializeUser(User.serializeUser()); +passport.deserializeUser(User.deserializeUser()); app.listen(3000) diff --git a/controllers/controllerArticle.js b/controllers/controllerArticle.js index da65ee9..5bbbe05 100644 --- a/controllers/controllerArticle.js +++ b/controllers/controllerArticle.js @@ -1,18 +1,8 @@ 'use strict' -var Article = require('../models/article.js'); -var slug = require('slug') +var Article = require('../models/article.js'); +var slug = require('slug') module.exports = { - getArticle : (req, res, next) => { - Article.find({}, (err, articles) => { - if (err) { - res.send(err) - } else { - res.status(200).json(articles); - } - }) - }, - createArticle : (req, res, next) => { var newArticle = Article( { @@ -31,6 +21,28 @@ module.exports = { }) }, + getArticle : (req, res, next) => { + Article.find({}, (err, articles) => { + if (err) { + res.send(err) + } else { + res.status(200).json(articles); + } + }) + }, + + getOneArticle : (req, res, next) => { + Article.find({ + slug: req.params.slug + }, (err, article) => { + if (err) { + res.send(err) + } else { + res.status(200).json(article); + } + }) + }, + updateArticle : (req, res, next) => { Article.findOneAndUpdate( { @@ -55,17 +67,6 @@ module.exports = { res.send(data); } }) - }, - - getOneArticle : (req, res, next) => { - Article.find({ - slug: req.params.slug - }, (err, article) => { - if (err) { - res.send(err) - } else { - res.status(200).json(article); - } - }) } + } diff --git a/controllers/controllerUser.js b/controllers/controllerUser.js new file mode 100644 index 0000000..c02d981 --- /dev/null +++ b/controllers/controllerUser.js @@ -0,0 +1,74 @@ +var passport = require('passport'); +var User = require('../models/user'); +var LocalStrategy = require('passport-local').Strategy; + + +module.exports = { + register : (req, res, next) => { + User.register({ + username: req.body.username, + email: req.body.email + }, req.body.password, (err, user) => { + if (err) { + res.send(err) + } else { + // passport.authenticate('local')(req, res, () => { + res.json(user); + // }) + } + }) + }, + + signin : (req, res, next) => { + passport.authenticate('local', (err, user) => { + res.json(user) + })(req, res, next ) + }, + + getUsers : (req, res, next) => { + User.find({}, (err, users) => { + if (err) { + res.send(err) + } else { + res.json(users); + } + }) + }, + + getOneUser : (req, res, next) => { + User.findOne({ + username: req.params.username + }, (err, user) => { + if (err) { + res.send(err) + } else { + res.json(user); + } + }) + }, + + updateUser : (req, res, next) => { + User.findOneAndUpdate({ + username: req.params.username + }, req.body, {new: true}, (err, user) => { + if (err) { + res.send(err) + } else { + res.json(user) + } + }) + }, + + deleteUser : (req, res, next) => { + User.findOneAndRemove({ + username: req.params.username + }, (err, user) => { + if (err) { + res.send(err) + } else { + res.json(user) + } + }) + } + +} diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000..10f6eb3 --- /dev/null +++ b/models/user.js @@ -0,0 +1,14 @@ +'use strict' +var mongoose = require('mongoose'); +var passportLocalMongoose = require('passport-local-mongoose'); + +var userSchema = mongoose.Schema({ + username: String, + email: String, + password: String +}) + +userSchema.plugin(passportLocalMongoose) +var user = mongoose.model('User', userSchema) + +module.exports = user diff --git a/package.json b/package.json index 2377978..0570fb2 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,16 @@ "homepage": "https://github.com/isumizumi/blog-tdd#readme", "dependencies": { "body-parser": "^1.17.1", + "cors": "^2.8.1", "express": "^4.15.2", + "jsonwebtoken": "^7.3.0", "mongodb": "^2.2.24", "mongoose": "^4.8.7", "morgan": "^1.8.1", + "passport": "^0.3.2", + "passport-local": "^1.0.0", + "passport-local-mongoose": "^4.0.0", + "password-hash": "^1.2.2", "slug": "^0.9.1" }, "devDependencies": { diff --git a/public/css/bootstrap.less b/public/css/bootstrap.less new file mode 100644 index 0000000..c9f1e40 --- /dev/null +++ b/public/css/bootstrap.less @@ -0,0 +1,731 @@ +/*! + * Default theme for Pingendo + * Homepage: http://pingendo.com + * Copyright 2015 Pingendo + * Licensed under MIT + * Based on Bootstrap v3.3.4 +*/ + + +/* Add custom CSS classes here + * + * img { + * box-shadow : 0px 0px 10px black !important; + * } +*/ + +// +// Variables +// -------------------------------------------------- +//== Colors +// +//## Gray and brand colors for use across Bootstrap. +@gray-base : #000; +@gray-darker : lighten(@gray-base, 13.5%); // #222 +@gray-dark : lighten(@gray-base, 20%); // #333 +@gray : lighten(@gray-base, 33.5%); // #555 +@gray-light : lighten(@gray-base, 46.7%); // #777 +@gray-lighter : lighten(@gray-base, 93.5%); // #eee +@brand-primary : #337CBB; +@brand-success : #5cb85c; +@brand-info : #5bc0de; +@brand-warning : #f0ad4e; +@brand-danger : #d9534f; +//== Scaffolding +// +//## Settings for some of the most global styles. +//** Background color for ``. +@body-bg : white; +//** Global text color on ``. +@text-color : black; +//** Global textual link color. +@link-color : @brand-primary; +//** Link hover color set via `darken()` function. +@link-hover-color : darken(@link-color, 15%); +//** Link hover decoration. +@link-hover-decoration : underline; +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. +@font-family-sans-serif : "Helvetica Neue", +Helvetica, +Arial, +sans-serif; +@font-family-serif : Georgia, +"Times New Roman", +Times, +serif; +//** Default monospace fonts for ``, ``, and `
    `.
    +@font-family-monospace : Menlo,
    +Monaco,
    +Consolas,
    +"Courier New",
    +monospace;
    +@font-family-base : Lato;
    +@font-size-base : 14px;
    +@font-size-large : ceil((@font-size-base * 1.25)); // ~18px
    +@font-size-small : ceil((@font-size-base * 0.85)); // ~12px
    +@font-size-h1 : floor((@font-size-base * 2.6)); // ~36px
    +@font-size-h2 : floor((@font-size-base * 2.15)); // ~30px
    +@font-size-h3 : ceil((@font-size-base * 1.7)); // ~24px
    +@font-size-h4 : ceil((@font-size-base * 1.25)); // ~18px
    +@font-size-h5 : @font-size-base;
    +@font-size-h6 : ceil((@font-size-base * 0.85)); // ~12px
    +//** Unit-less `line-height` for use in components like buttons.
    +@line-height-base : 1.428571429; // 20/14
    +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
    +@line-height-computed : floor((@font-size-base * @line-height-base)); // ~20px
    +//** By default, this inherits from the ``.
    +@headings-font-family : Lato;
    +@headings-font-weight : 500;
    +@headings-line-height : 1.1;
    +@headings-color : inherit;
    +//== Iconography
    +//
    +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
    +//** Load fonts from this directory.
    +@icon-font-path : "../fonts/";
    +//** File name for all font files.
    +@icon-font-name : "glyphicons-halflings-regular";
    +//** Element ID within SVG icon file.
    +@icon-font-svg-id : "glyphicons_halflingsregular";
    +//== Components
    +//
    +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
    +@padding-base-vertical : 6px;
    +@padding-base-horizontal : 12px;
    +@padding-large-vertical : 10px;
    +@padding-large-horizontal : 16px;
    +@padding-small-vertical : 5px;
    +@padding-small-horizontal : 10px;
    +@padding-xs-vertical : 1px;
    +@padding-xs-horizontal : 5px;
    +@line-height-large : 1.3333333; // extra decimals for Win 8.1 Chrome
    +@line-height-small : 1.5;
    +@border-radius-base : 4px;
    +@border-radius-large : 6px;
    +@border-radius-small : 3px;
    +//** Global color for active items (e.g., navs or dropdowns).
    +@component-active-color : contrast(@component-active-bg, @text-color, @text-inverse, 43%);
    +//** Global background color for active items (e.g., navs or dropdowns).
    +@component-active-bg : @brand-primary;
    +//** Width of the `border` for generating carets that indicator dropdowns.
    +@caret-width-base : 4px;
    +//** Carets increase slightly in size for larger components.
    +@caret-width-large : 5px;
    +//== Tables
    +//
    +//## Customizes the `.table` component with basic values, each used across all table variations.
    +//** Padding for ``s and ``s.
    +@table-cell-padding : 8px;
    +//** Padding for cells in `.table-condensed`.
    +@table-condensed-cell-padding : 5px;
    +//** Default background color used for all tables.
    +@table-bg : transparent;
    +//** Background color used for `.table-striped`.
    +@table-bg-accent : #f9f9f9;
    +//** Background color used for `.table-hover`.
    +@table-bg-hover : #f5f5f5;
    +@table-bg-active : @table-bg-hover;
    +//** Border color for table and cell borders.
    +@table-border-color : #ddd;
    +//== Buttons
    +//
    +//## For each of Bootstrap's buttons, define text, background and border color.
    +@btn-font-weight : normal;
    +@btn-default-color : @text-color;
    +@btn-default-bg : @body-bg;
    +@btn-default-border : #ccc;
    +@btn-primary-color : white;
    +@btn-primary-bg : @brand-primary;
    +@btn-primary-border : darken(@btn-primary-bg, 5%);
    +@btn-success-color : white;
    +@btn-success-bg : @brand-success;
    +@btn-success-border : darken(@btn-success-bg, 5%);
    +@btn-info-color : white;
    +@btn-info-bg : @brand-info;
    +@btn-info-border : darken(@btn-info-bg, 5%);
    +@btn-warning-color : white;
    +@btn-warning-bg : @brand-warning;
    +@btn-warning-border : darken(@btn-warning-bg, 5%);
    +@btn-danger-color : contrast(@btn-danger-bg, @text-color, @text-inverse, 43%);
    +@btn-danger-bg : @brand-danger;
    +@btn-danger-border : darken(@btn-danger-bg, 5%);
    +@btn-link-disabled-color : @gray-light;
    +//== Forms
    +//
    +//##
    +//** `` background color
    +@input-bg : #fff;
    +//** `` background color
    +@input-bg-disabled : @gray-lighter;
    +//** Text color for ``s
    +@input-color : @gray;
    +//** `` border color
    +@input-border : #ccc;
    +// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
    +//** Default `.form-control` border radius
    +@input-border-radius : @border-radius-base;
    +//** Large `.form-control` border radius
    +@input-border-radius-large : @border-radius-large;
    +//** Small `.form-control` border radius
    +@input-border-radius-small : @border-radius-small;
    +//** Border color for inputs on focus
    +@input-border-focus : #66afe9;
    +//** Placeholder text color
    +@input-color-placeholder : #999;
    +//** Default `.form-control` height
    +@input-height-base : (@line-height-computed + (@padding-base-vertical * 2) + 2);
    +//** Large `.form-control` height
    +@input-height-large : (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
    +//** Small `.form-control` height
    +@input-height-small : (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
    +//** `.form-group` margin
    +@form-group-margin-bottom: 15px;
    +@legend-color : @gray-dark;
    +@legend-border-color : #e5e5e5;
    +//** Background color for textual input addons
    +@input-group-addon-bg : @gray-lighter;
    +//** Border color for textual input addons
    +@input-group-addon-border-color : @input-border;
    +//** Disabled cursor for form controls and buttons.
    +@cursor-disabled : not-allowed;
    +//== Dropdowns
    +//
    +//## Dropdown menu container and contents.
    +//** Background for the dropdown menu.
    +@dropdown-bg : #fff;
    +//** Dropdown menu `border-color`.
    +@dropdown-border : rgba(0, 0, 0, .15);
    +//** Dropdown menu `border-color` **for IE8**.
    +@dropdown-fallback-border : #ccc;
    +//** Divider color for between dropdown items.
    +@dropdown-divider-bg : #e5e5e5;
    +//** Dropdown link text color.
    +@dropdown-link-color : @gray-dark;
    +//** Hover color for dropdown links.
    +@dropdown-link-hover-color : darken(@gray-dark, 5%);
    +//** Hover background for dropdown links.
    +@dropdown-link-hover-bg : #f5f5f5;
    +//** Active dropdown menu item text color.
    +@dropdown-link-active-color : @component-active-color;
    +//** Active dropdown menu item background color.
    +@dropdown-link-active-bg : @component-active-bg;
    +//** Disabled dropdown menu item background color.
    +@dropdown-link-disabled-color : @gray-light;
    +//** Text color for headers within dropdown menus.
    +@dropdown-header-color : @gray-light;
    +//** Deprecated `@dropdown-caret-color` as of v3.1.0
    +@dropdown-caret-color : #000;
    +//-- Z-index master list
    +//
    +// Warning: Avoid customizing these values. They're used for a bird's eye view
    +// of components dependent on the z-axis and are designed to all work together.
    +//
    +// Note: These variables are not generated into the Customizer.
    +@zindex-navbar : 1000;
    +@zindex-dropdown : 1000;
    +@zindex-popover : 1060;
    +@zindex-tooltip : 1070;
    +@zindex-navbar-fixed : 1030;
    +@zindex-modal-background: 1040;
    +@zindex-modal: 1050;
    +//== Media queries breakpoints
    +//
    +//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
    +// Extra small screen / phone
    +//** Deprecated `@screen-xs` as of v3.0.1
    +@screen-xs : 480px;
    +//** Deprecated `@screen-xs-min` as of v3.2.0
    +@screen-xs-min : @screen-xs;
    +//** Deprecated `@screen-phone` as of v3.0.1
    +@screen-phone : @screen-xs-min;
    +// Small screen / tablet
    +//** Deprecated `@screen-sm` as of v3.0.1
    +@screen-sm : 768px;
    +@screen-sm-min : @screen-sm;
    +//** Deprecated `@screen-tablet` as of v3.0.1
    +@screen-tablet : @screen-sm-min;
    +// Medium screen / desktop
    +//** Deprecated `@screen-md` as of v3.0.1
    +@screen-md : 992px;
    +@screen-md-min : @screen-md;
    +//** Deprecated `@screen-desktop` as of v3.0.1
    +@screen-desktop : @screen-md-min;
    +// Large screen / wide desktop
    +//** Deprecated `@screen-lg` as of v3.0.1
    +@screen-lg : 1200px;
    +@screen-lg-min : @screen-lg;
    +//** Deprecated `@screen-lg-desktop` as of v3.0.1
    +@screen-lg-desktop : @screen-lg-min;
    +// So media queries don't overlap when required, provide a maximum
    +@screen-xs-max : (@screen-sm-min - 1);
    +@screen-sm-max : (@screen-md-min - 1);
    +@screen-md-max : (@screen-lg-min - 1);
    +//== Grid system
    +//
    +//## Define your custom responsive grid.
    +//** Number of columns in the grid.
    +@grid-columns : 12;
    +//** Padding between columns. Gets divided in half for the left and right.
    +@grid-gutter-width : 30px;
    +// Navbar collapse
    +//** Point at which the navbar becomes uncollapsed.
    +@grid-float-breakpoint : @screen-sm-min;
    +//** Point at which the navbar begins collapsing.
    +@grid-float-breakpoint-max : (@grid-float-breakpoint - 1);
    +//== Container sizes
    +//
    +//## Define the maximum width of `.container` for different screen sizes.
    +// Small screen / tablet
    +@container-tablet : (720px + @grid-gutter-width);
    +//** For `@screen-sm-min` and up.
    +@container-sm : @container-tablet;
    +// Medium screen / desktop
    +@container-desktop : (940px + @grid-gutter-width);
    +//** For `@screen-md-min` and up.
    +@container-md : @container-desktop;
    +// Large screen / wide desktop
    +@container-large-desktop : (1140px + @grid-gutter-width);
    +//** For `@screen-lg-min` and up.
    +@container-lg : @container-large-desktop;
    +//== Navbar
    +//
    +//##
    +// Basics of a navbar
    +@navbar-height : 50px;
    +@navbar-margin-bottom : @line-height-computed;
    +@navbar-border-radius : @border-radius-base;
    +@navbar-padding-horizontal : floor((@grid-gutter-width / 2));
    +@navbar-padding-vertical : ((@navbar-height - @line-height-computed) / 2);
    +@navbar-collapse-max-height : 340px;
    +@navbar-default-color : black;
    +@navbar-default-bg : #f8f8f8;
    +@navbar-default-border : darken(@navbar-default-bg, 6.5%);
    +// Navbar links
    +@navbar-default-link-color : contrast(@navbar-default-bg, @text-color, @text-inverse, 43%);
    +@navbar-default-link-hover-color : @navbar-default-color;
    +@navbar-default-link-hover-bg : transparent;
    +@navbar-default-link-active-color : @navbar-default-color;
    +@navbar-default-link-active-bg : darken(@navbar-default-bg, 6.5%);
    +@navbar-default-link-disabled-color : #ccc;
    +@navbar-default-link-disabled-bg : transparent;
    +// Navbar brand label
    +@navbar-default-brand-color : @navbar-default-link-color;
    +@navbar-default-brand-hover-color : darken(@navbar-default-brand-color, 10%);
    +@navbar-default-brand-hover-bg : transparent;
    +// Navbar toggle
    +@navbar-default-toggle-hover-bg : #ddd;
    +@navbar-default-toggle-icon-bar-bg : #888;
    +@navbar-default-toggle-border-color : #ddd;
    +// Inverted navbar
    +// Reset inverted navbar basics
    +@navbar-inverse-color : lighten(@gray-light, 15%);
    +@navbar-inverse-bg : #222;
    +@navbar-inverse-border : darken(@navbar-inverse-bg, 10%);
    +// Inverted navbar links
    +@navbar-inverse-link-color : lighten(@gray-light, 15%);
    +@navbar-inverse-link-hover-color : #fff;
    +@navbar-inverse-link-hover-bg : transparent;
    +@navbar-inverse-link-active-color : @navbar-inverse-link-hover-color;
    +@navbar-inverse-link-active-bg : darken(@navbar-inverse-bg, 10%);
    +@navbar-inverse-link-disabled-color : #444;
    +@navbar-inverse-link-disabled-bg : transparent;
    +// Inverted navbar brand label
    +@navbar-inverse-brand-color : @navbar-inverse-link-color;
    +@navbar-inverse-brand-hover-color : #fff;
    +@navbar-inverse-brand-hover-bg : transparent;
    +// Inverted navbar toggle
    +@navbar-inverse-toggle-hover-bg : #333;
    +@navbar-inverse-toggle-icon-bar-bg : #fff;
    +@navbar-inverse-toggle-border-color : #333;
    +//== Navs
    +//
    +//##
    +//=== Shared nav styles
    +@nav-link-padding : 10px 15px;
    +@nav-link-hover-bg : @gray-lighter;
    +@nav-disabled-link-color : @gray-light;
    +@nav-disabled-link-hover-color : @gray-light;
    +//== Tabs
    +@nav-tabs-border-color : #ddd;
    +@nav-tabs-link-hover-border-color : @gray-lighter;
    +@nav-tabs-active-link-hover-bg : @body-bg;
    +@nav-tabs-active-link-hover-color : @gray;
    +@nav-tabs-active-link-hover-border-color : #ddd;
    +@nav-tabs-justified-link-border-color : #ddd;
    +@nav-tabs-justified-active-link-border-color : @body-bg;
    +//== Pills
    +@nav-pills-border-radius : @border-radius-base;
    +@nav-pills-active-link-hover-bg : @component-active-bg;
    +@nav-pills-active-link-hover-color : @component-active-color;
    +//== Pagination
    +//
    +//##
    +@pagination-color : @link-color;
    +@pagination-bg : #fff;
    +@pagination-border : #ddd;
    +@pagination-hover-color : @link-hover-color;
    +@pagination-hover-bg : @gray-lighter;
    +@pagination-hover-border : #ddd;
    +@pagination-active-color : #fff;
    +@pagination-active-bg : @brand-primary;
    +@pagination-active-border : @brand-primary;
    +@pagination-disabled-color : @gray-light;
    +@pagination-disabled-bg : #fff;
    +@pagination-disabled-border : #ddd;
    +//== Pager
    +//
    +//##
    +@pager-bg : @pagination-bg;
    +@pager-border : @pagination-border;
    +@pager-border-radius : 15px;
    +@pager-hover-bg : @pagination-hover-bg;
    +@pager-active-bg : @pagination-active-bg;
    +@pager-active-color : @pagination-active-color;
    +@pager-disabled-color : @pagination-disabled-color;
    +//== Jumbotron
    +//
    +//##
    +@jumbotron-padding : 30px;
    +@jumbotron-color : inherit;
    +@jumbotron-bg : @gray-lighter;
    +@jumbotron-heading-color : inherit;
    +@jumbotron-font-size : ceil((@font-size-base * 1.5));
    +@jumbotron-heading-font-size: ceil((@font-size-base * 4.5));
    +//== Form states and alerts
    +//
    +//## Define colors for form feedback states and, by default, alerts.
    +@state-success-text : @brand-success;
    +@state-success-bg : lighten(@brand-success, 20%);
    +@state-success-border : darken(spin(@state-success-bg, -10), 5%);
    +@state-info-text : @brand-info;
    +@state-info-bg : lighten(@brand-info, 20%);
    +@state-info-border : darken(spin(@state-info-bg, -10), 7%);
    +@state-warning-text : @brand-warning;
    +@state-warning-bg : lighten(@brand-warning, 20%);
    +@state-warning-border : darken(spin(@state-warning-bg, -10), 5%);
    +@state-danger-text : @brand-danger;
    +@state-danger-bg : lighten(@brand-danger, 20%);
    +@state-danger-border : darken(spin(@state-danger-bg, -10), 5%);
    +//== Tooltips
    +//
    +//##
    +//** Tooltip max width
    +@tooltip-max-width : 200px;
    +//** Tooltip text color
    +@tooltip-color : #fff;
    +//** Tooltip background color
    +@tooltip-bg : #000;
    +@tooltip-opacity : .9;
    +//** Tooltip arrow width
    +@tooltip-arrow-width : 5px;
    +//** Tooltip arrow color
    +@tooltip-arrow-color : @tooltip-bg;
    +//== Popovers
    +//
    +//##
    +//** Popover body background color
    +@popover-bg : #fff;
    +//** Popover maximum width
    +@popover-max-width : 276px;
    +//** Popover border color
    +@popover-border-color : rgba(0, 0, 0, .2);
    +//** Popover fallback border color
    +@popover-fallback-border-color : #ccc;
    +//** Popover title background color
    +@popover-title-bg : darken(@popover-bg, 3%);
    +//** Popover arrow width
    +@popover-arrow-width : 10px;
    +//** Popover arrow color
    +@popover-arrow-color : @popover-bg;
    +//** Popover outer arrow width
    +@popover-arrow-outer-width : (@popover-arrow-width + 1);
    +//** Popover outer arrow color
    +@popover-arrow-outer-color : fadein(@popover-border-color, 5%);
    +//** Popover outer arrow fallback color
    +@popover-arrow-outer-fallback-color : darken(@popover-fallback-border-color, 20%);
    +//== Labels
    +//
    +//##
    +//** Default label background color
    +@label-default-bg : @gray-light;
    +//** Primary label background color
    +@label-primary-bg : @brand-primary;
    +//** Success label background color
    +@label-success-bg : @brand-success;
    +//** Info label background color
    +@label-info-bg : @brand-info;
    +//** Warning label background color
    +@label-warning-bg : @brand-warning;
    +//** Danger label background color
    +@label-danger-bg : @brand-danger;
    +//** Default label text color
    +@label-color : #fff;
    +//** Default text color of a linked label
    +@label-link-hover-color : #fff;
    +//== Modals
    +//
    +//##
    +//** Padding applied to the modal body
    +@modal-inner-padding : 15px;
    +//** Padding applied to the modal title
    +@modal-title-padding : 15px;
    +//** Modal title line-height
    +@modal-title-line-height : @line-height-base;
    +//** Background color of modal content area
    +@modal-content-bg : #fff;
    +//** Modal content border color
    +@modal-content-border-color : rgba(0, 0, 0, .2);
    +//** Modal content border color **for IE8**
    +@modal-content-fallback-border-color : #999;
    +//** Modal backdrop background color
    +@modal-backdrop-bg : #000;
    +//** Modal backdrop opacity
    +@modal-backdrop-opacity : .5;
    +//** Modal header border color
    +@modal-header-border-color : #e5e5e5;
    +//** Modal footer border color
    +@modal-footer-border-color : @modal-header-border-color;
    +@modal-lg : 900px;
    +@modal-md : 600px;
    +@modal-sm : 300px;
    +//== Alerts
    +//
    +//## Define alert colors, border radius, and padding.
    +@alert-padding : 15px;
    +@alert-border-radius : @border-radius-base;
    +@alert-link-font-weight : bold;
    +@alert-success-bg : @state-success-bg;
    +@alert-success-text : @state-success-text;
    +@alert-success-border : @state-success-border;
    +@alert-info-bg : @state-info-bg;
    +@alert-info-text : @state-info-text;
    +@alert-info-border : @state-info-border;
    +@alert-warning-bg : @state-warning-bg;
    +@alert-warning-text : @state-warning-text;
    +@alert-warning-border : @state-warning-border;
    +@alert-danger-bg : @state-danger-bg;
    +@alert-danger-text : @state-danger-text;
    +@alert-danger-border : @state-danger-border;
    +//== Progress bars
    +//
    +//##
    +//** Background color of the whole progress component
    +@progress-bg : #f5f5f5;
    +//** Progress bar text color
    +@progress-bar-color : #fff;
    +//** Variable for setting rounded corners on progress bar.
    +@progress-border-radius : @border-radius-base;
    +//** Default progress bar color
    +@progress-bar-bg : @brand-primary;
    +//** Success progress bar color
    +@progress-bar-success-bg : @brand-success;
    +//** Warning progress bar color
    +@progress-bar-warning-bg : @brand-warning;
    +//** Danger progress bar color
    +@progress-bar-danger-bg : @brand-danger;
    +//** Info progress bar color
    +@progress-bar-info-bg : @brand-info;
    +//== List group
    +//
    +//##
    +//** Background color on `.list-group-item`
    +@list-group-bg : @body-bg;
    +//** `.list-group-item` border color
    +@list-group-border : darken(@list-group-bg, 40%);
    +//** List group border radius
    +@list-group-border-radius : @border-radius-base;
    +//** Background color of single list items on hover
    +@list-group-hover-bg : #f5f5f5;
    +//** Text color of active list items
    +@list-group-active-color : @component-active-color;
    +//** Background color of active list items
    +@list-group-active-bg : @component-active-bg;
    +//** Border color of active list elements
    +@list-group-active-border : @list-group-active-bg;
    +//** Text color for content within active list items
    +@list-group-active-text-color : darken(@list-group-active-bg, 40%);
    +//** Text color of disabled list items
    +@list-group-disabled-color : @gray-light;
    +//** Background color of disabled list items
    +@list-group-disabled-bg : @gray-lighter;
    +//** Text color for content within disabled list items
    +@list-group-disabled-text-color : @list-group-disabled-color;
    +@list-group-link-color : #555;
    +@list-group-link-hover-color : @list-group-link-color;
    +@list-group-link-heading-color : #333;
    +//== Panels
    +//
    +//##
    +@panel-bg : @body-bg;
    +@panel-body-padding : 15px;
    +@panel-heading-padding : 10px 15px;
    +@panel-footer-padding : @panel-heading-padding;
    +@panel-border-radius : @border-radius-base;
    +//** Border color for elements within panels
    +@panel-inner-border : #ddd;
    +@panel-footer-bg : #f5f5f5;
    +@panel-default-text : @gray-dark;
    +@panel-default-border : #ddd;
    +@panel-default-heading-bg : #f5f5f5;
    +@panel-primary-text : contrast(@brand-primary, @text-color, @text-inverse, 43%);
    +@panel-primary-border : @brand-primary;
    +@panel-primary-heading-bg : @brand-primary;
    +@panel-success-text : @state-success-text;
    +@panel-success-border : @state-success-border;
    +@panel-success-heading-bg : @state-success-bg;
    +@panel-info-text : @state-info-text;
    +@panel-info-border : @state-info-border;
    +@panel-info-heading-bg : @state-info-bg;
    +@panel-warning-text : @state-warning-text;
    +@panel-warning-border : @state-warning-border;
    +@panel-warning-heading-bg : @state-warning-bg;
    +@panel-danger-text : @state-danger-text;
    +@panel-danger-border : @state-danger-border;
    +@panel-danger-heading-bg : @state-danger-bg;
    +//== Thumbnails
    +//
    +//##
    +//** Padding around the thumbnail image
    +@thumbnail-padding : 4px;
    +//** Thumbnail background color
    +@thumbnail-bg : @body-bg;
    +//** Thumbnail border color
    +@thumbnail-border : #ddd;
    +//** Thumbnail border radius
    +@thumbnail-border-radius : @border-radius-base;
    +//** Custom text color for thumbnail captions
    +@thumbnail-caption-color : @text-color;
    +//** Padding around the thumbnail caption
    +@thumbnail-caption-padding : 9px;
    +//== Wells
    +//
    +//##
    +@well-bg : #f5f5f5;
    +@well-border : darken(@well-bg, 7%);
    +//== Badges
    +//
    +//##
    +@badge-color : #fff;
    +//** Linked badge text color on hover
    +@badge-link-hover-color : #fff;
    +@badge-bg : @gray-light;
    +//** Badge text color in active nav link
    +@badge-active-color : @link-color;
    +//** Badge background color in active nav link
    +@badge-active-bg : #fff;
    +@badge-font-weight : bold;
    +@badge-line-height : 1;
    +@badge-border-radius : 10px;
    +//== Breadcrumbs
    +//
    +//##
    +@breadcrumb-padding-vertical : 8px;
    +@breadcrumb-padding-horizontal : 15px;
    +//** Breadcrumb background color
    +@breadcrumb-bg : #f5f5f5;
    +//** Breadcrumb text color
    +@breadcrumb-color : #ccc;
    +//** Text color of current page in the breadcrumb
    +@breadcrumb-active-color : @gray-light;
    +//** Textual separator for between breadcrumb elements
    +@breadcrumb-separator : "/";
    +//== Carousel
    +//
    +//##
    +@carousel-text-shadow : 0 1px 2px rgba(0, 0, 0, .6);
    +@carousel-control-color : #fff;
    +@carousel-control-width : 15%;
    +@carousel-control-opacity : .5;
    +@carousel-control-font-size : 20px;
    +@carousel-indicator-active-bg : #fff;
    +@carousel-indicator-border-color : #fff;
    +@carousel-caption-color : #fff;
    +//== Close
    +//
    +//##
    +@close-font-weight : bold;
    +@close-color : #000;
    +@close-text-shadow : 0 1px 0 #fff;
    +//== Code
    +//
    +//##
    +@code-color : #c7254e;
    +@code-bg : #f9f2f4;
    +@kbd-color : #fff;
    +@kbd-bg : #333;
    +@pre-bg : #f5f5f5;
    +@pre-color : @gray-dark;
    +@pre-border-color : #ccc;
    +@pre-scrollable-max-height : 340px;
    +//== Type
    +//
    +//##
    +//** Horizontal offset for forms and lists.
    +@component-offset-horizontal : 180px;
    +//** Text muted color
    +@text-muted : @gray-light;
    +//** Abbreviations and acronyms border color
    +@abbr-border-color : @gray-light;
    +//** Headings small color
    +@headings-small-color : @gray-light;
    +//** Blockquote small color
    +@blockquote-small-color : @gray-light;
    +//** Blockquote font size
    +@blockquote-font-size : (@font-size-base * 1.25);
    +//** Blockquote border color
    +@blockquote-border-color : @gray-lighter;
    +//** Page header border color
    +@page-header-border-color: @gray-lighter;
    +//** Width of horizontal description list titles
    +@dl-horizontal-offset : @component-offset-horizontal;
    +//** Horizontal line color.
    +@hr-border : @gray-lighter;
    +// Pingendo variables
    +@section-spacing : 35px;
    +// Core variables and mixins
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/mixins.less";
    +// Reset and dependencies
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/normalize.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/print.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/glyphicons.less";
    +// Core CSS
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/scaffolding.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/type.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/code.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/grid.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/tables.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/forms.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/buttons.less";
    +// Components
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/component-animations.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/dropdowns.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/button-groups.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/input-groups.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/navs.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/navbar.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/breadcrumbs.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/pagination.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/pager.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/labels.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/badges.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/jumbotron.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/thumbnails.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/alerts.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/progress-bars.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/media.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/list-group.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/panels.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/responsive-embed.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/wells.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/close.less";
    +// Components w/ JavaScript
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/modals.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/tooltip.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/popovers.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/carousel.less";
    +// Utility classes
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/utilities.less";
    +@import "https://raw.githubusercontent.com/twbs/bootstrap/v3.3.4/less/responsive-utilities.less";
    +@import "https://raw.githubusercontent.com/Pingendo/pingendo-bootstrap/gh-pages/less/pingendo-custom.less";
    diff --git a/public/img/city-logo.svg b/public/img/city-logo.svg
    new file mode 100644
    index 0000000..7d99a83
    --- /dev/null
    +++ b/public/img/city-logo.svg
    @@ -0,0 +1,22 @@
    +
    +
    +
    +	
    +
    +
    +	
    +	
    +
    +
    +	
    +
    +
    diff --git a/public/img/css-logo.png b/public/img/css-logo.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..200f95caef6e39aaada301d96c8e21e37a71a742
    GIT binary patch
    literal 13308
    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
    
    literal 0
    HcmV?d00001
    
    diff --git a/routes/article.js b/routes/article.js
    new file mode 100644
    index 0000000..2a1fa56
    --- /dev/null
    +++ b/routes/article.js
    @@ -0,0 +1,17 @@
    +const express           = require('express');
    +const router            = express.Router();
    +const controllerArticle = require('../controllers/controllerArticle')
    +
    +
    +/* GET users listing. */
    +router.post('/', controllerArticle.createArticle);
    +
    +router.get('/', controllerArticle.getArticle);
    +
    +router.get('/:slug', controllerArticle.getOneArticle);
    +
    +router.put('/:slug', controllerArticle.updateArticle);
    +
    +router.delete('/:slug', controllerArticle.deleteArticle);
    +
    +module.exports = router;
    diff --git a/routes/index.js b/routes/index.js
    new file mode 100644
    index 0000000..ecca96a
    --- /dev/null
    +++ b/routes/index.js
    @@ -0,0 +1,9 @@
    +var express = require('express');
    +var router = express.Router();
    +
    +/* GET home page. */
    +router.get('/', function(req, res, next) {
    +  res.render('index', { title: 'Express' });
    +});
    +
    +module.exports = router;
    diff --git a/routes/user.js b/routes/user.js
    new file mode 100644
    index 0000000..4260772
    --- /dev/null
    +++ b/routes/user.js
    @@ -0,0 +1,20 @@
    +const express           = require('express');
    +const router            = express.Router();
    +const controllerUser    = require('../controllers/controllerUser')
    +const passport          = require('passport');
    +
    +
    +/* GET users listing. */
    +router.post('/signup', controllerUser.register);
    +
    +router.post('/signin', controllerUser.signin);
    +
    +router.get('/', controllerUser.getUsers);
    +
    +router.get('/:username', controllerUser.getOneUser);
    +
    +router.put('/:username', controllerUser.updateUser);
    +
    +router.delete('/:username', controllerUser.deleteUser);
    +
    +module.exports = router;
    diff --git a/routes/users.js b/routes/users.js
    deleted file mode 100644
    index bac2d07..0000000
    --- a/routes/users.js
    +++ /dev/null
    @@ -1,17 +0,0 @@
    -const express           = require('express');
    -const router            = express.Router();
    -const controllerArticle = require('../controllers/controllerArticle')
    -
    -
    -/* GET users listing. */
    -router.get('/api/article', controllerArticle.getArticle);
    -
    -router.post('/api/article', controllerArticle.createArticle);
    -
    -router.put('/api/article/:slug', controllerArticle.updateArticle);
    -
    -router.delete('/api/article/:slug', controllerArticle.deleteArticle);
    -
    -router.get('/api/article/:slug', controllerArticle.getOneArticle);
    -
    -module.exports = router;
    diff --git a/test/testing.js b/test/testing_article.js
    similarity index 63%
    rename from test/testing.js
    rename to test/testing_article.js
    index 0fac7a1..10bc4bc 100644
    --- a/test/testing.js
    +++ b/test/testing_article.js
    @@ -4,33 +4,17 @@ const chaiHttp  = require('chai-http')
     
     chai.use(chaiHttp);
     
    -describe('Getting list articles', () => {
    -      it('should show list articles', (done) => {
    -            chai.request('http://localhost:3000')
    -            .get('/users/api/article')
    -            .end((err,res) => {
    -                  res.should.be.json;
    -                  res.should.have.status(200);
    -                  done();
    -            })
    -      })
    -})
    -
     describe('Posting new article', () => {
           it('should store a new article to the database and return data', (done) => {
                 chai.request('http://localhost:3000')
    -            .post('/users/api/article')
    +            .post('/article')
                 .send({
                       title: 'Hello World!',
                       content: 'Lorem ipsum dolor sit amet',
    -                  category: 'Learning'
    +                  category: 'Learning',
    +                  slug: 'hello-world'
                 })
                 .end((err,res) => {
    -                  // console.log(res.body);
    -                  if(err){
    -                    done(err);
    -                  } else {
    -                  //   console.log(res);
                         res.should.be.json;
                         res.should.have.status(200);
                         res.body.title.should.equal('Hello World!');
    @@ -38,64 +22,68 @@ describe('Posting new article', () => {
                         res.body.category.should.equal('Learning');
                         res.body.slug.should.equal('hello-world');
                         done();
    -                  }
    +            })
    +      })
    +})
    +
    +describe('Getting list articles', () => {
    +      it('should show list articles', (done) => {
    +            chai.request('http://localhost:3000')
    +            .get('/article')
    +            .end((err,res) => {
    +                  res.should.be.json;
    +                  res.should.have.status(200);
    +                  done();
    +            })
    +      })
    +})
    +
    +describe('Getting an article', () => {
    +      let slug = 'hello-world'
    +      it('should show one article', (done) => {
    +            chai.request('http://localhost:3000')
    +            .get('/article/'+slug)
    +            .end((err,res) => {
    +                  res.should.be.json;
    +                  res.should.have.status(200);
    +                  done();
                 })
           })
     })
     
     describe('Editing an article', () => {
    +      let slug = 'hello-world'
           it('should store an edited article to the database and return data', (done) => {
                 chai.request('http://localhost:3000')
    -            .put('/users/api/article/hello-world')
    +            .put('/article/'+slug)
                 .send({
                       title: 'Hello World!',
    -                  content: 'Lorem ipsum dolor sit amet',
    -                  category: 'Learning'
    +                  content: 'Lorem ipsum dolor sit amit-amit!',
    +                  category: 'Tutorial'
                 })
                 .end((err,res) => {
    -                  if(err){
    -                    done(err);
    -                  } else {
    -                    res.should.be.json;
    -                    res.should.have.status(200);
    -                    res.body.title.should.equal('Hello World!');
    -                    res.body.content.should.equal('Lorem ipsum dolor sit amet');
    -                    res.body.category.should.equal('Learning');
    -                    res.body.slug.should.equal('hello-world');
    -                    done();
    -                  }
    +                  res.should.be.json;
    +                  res.should.have.status(200);
    +                  res.body.title.should.equal('Hello World!');
    +                  res.body.content.should.equal('Lorem ipsum dolor sit amit-amit!');
    +                  res.body.category.should.equal('Tutorial');
    +                  done();
                 })
           })
     })
     
     describe('Deleting an article', () => {
    +      let slug = 'hello-world'
           it('should delete an article from the database', (done) => {
                 chai.request('http://localhost:3000')
    -            .delete('/users/api/article/hello-world')
    +            .delete('/article/'+slug)
                 .end((err,res) => {
    -                  if(err){
    -                    done(err);
    -                  } else {
                         res.should.be.json;
                         res.should.have.status(200);
                         res.body.title.should.equal('Hello World!');
    -                    res.body.content.should.equal('Lorem ipsum dolor sit amet');
    -                    res.body.category.should.equal('Learning');
    -                    res.body.slug.should.equal('hello-world');
    +                    res.body.content.should.equal('Lorem ipsum dolor sit amit-amit!');
    +                    res.body.category.should.equal('Tutorial');
                         done();
    -                  }
    -            })
    -      })
    -})
    -
    -describe('Getting an article', () => {
    -      it('should show one article', (done) => {
    -            chai.request('http://localhost:3000')
    -            .get('/users/api/article/hello-world')
    -            .end((err,res) => {
    -                  res.should.be.json;
    -                  res.should.have.status(200);
    -                  done();
                 })
           })
     })
    diff --git a/test/testing_user.js b/test/testing_user.js
    new file mode 100644
    index 0000000..b0b6fb0
    --- /dev/null
    +++ b/test/testing_user.js
    @@ -0,0 +1,110 @@
    +'use strict'
    +const chai      = require('chai');
    +const chaiHttp  = require('chai-http');
    +const should    = chai.should();
    +chai.use(chaiHttp);
    +
    +describe('Testing register new user', () => {
    +  it('should store new user to the database', (done) => {
    +    chai.request('http://localhost:3000')
    +      .post('/user/signup')
    +      .send({
    +        username: 'isumi',
    +        email: 'isumi@hacktiv8.com',
    +        password: '123'
    +      })
    +      .end((err, res) => {
    +        res.should.be.json;
    +        res.should.have.status(200);
    +        done();
    +      })
    +  })
    +})
    +
    +describe('Testing sign in user', () => {
    +  it('should log in and save data (username and password) to the database', (done) => {
    +    chai.request('http://localhost:3000')
    +      .post('/user/signin')
    +      .send({
    +        username: 'isumi',
    +        password: '123'
    +      })
    +      .end((err, res) => {
    +        res.should.be.json;
    +        res.should.have.status(200);
    +        done();
    +      })
    +  })
    +})
    +
    +describe('Testing get all user', () => {
    +  it('should return data users from database', (done) => {
    +    chai.request('http://localhost:3000')
    +      .get('/user')
    +      .send({
    +        username: 'isumi',
    +        password: '123'
    +      })
    +      .end((err, res) => {
    +        res.should.be.json;
    +        res.should.have.status(200);
    +        done();
    +      })
    +  })
    +})
    +
    +describe('Testing get a user', () => {
    +  let username = 'isumi'
    +  it('should return data one user from database', (done) => {
    +    chai.request('http://localhost:3000')
    +      .get('/user/'+username)
    +      .end((err, res) => {
    +        res.should.be.json;
    +        res.should.have.status(200);
    +        res.body.username.should.equal('isumi')
    +        done();
    +      })
    +  })
    +})
    +
    +describe('Testing update a user', () => {
    +  let username = 'isumi'
    +  it('should update data to the database', (done) => {
    +    chai.request('http://localhost:3000')
    +      .put('/user/'+username)
    +      .send({
    +        username: 'ina',
    +        email: 'isumi@hacktiv8.com',
    +        password: '123'
    +      })
    +      .end((err, res) => {
    +        res.should.be.json;
    +        res.should.have.status(200);
    +        res.body.username.should.equal('ina');
    +        res.body.email.should.equal('isumi@hacktiv8.com');
    +        res.body.password.should.equal('123');
    +        done();
    +      })
    +  })
    +})
    +
    +describe('Testing delete a user', () => {
    +  let username = 'ina'
    +  it('should delete a user to the database', (done) => {
    +    chai.request('http://localhost:3000')
    +      .delete('/user/'+username)
    +      .send({
    +        username: 'ina',
    +        email: 'isumi@hacktiv8.com',
    +        password: '123'
    +      })
    +      .end((err, res) => {
    +        res.should.be.json;
    +        res.should.have.status(200);
    +        res.body.username.should.equal('ina');
    +        res.body.email.should.equal('isumi@hacktiv8.com');
    +        res.body.password.should.equal('123');
    +        done();
    +      })
    +  })
    +})
    diff --git a/views/article.html b/views/article.html
    new file mode 100644
    index 0000000..5369992
    --- /dev/null
    +++ b/views/article.html
    @@ -0,0 +1,157 @@
    +
    +
    +  
    +    
    +    
    +    
    +    
    +    
    +    
    +  
    +
    +  
    +    
    +    
    +    
    +
    +
    +
    +
    +
    + +

    Title

    +

    Lorem ipsum dolor sit amet, consectetur adipisici elit, +
    sed eiusmod tempor incidunt ut labore et dolore magna aliqua. +
    Ut enim ad minim veniam, quis nostrud

    +
    +
    + +

    Title

    +

    Lorem ipsum dolor sit amet, consectetur adipisici elit, +
    sed eiusmod tempor incidunt ut labore et dolore magna aliqua. +
    Ut enim ad minim veniam, quis nostrud

    +
    +
    + +

    Title

    +

    Lorem ipsum dolor sit amet, consectetur adipisici elit, +
    sed eiusmod tempor incidunt ut labore et dolore magna aliqua. +
    Ut enim ad minim veniam, quis nostrud

    +
    +
    +
    +
    +
    +
      +
    • + Prev +
    • +
    • + 1 +
    • +
    • + 2 +
    • +
    • + 3 +
    • +
    • + 4 +
    • +
    • + 5 +
    • +
    • + 6 +
    • +
    • + 7 +
    • +
    • + Next +
    • +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Together We Can!

    +

    Lorem ipsum dolor sit amet, consectetur adipisici elit, +
    sed eiusmod tempor incidunt ut labore et dolore magna aliqua. +
    Ut enim ad minim veniam, quis nostrud

    +
    +
    +

    +
    +
    +

    +
    + +
    +
    + +
    +
    +
    +
    +
    + + + diff --git a/views/index.html b/views/index.html index 055be99..bfc2dc2 100644 --- a/views/index.html +++ b/views/index.html @@ -4,7 +4,7 @@ My CSS Framework! - + @@ -30,7 +30,7 @@