diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 5b30cd0d7a..f2e0fbc5a7 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -160,7 +160,7 @@ helpers.getCustomUserFields = async function (callerUID, userData) { if (f.type === 'input-link' && userValue) { f.linkValue = validator.escape(String(userValue.replace('http://', '').replace('https://', ''))); } - f['select-options'] = f['select-options'].split('\n').filter(Boolean).map( + f['select-options'] = (f['select-options'] || '').split('\n').filter(Boolean).map( opt => ({ value: opt, selected: Array.isArray(userValue) ? diff --git a/src/user/profile.js b/src/user/profile.js index 7869f14410..14942f98aa 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -16,7 +16,7 @@ const tx = require('../translator'); module.exports = function (User) { User.updateProfile = async function (uid, data, extraFields) { let fields = [ - 'username', 'email', 'fullname', 'website', 'location', + 'username', 'email', 'fullname', 'groupTitle', 'birthday', 'signature', 'aboutme', ...await db.getSortedSetRange('user-custom-fields', 0, -1), ]; diff --git a/test/socket.io.js b/test/socket.io.js index 9f2aeebbb6..8fd2895809 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -199,6 +199,26 @@ describe('socket.io', () => { assert(Array.isArray(users[0].groups)); }); + it('should error with invalid data set user reputation', async () => { + await assert.rejects( + socketAdmin.user.setReputation({ uid: adminUid }, null), + { message: '[[error:invalid-data]]' } + ); + await assert.rejects( + socketAdmin.user.setReputation({ uid: adminUid }, {}), + { message: '[[error:invalid-data]]' } + ); + await assert.rejects( + socketAdmin.user.setReputation({ uid: adminUid }, { uids: [], value: null }), + { message: '[[error:invalid-data]]' } + ); + }); + + it('should set user reputation', async () => { + await socketAdmin.user.setReputation({ uid: adminUid }, { uids: [adminUid], value: 10 }); + assert.strictEqual(10, await db.sortedSetScore('users:reputation', adminUid)); + }); + it('should reset lockouts', (done) => { socketAdmin.user.resetLockouts({ uid: adminUid }, [regularUid], (err) => { assert.ifError(err); diff --git a/test/user/custom-fields.js b/test/user/custom-fields.js new file mode 100644 index 0000000000..c032fd47a8 --- /dev/null +++ b/test/user/custom-fields.js @@ -0,0 +1,117 @@ +'use strict'; + +const nconf = require('nconf'); +const assert = require('assert'); +const async = require('async'); + +const db = require('../mocks/databasemock'); + +const user = require('../../src/user'); +const groups = require('../../src/groups'); + +const request = require('../../src/request'); +const socketUser = require('../../src/socket.io/user'); +const adminUser = require('../../src/socket.io/admin/user'); + + +describe('custom user fields', () => { + let adminUid; + let lowRepUid; + let highRepUid; + before(async () => { + adminUid = await user.create({ username: 'admin' }); + await groups.join('administrators', adminUid); + lowRepUid = await user.create({ username: 'lowRepUser' }); + highRepUid = await user.create({ username: 'highRepUser' }); + await db.setObjectField(`user:${highRepUid}`, 'reputation', 10); + await db.sortedSetAdd(`users:reputation`, 10, highRepUid); + }); + + it('should create custom user fields', async () => { + const fields = [ + { key: 'website', icon: 'fa-solid fa-globe', name: 'Website', type: 'input-link', visibility: 'all', 'min:rep': 0 }, + { key: 'location', icon: 'fa-solid fa-pin', name: 'Location', type: 'input-text', visibility: 'all', 'min:rep': 0 }, + { key: 'favouriteDate', icon: '', name: 'Anniversary', type: 'input-date', visibility: 'all', 'min:rep': 0 }, + { key: 'favouriteLanguages', icon: 'fa-solid fa-code', name: 'Favourite Languages', type: 'select-multi', visibility: 'all', 'min:rep': 0, 'select-options': 'C++\nC\nJavascript\nPython\nAssembly' }, + { key: 'luckyNumber', icon: 'fa-solid fa-dice', name: 'Lucky Number', type: 'input-number', visibility: 'privileged', 'min:rep': 7 }, + { key: 'soccerTeam', icon: 'fa-regular fa-futbol', name: 'Soccer Team', type: 'select', visibility: 'all', 'min:rep': 0, 'select-options': 'Barcelona\nLiverpool\nArsenal\nGalatasaray\n' }, + ]; + await adminUser.saveCustomFields({ uid: adminUid }, fields); + }); + + it('should fail to update a field if user does not have enough reputation', async () => { + await assert.rejects( + user.updateProfile(lowRepUid, { + uid: lowRepUid, + luckyNumber: 13, + }), + { message: '[[error:not-enough-reputation-custom-field, 7, Lucky Number]]' }, + ); + }); + + it('should fail with invalid field data', async () => { + await assert.rejects( + user.updateProfile(highRepUid, { + uid: highRepUid, + location: new Array(300).fill('a').join(''), + }), + { message: '[[error:custom-user-field-value-too-long, Location]]' }, + ); + + await assert.rejects( + user.updateProfile(highRepUid, { + uid: highRepUid, + luckyNumber: 'not-a-number', + }), + { message: '[[error:custom-user-field-invalid-number, Lucky Number]]' }, + ); + + await assert.rejects( + user.updateProfile(highRepUid, { + uid: highRepUid, + favouriteDate: 'not-a-date', + }), + { message: '[[error:custom-user-field-invalid-date, Anniversary]]' }, + ); + + await assert.rejects( + user.updateProfile(highRepUid, { + uid: highRepUid, + website: 'not-a-url', + }), + { message: '[[error:custom-user-field-invalid-link, Website]]' }, + ); + + await assert.rejects( + user.updateProfile(highRepUid, { + uid: highRepUid, + soccerTeam: 'not-in-options', + }), + { message: '[[error:custom-user-field-select-value-invalid, Soccer Team]]' }, + ); + + await assert.rejects( + user.updateProfile(highRepUid, { + uid: highRepUid, + favouriteLanguages: '["not-in-options"]', + }), + { message: '[[error:custom-user-field-select-value-invalid, Favourite Languages]]' }, + ); + }); + + it('should update a users custom fields if they have enough reputation', async () => { + await user.updateProfile(highRepUid, { + uid: highRepUid, + website: 'https://nodebb.org', + location: 'Toronto', + favouriteDate: '2014-05-01', + favouriteLanguages: '["Javascript", "Python"]', + luckyNumber: 13, + soccerTeam: 'Galatasaray', + }); + + const { response, body } = await request.get(`${nconf.get('url')}/api/user/highrepuser`); + // console.log(body); + assert.strictEqual(body.website, 'https://nodebb.org'); + }); +});