From 79a48cec5f441712963cede1f23577e379d95ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 25 May 2017 20:01:07 -0400 Subject: [PATCH] controllers/category tests --- src/categories.js | 16 +-- src/categories/create.js | 2 +- src/controllers/category.js | 159 ++++++++++----------- test/controllers.js | 269 +++++++++++++++++++++++++++++++++++- 4 files changed, 353 insertions(+), 93 deletions(-) diff --git a/src/categories.js b/src/categories.js index 815049ad4c..e6f0f5aa57 100644 --- a/src/categories.js +++ b/src/categories.js @@ -271,16 +271,14 @@ var privileges = require('./privileges'); Categories.flattenCategories = function (allCategories, categoryData) { categoryData.forEach(function (category) { - if (!category) { - return; - } - - if (!category.parent) { - allCategories.push(category); - } + if (category) { + if (!category.parent) { + allCategories.push(category); + } - if (Array.isArray(category.children) && category.children.length) { - Categories.flattenCategories(allCategories, category.children); + if (Array.isArray(category.children) && category.children.length) { + Categories.flattenCategories(allCategories, category.children); + } } }); }; diff --git a/src/categories/create.js b/src/categories/create.js index eb64f91995..faae87a610 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -37,7 +37,7 @@ module.exports = function (Categories) { post_count: 0, disabled: 0, order: order, - link: '', + link: data.link || '', numRecentReplies: 1, class: (data.class ? data.class : 'col-md-3 col-xs-6'), imageClass: 'cover', diff --git a/src/controllers/category.js b/src/controllers/category.js index 114fd8ba63..cb5b08f9a3 100644 --- a/src/controllers/category.js +++ b/src/controllers/category.js @@ -14,7 +14,7 @@ var helpers = require('./helpers'); var utils = require('../utils'); var translator = require('../translator'); -var categoryController = {}; +var categoryController = module.exports; categoryController.get = function (req, res, callback) { var cid = req.params.category_id; @@ -129,97 +129,102 @@ categoryController.get = function (req, res, callback) { if (categoryData.link) { db.incrObjectField('category:' + categoryData.cid, 'timesClicked'); - return res.redirect(categoryData.link); + return helpers.redirect(res, categoryData.link); } - var breadcrumbs = [ - { - text: categoryData.name, - url: nconf.get('relative_path') + '/category/' + categoryData.slug, - }, - ]; - helpers.buildCategoryBreadcrumbs(categoryData.parentCid, function (err, crumbs) { - if (err) { - return next(err); - } - categoryData.breadcrumbs = crumbs.concat(breadcrumbs); - next(null, categoryData); - }); + buildBreadcrumbs(categoryData, next); }, function (categoryData, next) { if (!categoryData.children.length) { return next(null, categoryData); } + var allCategories = []; categories.flattenCategories(allCategories, categoryData.children); categories.getRecentTopicReplies(allCategories, req.uid, function (err) { next(err, categoryData); }); }, - ], function (err, categoryData) { - if (err) { - return callback(err); - } - - categoryData.description = translator.escape(categoryData.description); - categoryData.privileges = userPrivileges; - categoryData.showSelect = categoryData.privileges.editable; - - res.locals.metaTags = [ - { - name: 'title', - content: categoryData.name, - }, - { - property: 'og:title', - content: categoryData.name, - }, - { - name: 'description', - content: categoryData.description, - }, - { - property: 'og:type', - content: 'website', - }, - ]; - - if (categoryData.backgroundImage) { - res.locals.metaTags.push({ - name: 'og:image', - content: categoryData.backgroundImage, + function (categoryData) { + categoryData.description = translator.escape(categoryData.description); + categoryData.privileges = userPrivileges; + categoryData.showSelect = categoryData.privileges.editable; + + addTags(categoryData, res); + + if (parseInt(req.uid, 10)) { + categories.markAsRead([cid], req.uid); + } + + categoryData['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; + categoryData.rssFeedUrl = nconf.get('relative_path') + '/category/' + categoryData.cid + '.rss'; + categoryData.title = translator.escape(categoryData.name); + pageCount = Math.max(1, Math.ceil(categoryData.topic_count / settings.topicsPerPage)); + categoryData.pagination = pagination.create(currentPage, pageCount, req.query); + categoryData.pagination.rel.forEach(function (rel) { + rel.href = nconf.get('url') + '/category/' + categoryData.slug + rel.href; + res.locals.linkTags.push(rel); }); - } - - res.locals.linkTags = [ - { - rel: 'alternate', - type: 'application/rss+xml', - href: nconf.get('url') + '/category/' + cid + '.rss', - }, - { - rel: 'up', - href: nconf.get('url'), - }, - ]; - - if (parseInt(req.uid, 10)) { - categories.markAsRead([cid], req.uid); - } - - categoryData['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1; - categoryData.rssFeedUrl = nconf.get('relative_path') + '/category/' + categoryData.cid + '.rss'; - categoryData.title = translator.escape(categoryData.name); - pageCount = Math.max(1, Math.ceil(categoryData.topic_count / settings.topicsPerPage)); - categoryData.pagination = pagination.create(currentPage, pageCount, req.query); - categoryData.pagination.rel.forEach(function (rel) { - rel.href = nconf.get('url') + '/category/' + categoryData.slug + rel.href; - res.locals.linkTags.push(rel); - }); - res.render('category', categoryData); - }); + res.render('category', categoryData); + }, + ], callback); }; +function buildBreadcrumbs(categoryData, callback) { + var breadcrumbs = [ + { + text: categoryData.name, + url: nconf.get('relative_path') + '/category/' + categoryData.slug, + }, + ]; + async.waterfall([ + function (next) { + helpers.buildCategoryBreadcrumbs(categoryData.parentCid, next); + }, + function (crumbs, next) { + categoryData.breadcrumbs = crumbs.concat(breadcrumbs); + next(null, categoryData); + }, + ], callback); +} + +function addTags(categoryData, res) { + res.locals.metaTags = [ + { + name: 'title', + content: categoryData.name, + }, + { + property: 'og:title', + content: categoryData.name, + }, + { + name: 'description', + content: categoryData.description, + }, + { + property: 'og:type', + content: 'website', + }, + ]; -module.exports = categoryController; + if (categoryData.backgroundImage) { + res.locals.metaTags.push({ + name: 'og:image', + content: categoryData.backgroundImage, + }); + } + + res.locals.linkTags = [ + { + rel: 'alternate', + type: 'application/rss+xml', + href: nconf.get('url') + '/category/' + categoryData.cid + '.rss', + }, + { + rel: 'up', + href: nconf.get('url'), + }, + ]; +} diff --git a/test/controllers.js b/test/controllers.js index 24d71337c8..7a0b0cc656 100644 --- a/test/controllers.js +++ b/test/controllers.js @@ -12,12 +12,15 @@ var user = require('../src/user'); var groups = require('../src/groups'); var meta = require('../src/meta'); var translator = require('../src/translator'); +var privileges = require('../src/privileges'); +var helpers = require('./helpers'); describe('Controllers', function () { var tid; var cid; var pid; var fooUid; + var category; before(function (done) { groups.resetCache(); @@ -41,6 +44,7 @@ describe('Controllers', function () { if (err) { return done(err); } + category = results.category; cid = results.category.cid; fooUid = results.user; @@ -586,7 +590,7 @@ describe('Controllers', function () { var uid; var jar; var csrf_token; - var helpers = require('./helpers'); + before(function (done) { user.create({ username: 'revokeme', password: 'barbar' }, function (err, _uid) { assert.ifError(err); @@ -791,7 +795,6 @@ describe('Controllers', function () { }); describe('account pages', function () { - var helpers = require('./helpers'); var jar; before(function (done) { helpers.loginUser('foo', 'barbar', function (err, _jar) { @@ -811,7 +814,7 @@ describe('Controllers', function () { }); it('should 404 if uid is not a number', function (done) { - request(nconf.get('url') + '/api/uid/test', { json: true }, function (err, res, body) { + request(nconf.get('url') + '/api/uid/test', { json: true }, function (err, res) { assert.ifError(err); assert.equal(res.statusCode, 404); done(); @@ -828,7 +831,7 @@ describe('Controllers', function () { }); it('should 404 if user does not exist', function (done) { - request(nconf.get('url') + '/api/uid/123123', { json: true }, function (err, res, body) { + request(nconf.get('url') + '/api/uid/123123', { json: true }, function (err, res) { assert.ifError(err); assert.equal(res.statusCode, 404); done(); @@ -836,7 +839,7 @@ describe('Controllers', function () { }); it('should 401 if user is not logged in', function (done) { - request(nconf.get('url') + '/api/admin', { json: true }, function (err, res, body) { + request(nconf.get('url') + '/api/admin', { json: true }, function (err, res) { assert.ifError(err); assert.equal(res.statusCode, 401); done(); @@ -844,7 +847,7 @@ describe('Controllers', function () { }); it('should 403 if user is not admin', function (done) { - request(nconf.get('url') + '/api/admin', { jar: jar, json: true }, function (err, res, body) { + request(nconf.get('url') + '/api/admin', { jar: jar, json: true }, function (err, res) { assert.ifError(err); assert.equal(res.statusCode, 403); done(); @@ -1307,6 +1310,260 @@ describe('Controllers', function () { }); }); + describe('category', function () { + var jar; + before(function (done) { + helpers.loginUser('foo', 'barbar', function (err, _jar) { + assert.ifError(err); + jar = _jar; + done(); + }); + }); + + it('should return 404 if cid is not a number', function (done) { + request(nconf.get('url') + '/api/category/fail', function (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 404); + done(); + }); + }); + + it('should return 404 if topic index is not a number', function (done) { + request(nconf.get('url') + '/api/category/' + category.slug + '/invalidtopicindex', function (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 404); + done(); + }); + }); + + it('should 404 if category does not exist', function (done) { + request(nconf.get('url') + '/api/category/123123', function (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 404); + done(); + }); + }); + + it('should 404 if category is disabled', function (done) { + categories.create({ name: 'disabled' }, function (err, category) { + assert.ifError(err); + categories.setCategoryField(category.cid, 'disabled', 1, function (err) { + assert.ifError(err); + request(nconf.get('url') + '/api/category/' + category.slug, function (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 404); + done(); + }); + }); + }); + }); + + it('should return 401 if not allowed to read', function (done) { + categories.create({ name: 'hidden' }, function (err, category) { + assert.ifError(err); + privileges.categories.rescind(['read'], category.cid, 'guests', function (err) { + assert.ifError(err); + request(nconf.get('url') + '/api/category/' + category.slug, function (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 401); + done(); + }); + }); + }); + }); + + it('should redirect if topic index is negative', function (done) { + request(nconf.get('url') + '/api/category/' + category.slug + '/-10', function (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 308); + done(); + }); + }); + + it('should 404 if page is not found', function (done) { + user.setSetting(fooUid, 'usePagination', 1, function (err) { + assert.ifError(err); + request(nconf.get('url') + '/api/category/' + category.slug + '?page=100', { jar: jar, json: true }, function (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 404); + done(); + }); + }); + }); + + it('should load page 1 if req.query.page is not sent', function (done) { + request(nconf.get('url') + '/api/category/' + category.slug, { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.equal(body.pagination.currentPage, 1); + done(); + }); + }); + + it('should sort topics by most posts', function (done) { + async.waterfall([ + function (next) { + categories.create({ name: 'most-posts-category' }, next); + }, + function (category, next) { + async.waterfall([ + function (next) { + topics.post({ uid: fooUid, cid: category.cid, title: 'topic 1', content: 'topic 1 OP' }, next); + }, + function (data, next) { + topics.post({ uid: fooUid, cid: category.cid, title: 'topic 2', content: 'topic 2 OP' }, next); + }, + function (data, next) { + topics.reply({ uid: fooUid, content: 'topic 2 reply', tid: data.topicData.tid }, next); + }, + function (postData, next) { + request(nconf.get('url') + '/api/category/' + category.slug + '?sort=most_posts', { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.equal(body.topics[0].title, 'topic 2'); + assert.equal(body.topics[0].postcount, 2); + assert.equal(body.topics[1].postcount, 1); + next(); + }); + }, + ], function (err) { + next(err); + }); + }, + ], done); + }); + + it('should load a specific users topics from a category with tags', function (done) { + async.waterfall([ + function (next) { + categories.create({ name: 'filtered-category' }, next); + }, + function (category, next) { + async.waterfall([ + function (next) { + topics.post({ uid: fooUid, cid: category.cid, title: 'topic 1', content: 'topic 1 OP', tags: ['java', 'cpp'] }, next); + }, + function (data, next) { + topics.post({ uid: fooUid, cid: category.cid, title: 'topic 2', content: 'topic 2 OP', tags: ['node', 'javascript'] }, next); + }, + function (data, next) { + topics.post({ uid: fooUid, cid: category.cid, title: 'topic 3', content: 'topic 3 OP', tags: ['java', 'cpp', 'best'] }, next); + }, + function (data, next) { + request(nconf.get('url') + '/api/category/' + category.slug + '?tag=node&author=foo', { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.equal(body.topics[0].title, 'topic 2'); + next(); + }); + }, + function (next) { + request(nconf.get('url') + '/api/category/' + category.slug + '?tag[]=java&tag[]=cpp', { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.equal(body.topics[0].title, 'topic 3'); + assert.equal(body.topics[1].title, 'topic 1'); + next(); + }); + }, + ], function (err) { + next(err); + }); + }, + ], done); + }); + + it('should redirect if category is a link', function (done) { + async.waterfall([ + function (next) { + categories.create({ name: 'redirect', link: 'https://nodebb.org' }, next); + }, + function (category, next) { + request(nconf.get('url') + '/api/category/' + category.slug, { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 308); + assert.equal(body, 'https://nodebb.org'); + next(); + }); + }, + ], done); + }); + + it('should get recent topic replies from children categories', function (done) { + var parentCategory; + var childCategory1; + var childCategory2; + + async.waterfall([ + function (next) { + categories.create({ name: 'parent category', backgroundImage: 'path/to/some/image' }, next); + }, + function (category, next) { + parentCategory = category; + async.waterfall([ + function (next) { + categories.create({ name: 'child category 1', parentCid: category.cid }, next); + }, + function (category, next) { + childCategory1 = category; + categories.create({ name: 'child category 2', parentCid: parentCategory.cid }, next); + }, + function (category, next) { + childCategory2 = category; + topics.post({ uid: fooUid, cid: childCategory2.cid, title: 'topic 1', content: 'topic 1 OP' }, next); + }, + function (data, next) { + request(nconf.get('url') + '/api/category/' + parentCategory.slug, { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.equal(body.children[1].posts[0].content, 'topic 1 OP'); + next(); + }); + }, + ], function (err) { + next(err); + }); + }, + ], done); + }); + + it('should create 2 pages of topics', function (done) { + async.waterfall([ + function (next) { + categories.create({ name: 'category with 2 pages' }, next); + }, + function (category, next) { + var titles = []; + for (var i = 0; i < 30; i++) { + titles.push('topic title ' + i); + } + + async.waterfall([ + function (next) { + async.eachSeries(titles, function (title, next) { + topics.post({ uid: fooUid, cid: category.cid, title: title, content: 'does not really matter' }, next); + }, next); + }, + function (next) { + user.getSettings(fooUid, next); + }, + function (settings, next) { + request(nconf.get('url') + '/api/category/' + category.slug, { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.equal(body.topics.length, settings.topicsPerPage); + assert.equal(body.pagination.pageCount, 2); + next(); + }); + }, + ], function (err) { + next(err); + }); + }, + ], done); + }); + }); + after(function (done) { var analytics = require('../src/analytics'); analytics.writeData(function (err) {