From 9b23edb60ae797a6ec4ef3ea70ca1e2911982783 Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Sat, 12 Oct 2019 09:21:11 +0100 Subject: [PATCH] Updated ESLint and JSDoc Exercise The exercise was unclear. Exercise is now based on the todo files which have been updated --- .eslintrc.json | 1 + .gitignore | 1 + 06 Code Quality.md | 34 +++--- exercises/06_code_quality/linter/index.js | 107 ++++-------------- .../linter/jest-test.config.js | 20 ++++ exercises/06_code_quality/linter/jsdoc.conf | 22 ---- .../linter/modules/accounts.js | 60 ---------- .../06_code_quality/linter/modules/todo.js | 26 +++++ exercises/06_code_quality/linter/package.json | 48 +++----- .../06_code_quality/linter/public/style.css | 11 ++ exercises/06_code_quality/linter/readme.md | 6 + .../linter/unit tests/todo.spec.js | 59 ++++++++++ .../06_code_quality/linter/views/empty.hbs | 19 ++++ .../06_code_quality/linter/views/home.hbs | 33 ++++++ exercises/06_code_quality/todo/index.js | 98 ++++++++-------- .../06_code_quality/todo/modules/list.js | 73 +++++++----- exercises/06_code_quality/todo/package.json | 5 +- .../06_code_quality/unit tests/user.spec.js | 76 ------------- .../06_code_quality/views/error.handlebars | 16 --- .../06_code_quality/views/index.handlebars | 16 --- .../06_code_quality/views/login.handlebars | 24 ---- .../06_code_quality/views/register.handlebars | 21 ---- 22 files changed, 330 insertions(+), 446 deletions(-) create mode 100644 exercises/06_code_quality/linter/jest-test.config.js delete mode 100644 exercises/06_code_quality/linter/jsdoc.conf delete mode 100644 exercises/06_code_quality/linter/modules/accounts.js create mode 100644 exercises/06_code_quality/linter/modules/todo.js create mode 100644 exercises/06_code_quality/linter/public/style.css create mode 100644 exercises/06_code_quality/linter/readme.md create mode 100644 exercises/06_code_quality/linter/unit tests/todo.spec.js create mode 100644 exercises/06_code_quality/linter/views/empty.hbs create mode 100644 exercises/06_code_quality/linter/views/home.hbs delete mode 100644 exercises/06_code_quality/unit tests/user.spec.js delete mode 100644 exercises/06_code_quality/views/error.handlebars delete mode 100644 exercises/06_code_quality/views/index.handlebars delete mode 100644 exercises/06_code_quality/views/login.handlebars delete mode 100644 exercises/06_code_quality/views/register.handlebars diff --git a/.eslintrc.json b/.eslintrc.json index 3fb6a540..fb7e68bd 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -53,6 +53,7 @@ "no-var": 2, "prefer-arrow-callback": 1, "prefer-const": 2, + "prefer-template": "error", "quotes": [1, "single"], "semi": [1, "never"], "space-before-blocks": ["error", { "functions": "always", "keywords": "always", "classes": "always" }], diff --git a/.gitignore b/.gitignore index 008f8e67..6dcdeeca 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ coverage/ docs/ sessions/ screenshots/* +out/ # sqlite databases # *.db diff --git a/06 Code Quality.md b/06 Code Quality.md index a65ca43d..5972de6a 100644 --- a/06 Code Quality.md +++ b/06 Code Quality.md @@ -56,7 +56,7 @@ The custom object prototype defined in the `list.js` module already contains the Now much of the business logic has been moved to the separate module, are there any module imports in `index.js` that are no longer needed? Locate these and delete. -## 2 Linting +## 3 Linting When using a language as flexible as JavaScript which contains so many legal (but terrible) features, it is important to use a linter. This will check your code against a set of rules. These ensure: @@ -64,14 +64,14 @@ When using a language as flexible as JavaScript which contains so many legal (bu 2. You are implementing optional syntax (such as indentation and semicolons) in a consistent manner. 3. You are writing code that is easy to maintain. -If you look over both your `index.js` and `accounts.js` files you should be feeling pretty comfortable that you are already writing clean, consistent and maintainable code, lets see how good your code really is! +Locate the `06_code_quality/todo/` directory and study the `index.js` file. You should be feeling pretty comfortable that you are looking at clean, consistent and maintainable code, lets see how good your code really is! -You should start by ensuring you have installed `eslint` which is considered the industry standard and that you have a copy of the approved configuration file `.eslintrc.json` in the root directory of your project. You can find this in the `TEACHING-MATERIALS` repository but make sure you take a copy of the latest version from the master repository! +You should start by ensuring you have installed `eslint` which is considered the industry standard and that you have a copy of the approved configuration file `.eslintrc.json` in the root directory of your project. -Try running the linter on your `index.js` routes file: +Now navigate to the `06_code_quality/` directory using terminal and run the linter on the `index.js` code in the `todo/` directory: ```shell -$ node_modules/.bin/eslint index.js +$ node_modules/.bin/eslint todo/index.js ``` You will see a list of issues that the linter has flagged in your code. Notice that some of these are flagged as errors (serious) and some as warnings (recommendations). Each message includes: @@ -85,38 +85,38 @@ The latter can be used to quickly look up the rules in the [comprehensive docume Instead of running separate checks on every file, we can specify the directory we want to check and it will automatically scan all the subdirectories. For example to scan all the files in the `modules/` directory we could run: ```shell -$ node_modules/.bin/eslint modules/ +$ node_modules/.bin/eslint todo/ ``` -### 2.1 Test Your Understanding +### 3.1 Test Your Understanding -1. How could you run the linter to scan _all_ the files in your project (HINT: you need to start scanning in the _current directory_)? -2. Now you should locate and fix all the errors and warnings in your code. -3. If you are using VS Code, install `eslint` globally and then install the [eslint extension](https://github.com/Microsoft/vscode-eslint). After restarting your editor you should see any errors and warnings flagged in the editor. +1. Locate and fix all the errors and warnings in the `index.js`. +2. Install `eslint` globally and then install the [eslint extension](https://github.com/Microsoft/vscode-eslint). After restarting your editor you should see any errors and warnings flagged in the editor. +3. Use the `eslint` plugin to locate and fix all the errors in the `modules/list.js` file. -## 3 Documentation +## 4 Documentation In this third and last topic we will be using the [JSDoc](http://usejsdoc.org) tool to build a detailed code documentation website by extracting special comments inserted into our source code. The default set of documentation tools provided in JSDoc are not suitable for documenting Koa routes and so we will be using a plugin called [jsdoc-route-plugin](https://www.npmjs.com/package/jsdoc-route-plugin). This should have been installed by the package manifest however you should check that you are using the current version of the `package.json` file and update if needed, rerunning the `npm install` command to ensure all packages are installed. You should also check that you have the latest version of the `jsdoc.conf` configuration file. -Now everything is installed we can run the `jsdoc` tool to generate our documentation. +Now everything is installed we can run the `jsdoc` tool on the project in the `todo/` directory which will generate our documentation. The `-d` destination flag tells the tool where to save the documentation. ```shell -$ node_modules/.bin/jsdoc +$ ./node_modules/.bin/jsdoc -d ./docs/jsdoc/ modules/* ``` If you run this command you should see a new directory called `docs/` which will contain a `jsdoc/` directory. Inside this you will see some website files, opening the `index.html` file in your browser you should see the documentation pages for your website! -### 3.1 Test Your Understanding +### 4.1 Test Your Understanding You will probably have noticed that only a couple of the functions include complete JSDoc comments and so the documentation website is incomplete. Your task is to use the existing comments for guidance and complete the task of documenting your code. You will find the [JSDoc](http://usejsdoc.org) and [jsdoc-route-plugin](https://www.npmjs.com/package/jsdoc-route-plugin) documentation helpful. -## 4 Improved Async Code +## 5 Improved Async Code Since NodeJS has a single thread that handles all incoming requests it is vital that we push long-running tasks into their own threads, typically through the use of _callback functions_. In this section of the lab you will learn about the limitations of callbacks and explore more powerful ways to handle multi-threading. -## 4.1 Nested Callbacks +## 5.1 Nested Callbacks Because the code to be run after a callback is run needs to be _inside_ the callback code it is very challenging to build a script that contains several long-running tasks you get into a situation where you nest callbacks inside callbacks (inside callbacks) which makes the code very difficult to write, debug and read and means its very difficult to split into separate functions, a situation commonly known as **Callback Hell**. @@ -129,7 +129,7 @@ Open the file `nestedCallbacks.js` which asks for a _base_ currency code then pr Callbacks are the simplest possible mechanism for asynchronous code in JavaScript. Unfortunately, raw callbacks sacrifice the control flow, exception handling, and function semantics familiar from synchronous code. -### 4.1 Test Your Knowledge +### 5.2 Test Your Knowledge The callbacks are already nested 3 deep. To test your knowledge of deeply nested callbacks you are going to create a script that has 6 levels of nested callbacks! diff --git a/exercises/06_code_quality/linter/index.js b/exercises/06_code_quality/linter/index.js index 43c2fcd1..86e1c6e0 100644 --- a/exercises/06_code_quality/linter/index.js +++ b/exercises/06_code_quality/linter/index.js @@ -1,115 +1,56 @@ #!/usr/bin/env node -/** - * Routes File - */ - 'use strict' -/* MODULE IMPORTS */ -const bcrypt = require('bcrypt-promise') const Koa = require('koa') const Router = require('koa-router') -const views = require('koa-views') -const staticDir = require('koa-static') +const stat = require('koa-static') const bodyParser = require('koa-bodyparser') -const koaBody = require('koa-body')({multipart: true, uploadDir: '.'}) -const session = require('koa-session') -const sqlite = require('sqlite-async') -const fs = require('fs-extra') -const mime = require('mime-types') -//const jimp = require('jimp') - -/* IMPORT CUSTOM MODULES */ -const User = require('./modules/accounts') +const handlebars = require('koa-hbs-renderer') const app = new Koa() const router = new Router() - -/* CONFIGURING THE MIDDLEWARE */ -app.keys = ['darkSecret'] -app.use(staticDir('public')) +app.use(stat('public')) app.use(bodyParser()) -app.use(session(app)) -app.use(views(`${__dirname}/views`, { extension: 'handlebars' }, {map: { handlebars: 'handlebars' }})) +app.use(handlebars({ paths: { views: `${__dirname}/views` } })) +app.use(router.routes()) + +const port = 8080 -const defaultPort = 8080 -const port = process.env.PORT || defaultPort -const dbName = 'website.db' -const saltRounds = 10 +const todo = require('./modules/todo') -/** - * The secure home page. - * - * @name Home Page - * @route {GET} / - * @authentication This route requires cookie-based authentication. - */ router.get('/', async ctx => { try { - if(ctx.session.authorised !== true) return ctx.redirect('/login?msg=you need to log in') const data = {} if(ctx.query.msg) data.msg = ctx.query.msg - await ctx.render('index') + data.items = todo.getAll() + ctx.render('home', data) } catch(err) { - await ctx.render('error', {message: err.message}) + console.log(err.message) + ctx.render('home', {msg: err.message}) } }) -/** - * The user registration page. - * - * @name Register Page - * @route {GET} /register - */ -router.get('/register', async ctx => await ctx.render('register')) - -/** - * The script to process new user registrations. - * - * @name Register Script - * @route {POST} /register - */ -router.post('/register', koaBody, async ctx => { +router.post('/', ctx => { try { - // extract the data from the request const body = ctx.request.body - console.log(body) - const {path, type} = ctx.request.files.avatar - // call the functions in the module - const user = await new User(dbName) - await user.register(body.user, body.pass) - // await user.uploadPicture(path, type) - // redirect to the home page - ctx.redirect(`/?msg=new user "${body.name}" added`) + todo.add(body.item, body.qty) + ctx.redirect('/') } catch(err) { - await ctx.render('error', {message: err.message}) + console.log(err.message) + ctx.redirect(`/?msg=${err.message}`) } }) -router.get('/login', async ctx => { - const data = {} - if(ctx.query.msg) data.msg = ctx.query.msg - if(ctx.query.user) data.user = ctx.query.user - await ctx.render('login', data) -}) - -router.post('/login', async ctx => { +router.get('/delete/:key', ctx => { try { - const body = ctx.request.body - const user = await new User(dbName) - await user.login(body.user, body.pass) - ctx.session.authorised = true - return ctx.redirect('/?msg=you are now logged in...') + console.log(`key: ${ctx.params.key}`) + todo.delete(ctx.params.key) + ctx.redirect('/msg=item deleted') } catch(err) { - await ctx.render('error', {message: err.message}) + console.log(err.message) + ctx.redirect(`/${err.message}`) } }) -router.get('/logout', async ctx => { - ctx.session.authorised = null - ctx.redirect('/?msg=you are now logged out') -}) - -app.use(router.routes()) -module.exports = app.listen(port, async() => console.log(`listening on port ${port}`)) +module.exports = app.listen(port, () => console.log(`listening on port ${port}`)) diff --git a/exercises/06_code_quality/linter/jest-test.config.js b/exercises/06_code_quality/linter/jest-test.config.js new file mode 100644 index 00000000..02243478 --- /dev/null +++ b/exercises/06_code_quality/linter/jest-test.config.js @@ -0,0 +1,20 @@ + +'use strict'; + +module.exports = { + displayName: 'test', + verbose: true, + collectCoverage: true, + coverageThreshold: { + global: { + branches: 0, + functions: 0, + lines: 0, + statements: 0 + } + }, + testPathIgnorePatterns: [ + '/node_modules/', + '/__tests__/fixtures/', + ] +} diff --git a/exercises/06_code_quality/linter/jsdoc.conf b/exercises/06_code_quality/linter/jsdoc.conf deleted file mode 100644 index 9fa536f2..00000000 --- a/exercises/06_code_quality/linter/jsdoc.conf +++ /dev/null @@ -1,22 +0,0 @@ - -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc","closure"] - }, - "source": { - "include": [ "." ], - "exclude": [ "node_modules" ], - "includePattern": ".+\\.js(doc|x)?$", - "excludePattern": "(^|\\/|\\\\)_" - }, - "plugins": ["jsdoc-route-plugin"], - "templates": { - "cleverLinks": false, - "monospaceLinks": false - }, - "opts": { - "destination": "docs/jsdoc", - "recurse": true - } -} diff --git a/exercises/06_code_quality/linter/modules/accounts.js b/exercises/06_code_quality/linter/modules/accounts.js deleted file mode 100644 index fd1c7ec4..00000000 --- a/exercises/06_code_quality/linter/modules/accounts.js +++ /dev/null @@ -1,60 +0,0 @@ - -'use strict' - -const bcrypt = require('bcrypt-promise') -const fs = require('fs-extra') -const mime = require('mime-types') -const sqlite = require('sqlite-async') -const saltRounds = 10 - -module.exports = class User { - - constructor(dbName = ':memory:') { - return (async() => { - this.db = await sqlite.open(dbName) - // we need this table to store the user accounts - const sql = 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT, pass TEXT);' - await this.db.run(sql) - return this - })() - } - - async register(user, pass) { - try { - if(user.length === 0) throw new Error('missing username') - if(pass.length === 0) throw new Error('missing password') - let sql = `SELECT COUNT(id) as records FROM users WHERE user="${user}";` - const data = await this.db.get(sql) - if(data.records !== 0) throw new Error(`username "${user}" already in use`) - pass = await bcrypt.hash(pass, saltRounds) - sql = `INSERT INTO users(user, pass) VALUES("${user}", "${pass}")` - await this.db.run(sql) - return true - } catch(err) { - throw err - } - } - - async uploadPicture(path, mimeType) { - const extension = mime.extension(mimeType) - console.log(`path: ${path}`) - console.log(`extension: ${extension}`) - //await fs.copy(path, `public/avatars/${username}.${fileExtension}`) - } - - async login(username, password) { - try { - let sql = `SELECT count(id) AS count FROM users WHERE user="${username}";` - const records = await this.db.get(sql) - if(!records.count) throw new Error(`username "${username}" not found`) - sql = `SELECT pass FROM users WHERE user = "${username}";` - const record = await this.db.get(sql) - const valid = await bcrypt.compare(password, record.pass) - if(valid === false) throw new Error(`invalid password for account "${username}"`) - return true - } catch(err) { - throw err - } - } - -} \ No newline at end of file diff --git a/exercises/06_code_quality/linter/modules/todo.js b/exercises/06_code_quality/linter/modules/todo.js new file mode 100644 index 00000000..f4fdd7b8 --- /dev/null +++ b/exercises/06_code_quality/linter/modules/todo.js @@ -0,0 +1,26 @@ + +'use strict' + +let data = [] + +module.exports.clear = () => { + data = [] +} + +module.exports.add = (item, qty) => { + qty = Number(qty) + if(isNaN(qty)) throw new Error('qty must be a number') + data.push({item: item, qty: qty}) +} + +module.exports.getAll = () => { + for(const key in data) data[key].key = key + return data +} + +module.exports.delete = key => { + console.log(`delete key ${key}`) + return +} + +module.exports.countItems = () => data.length diff --git a/exercises/06_code_quality/linter/package.json b/exercises/06_code_quality/linter/package.json index 6eb0a08d..8b83c896 100644 --- a/exercises/06_code_quality/linter/package.json +++ b/exercises/06_code_quality/linter/package.json @@ -1,50 +1,32 @@ { - "name": "10_auth", + "name": "todo", "version": "1.0.0", "description": "", "main": "index.js", - "engines": { - "node": "12.x" - }, "scripts": { - "start": "node index.js", - "acceptance": "jest --coverage --detectOpenHandles", - "jsdoc": "node_modules/.bin/jsdoc -c jsdoc.conf", "linter": "node_modules/.bin/eslint .", - "test": "jest --coverage --detectOpenHandles", - "unit": "node_modules/.bin/jest --coverage --runInBand tests/unit/" - }, - "jest": { - "projects": [ - "/jest-test.config.js" - ] + "test": "node_modules/.bin/jest --coverage --runInBand" }, "author": "", "license": "ISC", "dependencies": { - "bcrypt": "^3.0.6", - "bcrypt-promise": "^2.0.0", - "fs-extra": "^7.0.1", - "handlebars": "^4.1.2", - "koa": "^2.6.2", - "koa-body": "^4.0.8", + "handlebars": "^4.2.0", + "http-status-codes": "^1.3.2", + "koa": "^2.8.1", "koa-bodyparser": "^4.2.1", + "koa-handlebars": "^1.0.0", + "koa-hbs-renderer": "^1.2.0", "koa-router": "^7.4.0", - "koa-session": "^5.10.1", "koa-static": "^5.0.0", - "koa-views": "^6.1.5", - "mime-types": "^2.1.22", - "readline": "^1.3.0", - "readline-sync": "^1.4.10", - "request": "^2.88.0", - "sqlite-async": "^1.0.11" + "koa-views": "^6.2.1" }, "devDependencies": { - "eslint": "^5.15.2", - "handlebars-validate": "^0.1.2", - "http-status-codes": "^1.3.2", - "jest": "^24.1.0", - "jsdoc": "^3.6.3", - "jsdoc-route-plugin": "^0.1.0" + "jest": "^24.9.0", + "supertest": "^4.0.2" + }, + "jest": { + "projects": [ + "/jest-test.config.js" + ] } } diff --git a/exercises/06_code_quality/linter/public/style.css b/exercises/06_code_quality/linter/public/style.css new file mode 100644 index 00000000..8a5442c3 --- /dev/null +++ b/exercises/06_code_quality/linter/public/style.css @@ -0,0 +1,11 @@ + +body { + font-family: Arial, Helvetica, sans-serif; +} + +.msg { + border: 1px solid red; + font-weight: bold; + color: red; + padding: 1em; +} diff --git a/exercises/06_code_quality/linter/readme.md b/exercises/06_code_quality/linter/readme.md new file mode 100644 index 00000000..915a7e3a --- /dev/null +++ b/exercises/06_code_quality/linter/readme.md @@ -0,0 +1,6 @@ + +# ToDo + +This project is designed to teach you how to write and debug unit tests and employ the Test-Driven Development (TDD) methodology in your software development. + +You should open this directory directly in VS Code, ensuring that this file is in the root of the file tree. diff --git a/exercises/06_code_quality/linter/unit tests/todo.spec.js b/exercises/06_code_quality/linter/unit tests/todo.spec.js new file mode 100644 index 00000000..55eb70db --- /dev/null +++ b/exercises/06_code_quality/linter/unit tests/todo.spec.js @@ -0,0 +1,59 @@ + +'use strict' + +const todo = require('../modules/todo.js') + +beforeAll( async() => { + // stuff to do before any of the tests run +}) + +afterAll( async() => { + // runs after all the tests have completed +}) + +describe('add()', () => { + // block of tests + beforeEach( async() => { + todo.clear() + }) + afterEach( async() => { + // runs after each test completes + }) + test('add a single item', async done => { + expect.assertions(1) + try { + todo.add('bread', 3) + expect(todo.countItems()).toBe(1) + } catch(err) { + done.fail(err) + } finally { + done() + } + }) + test('qty must be a number', async done => { + expect.assertions(1) + try { + todo.add('bread', 'three') + done.fail('test failed') + } catch(err) { + expect(err.message).toBe('qty must be a number') + } finally { + done() + } + }) + + // New test goes HERE! + +}) + +describe('delete()', () => { + // any tests for the delete() function should be written here +}) + +describe('getAll()', () => { + // any tests for the getAll() function should be written here +}) + +describe('clear()', () => { + // any tests for the clear() function should be written here +}) diff --git a/exercises/06_code_quality/linter/views/empty.hbs b/exercises/06_code_quality/linter/views/empty.hbs new file mode 100644 index 00000000..6be9593a --- /dev/null +++ b/exercises/06_code_quality/linter/views/empty.hbs @@ -0,0 +1,19 @@ + + + + + ToDo List + + + +

