diff --git a/src/app.js b/src/app.js index 4b3d7bb..f7c2495 100644 --- a/src/app.js +++ b/src/app.js @@ -29,12 +29,13 @@ module.exports = (config) => { * @param callback */ const reload = (callback) => { - fw.fetchArticles((err, list) => { + fw.fetchArticles((err, dict) => { if (err) { callback(false); return console.error(cons.error, 'loading articles : ' + err); } - articles.splice(0, articles.length, ...list); + Object.keys(articles).forEach((key) => delete articles[key]); + Object.keys(dict).forEach((key) => articles[key] = dict[key]); const nb = Object.keys(articles).length; if (nb > 0) console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`); @@ -49,15 +50,15 @@ module.exports = (config) => { /** * Render the page with the view engine and catch errors * @param res - * @param path - path of the view + * @param vPath - path of the view * @param data - data to pass to the view * @param code - code to send along the page */ - const render = (res, path, data, code = 200) => { - res.render(path, data, (err, html) => { + const render = (res, vPath, data, code = 200) => { + res.render(vPath, data, (err, html) => { if (err) { res.sendStatus(500); - console.log(cons.error, `failed to render ${path} : ${err}`); + console.log(cons.error, `failed to render ${vPath} : ${err}`); } else res.status(code).send(html); }); @@ -92,21 +93,24 @@ module.exports = (config) => { // catch all article urls and render them app.get('*', (req, res, next) => { - if (req.path.test(/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/)) { - const articlePath = req.path.substr(0, 11); + if (/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/.test(req.path)) { + const articlePath = req.path.substr(1, 10); const article = articles[articlePath]; if (!article) showError(req.path, 404, res); else { - renderer.render(article.realPath, (err, html) => { - if (err) + renderer.render(path.join(article.realPath, config['article']['index']), (err, html) => { + if (err) { + console.log(cons.error, `failed to render article ${req.path} : ${err}`); return showError(req.path, 500, res); + } article.content = html; - const templatePath = path.join(config['data_dir'], config['home']['index']); + const templatePath = path.join(config['data_dir'], config['article']['template']); fs.access(templatePath, fs.constants.R_OK, (err) => { - if (err) - showError(req.path, 404, res); - else + if (err) { + console.log(cons.error, `no template found at ${templatePath}`); + showError(req.path, 500, res); + } else render(res, templatePath, {article: article}); }); }); @@ -136,6 +140,12 @@ module.exports = (config) => { res.status(400).send('bad request'); }); + app.use((err, req, res, next) => { + console.log(cons.error, `error when handling ${req.path} request : ${err}`); + console.error(err.stack); + next(err); + }); + // must be use in a server.js to start the server app.start = () => { reload((res) => { diff --git a/src/file_walker.js b/src/file_walker.js index ac30e12..d336b7d 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -75,7 +75,7 @@ module.exports = (config) => { .map((path) => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) .filter((matches) => matches && matches.length > 1); if (paths.length === 0) - cb(null, []); + cb(null, {}); const articles = {}; let remaining = 0; paths.forEach((matches) => { @@ -94,8 +94,8 @@ module.exports = (config) => { article.title = info.title || config['article']['default_title']; article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail']; article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); - article.url = path.join(article.path, article.escapedTitle)+'/'; - articles[article.path]=article; + article.url = '/' + path.join(article.path, article.escapedTitle) + '/'; + articles[article.path] = article; remaining--; if (remaining === 0) cb(null, articles); diff --git a/test/app.test.js b/test/app.test.js index ae3b5de..ccc5953 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -7,6 +7,7 @@ const utils = require('./test_utils'); const dataDir = 'test_data'; const testIndex = 'testindex.ejs'; const testError = 'testerror.ejs'; +const testTemplate = 'testtemplate.ejs'; const config = { 'test': true, @@ -19,10 +20,12 @@ const config = { }, 'article': { 'index': 'index.md', + 'template' : testTemplate, 'thumbnail_tag': 'thumbnail', 'default_title': 'Untitled', 'default_thumbnail': null }, + 'showdown' : {} }; const app = require('../src/app')(config); @@ -83,6 +86,72 @@ describe('Test root path', () => { }); }); +describe('Test articles rendering', () => { + test('404 article not found', (done) =>{ + request(app).get('/2019/05/06/untitled/').then((response) =>{ + expect(response.statusCode).toBe(404); + done(); + }); + }); + + test('500 no template', (done) =>{ + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05','index.md'), '# Hello'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/hello/').then((response) =>{ + expect(response.statusCode).toBe(500); + done(); + }); + }); + }); + + test('200 rendered article', (done) =>{ + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05','index.md'), '# Hello'); + fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/hello/').then((response) =>{ + expect(response.statusCode).toBe(200); + expect(response.text).toBe('