ToDo List

+

Your list is empty, lets add some items...

+
+ Item: + + Qty: + + +
+ + diff --git a/exercises/06_code_quality/linter/views/home.hbs b/exercises/06_code_quality/linter/views/home.hbs new file mode 100644 index 00000000..1a9495f0 --- /dev/null +++ b/exercises/06_code_quality/linter/views/home.hbs @@ -0,0 +1,33 @@ + + + + + ToDo List + + + +

ToDo List

+

My List

+ {{#if msg}} +

{{msg}}

+ {{/if}} + {{#if items.length}} + + + + {{#each items}} + + {{/each}} +
Shopping List
ItemQtyAction
{{this.item}}{{this.qty}}delete
+ {{else}} +

Your list is empty, lets add some items...

+ {{/if}} +
+ Item: + + Qty: + + +
+ + diff --git a/exercises/06_code_quality/todo/index.js b/exercises/06_code_quality/todo/index.js index 914ec5bf..111255db 100644 --- a/exercises/06_code_quality/todo/index.js +++ b/exercises/06_code_quality/todo/index.js @@ -1,61 +1,57 @@ #!/usr/bin/env node -'use strict' +var Koa = require("koa") +var Router = require("koa-router"); +var stat = require("koa-static"); +var bodyParser = require("koa-bodyparser"); +var handlebars = require("koa-hbs-renderer"); -const Koa = require('koa') -const Router = require('koa-router') -const stat = require('koa-static') -const bodyParser = require('koa-bodyparser') -const handlebars = require('koa-hbs-renderer') +var app = new Koa(); +var router = new Router(); +app.use(stat ("public")); +app.use(bodyParser()); +app.use(handlebars({ paths: { views: __dirname + "/views" } })); +app.use(router.routes()); -const app = new Koa() -const router = new Router() -app.use(stat('public')) -app.use(bodyParser()) -app.use(handlebars({ paths: { views: `${__dirname}/views` } })) -app.use(router.routes()) -const port = 8080 +var port = 8080 -const items = [] +var items = [] // const List = require('./modules/list') // const list = new List() -router.get('/', async ctx => { - try { - const data = {} - if(ctx.query.msg) data.msg = ctx.query.msg - data.items = items.map( (element, index) => ({key: index, item: element.item, qty: element.qty})) - console.log(data.items) - ctx.render('home', data) - } catch(err) { - console.log(err.message) - ctx.render('home', {msg: err.message}) - } -}) - -router.post('/', ctx => { - try { - const body = ctx.request.body - const data = {item: body.item, qty: body.qty} - items.push(data) - ctx.redirect('/') - } catch(err) { - console.log(err.message) - ctx.redirect(`/?msg=${err.message}`) - } -}) - -router.get('/delete/:key', ctx => { - try { - console.log(`key: ${ctx.params.key}`) - items.splice(ctx.params.key, 1) - ctx.redirect('/msg=item deleted') - } catch(err) { - console.log(err.message) - ctx.redirect(`/${err.message}`) - } -}) - -module.exports = app.listen(port, () => console.log(`listening on port ${port}`)) +router.get("/", async function(ctx) { + try { + var data = {} + } catch(err) { + console.log(err.message); + ctx.render('home', {msg: err.message}); + } +}); + +router.post("/", function(ctx) { + try { + var body = ctx.request.body; + var data = {item: body.item, qty: body.qty}; + items.push(data); + ctx.redirect("/"); + } catch(err) { + console.log(err.message); + ctx.redirect("/?msg=" + err.message); + } +}); + +router.get("/delete/:key", function(ctx) { + try { + console.log(`key: ${ctx.params.key}`); + items.splice(ctx.params.key, 1); + ctx.redirect('/msg=item deleted'); + } catch(err) { + console.log(err.message); + ctx.redirect("/" + err.message); + } +}); + +module.exports = app.listen(port, function() { +}); \ No newline at end of file diff --git a/exercises/06_code_quality/todo/modules/list.js b/exercises/06_code_quality/todo/modules/list.js index 0849e064..103f77e6 100644 --- a/exercises/06_code_quality/todo/modules/list.js +++ b/exercises/06_code_quality/todo/modules/list.js @@ -1,27 +1,48 @@ -'use strict' - -module.exports = class List { - - constructor() { - this.items = [] - } - - add(item, qty) { - const data = {item: item, qty: qty} - this.items.push(data) - } - - getAll() { - return this.items.map( (element, index) => ({key: index, item: element.item, qty: element.qty})) - } - - delete(id) { - this.items.splice(id, 1) - } - - count() { - return this.items.count - } - -} +/** + * Class representing a list of items. + * */ +module.exports = class List { + + /** + * Create a list. + */ + constructor(){ + this.items = []; + } + + /** + * Add an item to the list. + * @param {String} item - The name of the eitem. + * @param {Number} qty - The number of items to add. + */ + add(item, qty) { + var data = {item: item, qty: qty}; + this.items.push(data); + } + + /** + * Return the list of items. + * @return {Array.<{item: String, qty: Number}>} An array containing the items. + */ + getAll(){ + return this.items.map( (element, index) => ({key: index, item: element.item, qty: element.qty})); + } + + /** + * Delete a single item. + * @param {Number} id - The index to be deleted. + */ + delete(id){ + this.items.splice(id, 1); + } + + /** + * Return the number of items in the list. + * @return {Number} The number of items. + */ + count(){ + return this.items.count; + } + +} \ No newline at end of file diff --git a/exercises/06_code_quality/todo/package.json b/exercises/06_code_quality/todo/package.json index e3637641..f8b39ff3 100644 --- a/exercises/06_code_quality/todo/package.json +++ b/exercises/06_code_quality/todo/package.json @@ -18,5 +18,8 @@ "koa-static": "^5.0.0", "koa-views": "^6.2.1" }, - "devDependencies": {} + "devDependencies": { + "eslint": "^6.5.1", + "jsdoc": "^3.6.3" + } } diff --git a/exercises/06_code_quality/unit tests/user.spec.js b/exercises/06_code_quality/unit tests/user.spec.js deleted file mode 100644 index 886d3440..00000000 --- a/exercises/06_code_quality/unit tests/user.spec.js +++ /dev/null @@ -1,76 +0,0 @@ - -'use strict' - -const Accounts = require('../modules/user.js') - -describe('register()', () => { - - test('register a valid account', async done => { - expect.assertions(1) - const account = await new Accounts() - const register = await account.register('doej', 'password') - expect(register).toBe(true) - done() - }) - - test('register a duplicate username', async done => { - expect.assertions(1) - const account = await new Accounts() - await account.register('doej', 'password') - await expect( account.register('doej', 'password') ) - .rejects.toEqual( Error('username "doej" already in use') ) - done() - }) - - test('error if blank username', async done => { - expect.assertions(1) - const account = await new Accounts() - await expect( account.register('', 'password') ) - .rejects.toEqual( Error('missing username') ) - done() - }) - - test('error if blank password', async done => { - expect.assertions(1) - const account = await new Accounts() - await expect( account.register('doej', '') ) - .rejects.toEqual( Error('missing password') ) - done() - }) - -}) - -describe('uploadPicture()', () => { - // this would have to be done by mocking the file system - // perhaps using mock-fs? -}) - -describe('login()', () => { - test('log in with valid credentials', async done => { - expect.assertions(1) - const account = await new Accounts() - await account.register('doej', 'password') - const valid = await account.login('doej', 'password') - expect(valid).toBe(true) - done() - }) - - test('invalid username', async done => { - expect.assertions(1) - const account = await new Accounts() - await account.register('doej', 'password') - await expect( account.login('roej', 'password') ) - .rejects.toEqual( Error('username "roej" not found') ) - done() - }) - - test('invalid password', async done => { - expect.assertions(1) - const account = await new Accounts() - await account.register('doej', 'password') - await expect( account.login('doej', 'bad') ) - .rejects.toEqual( Error('invalid password for account "doej"') ) - done() - }) - -}) diff --git a/exercises/06_code_quality/views/error.handlebars b/exercises/06_code_quality/views/error.handlebars deleted file mode 100644 index 11fe16d0..00000000 --- a/exercises/06_code_quality/views/error.handlebars +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - ERROR - - - - - -

An Error Has Occurred

-

{{message}}

- - diff --git a/exercises/06_code_quality/views/index.handlebars b/exercises/06_code_quality/views/index.handlebars deleted file mode 100644 index 5d9be040..00000000 --- a/exercises/06_code_quality/views/index.handlebars +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Home Page - - - - - -

Home

-

This is a secure page. Users need to have a valid account and be logged in to see it.

-

log out

- diff --git a/exercises/06_code_quality/views/login.handlebars b/exercises/06_code_quality/views/login.handlebars deleted file mode 100644 index e42e65bd..00000000 --- a/exercises/06_code_quality/views/login.handlebars +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - Log In - - - - - -

Log In

- {{#if msg}} -

{{msg}}

- {{/if}} -
-

Username:

-

Password:

-

-
-

register

- - diff --git a/exercises/06_code_quality/views/register.handlebars b/exercises/06_code_quality/views/register.handlebars deleted file mode 100644 index 2e543b83..00000000 --- a/exercises/06_code_quality/views/register.handlebars +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Create an Account - - - - - -

Create an Account

-
-

Username:

-

Password:

-

Profile Picture:

-

-
- -