Merge branch 'master' of github.com:coralproject/talk into community

This commit is contained in:
Belen Curcio
2016-11-08 17:43:00 -03:00
23 changed files with 409 additions and 206 deletions
+14 -2
View File
@@ -1,6 +1,12 @@
# Talk [![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk)
A commenting platform from The Coral Project. [https://coralproject.net](https://coralproject.net)
## Contributing to Talk
### Local Dependencies
Node
Mongo
### Getting Started
`npm install`
Run it once to install the dependencies.
@@ -8,16 +14,22 @@ Run it once to install the dependencies.
`npm start`
Runs Talk.
### Running with Docker
Make sure you have Docker running first and then run `docker-compose up -d`
### Testing
`npm test`
### Lint
`npm run lint`
### Helpful URLs
Bare comment stream: http://localhost:5000/client/coral-embed-stream/
Comment stream embedded on sample article: http://localhost:5000/client/coral-embed-stream/samplearticle.html
Moderator view: http://localhost:5000/admin/
### Docs
`swagger.yaml`
### Mantainers
### License
**Apache-2.0**
+6
View File
@@ -8,6 +8,8 @@ const app = express();
// Middleware declarations.
app.use(morgan('dev'));
app.use(bodyParser.json());
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// API Routes.
app.use('/api/v1', require('./routes/api'));
@@ -15,4 +17,8 @@ app.use('/api/v1', require('./routes/api'));
// Static Routes.
app.use('/client/', express.static(path.join(__dirname, 'dist')));
app.get('/admin*', (req, res) => {
res.render('admin', {basePath: '/client/coral-admin'});
});
module.exports = app;
-1
View File
@@ -3,5 +3,4 @@ public/bundle.js
public/embed/comment-stream
.DS_Store
npm-debug.log
config.json
yarn.lock
+5
View File
@@ -0,0 +1,5 @@
{
"basePath": "admin",
"talkHost": "http://localhost:16180",
"xeniaHost": "http://localhost:16180"
}
-3
View File
@@ -1,3 +0,0 @@
{
"base": "client/coral-admin"
}
+3 -3
View File
@@ -27,8 +27,8 @@
},
"devDependencies": {
"autoprefixer": "6.5.0",
"buble": "0.13.0",
"buble-loader": "0.3.0",
"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"copy-webpack-plugin": "3.0.1",
"css-loader": "0.25.0",
"json-loader": "0.5.4",
@@ -37,7 +37,7 @@
"precss": "1.4.0",
"standard": "8.2.0",
"style-loader": "0.13.1",
"webpack": "2.1.0-beta.25",
"webpack": "^1.13.3",
"webpack-dev-server": "1.16.1"
},
"config": {
+1 -1
View File
@@ -17,6 +17,6 @@
</head>
<body>
<div id="root"></div>
<script src="undefined/bundle.js" charset="utf-8"></script>
<script src="client/coral-admin/bundle.js" charset="utf-8"></script>
</body>
</html>
@@ -120,7 +120,7 @@ export default class CommentList extends React.Component {
{commentIds.map((commentId, index) => (
<Comment comment={comments.get(commentId)}
ref={el => { if (el && commentId === active) { this._active = el } }}
key={index}
key={index + 'comment'}
index={index}
onClickAction={this.onClickAction}
actions={this.props.actions}
@@ -28,8 +28,8 @@ export default ({ open, onClose }) => (
<Modal open={open} onClose={onClose}>
<h3>{lang.t('modqueue.shortcuts')}</h3>
<div className={styles.container}>
{shortcuts.map(shortcut => (
<table className={styles.table}>
{shortcuts.map((shortcut, i) => (
<table className={styles.table} key={i}>
<thead>
<tr>
<th>{lang.t(shortcut.title)}</th>
@@ -37,7 +37,7 @@ export default ({ open, onClose }) => (
</thead>
<tbody>
{Object.keys(shortcut.shortcuts).map(key => (
<tr>
<tr key={key + 'tr'}>
<td className={styles.shortcut}><span className={styles.key}>{key}</span></td>
<td>{lang.t(shortcut.shortcuts[key])}</td>
</tr>
@@ -23,7 +23,10 @@
}
.configSettingEmbed {
composes: configSetting;
border: 1px solid #ccc;
border-radius: 4px;
height: 90px;
margin-bottom: 10px;
display: block;
height: 170px;
}
+48 -28
View File
@@ -11,6 +11,8 @@ import {
Icon
} from 'react-mdl'
import styles from './Configure.css'
import I18n from 'coral-framework/i18n/i18n'
import translations from '../translations'
class Configure extends React.Component {
constructor (props) {
@@ -39,12 +41,28 @@ class Configure extends React.Component {
</List>
}
copyToClipBoard (event) {
const copyTextarea = document.querySelector('.' + styles.embedInput)
copyTextarea.select()
try {
document.execCommand('copy')
} catch (err) {
console.error('Unable to copy')
}
}
getEmbed () {
const embedText =
`<div id='coralStreamEmbed'></div><script type='text/javascript' src='https://pym.nprapps.org/pym.v1.min.js'></script><script>var pymParent = new pym.Parent('coralStreamEmbed', '${window.location.protocol}//${window.location.host}/client/coral-embed-stream/', {title: 'comments'});</script>`
return <List>
<ListItem className={styles.configSettingEmbed}>
<p>Copy and paste code below into your CMS to embed your comment box in your articles</p>
<input type='text' className={styles.embedInput} />
<Button raised colored>Copy</Button>
<textarea type='text' className={styles.embedInput}>
{embedText}
</textarea>
<Button raised colored>{lang.t('embedlink.copy')}</Button>
</ListItem>
</List>
}
@@ -59,35 +77,37 @@ class Configure extends React.Component {
: 'Embed Comment Stream'
return (
<div className={styles.container}>
<div className={styles.leftColumn}>
<List>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'comments')}
icon='settings'>Comment Settings</ListItemContent>
</ListItem>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'embed')}
icon='code'>Embed Comment Stream</ListItemContent>
</ListItem>
</List>
<Button raised colored>
<Icon name='save' /> Save Changes
</Button>
<div className={styles.container}>
<div className={styles.leftColumn}>
<List>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'comments')}
icon='settings'>Comment Settings</ListItemContent>
</ListItem>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'embed')}
icon='code'>Embed Comment Stream</ListItemContent>
</ListItem>
</List>
<Button raised colored>
<Icon name='save' /> Save Changes
</Button>
</div>
<div className={styles.mainContent}>
<h1>{pageTitle}</h1>
{
this.state.activeSection === 'comments'
? this.getCommentSettings()
: this.getEmbed()
}
</div>
</div>
<div className={styles.mainContent}>
<h1>{pageTitle}</h1>
{
this.state.activeSection === 'comments'
? this.getCommentSettings()
: this.getEmbed()
}
</div>
</div>
)
}
}
export default connect(x => x)(Configure)
const lang = new I18n(translations)
@@ -59,52 +59,55 @@ class ModerationQueue extends React.Component {
render () {
const { comments } = this.props
const { activeTab, singleView, modalOpen } = this.state
console.log('moderation queue', styles)
return (
<div className='mdl-tabs mdl-js-tabs mdl-js-ripple-effect'>
<div className='mdl-tabs__tab-bar'>
<a href='#pending' onClick={() => this.onTabClick('pending')}
className={`mdl-tabs__tab is-active ${styles.tab}`}>{lang.t('modqueue.pending')}</a>
<a href='#rejected' onClick={() => this.onTabClick('rejected')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.rejected')}</a>
<a href='#flagged' onClick={() => this.onTabClick('flagged')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.flagged')}</a>
<div>
<div className='mdl-tabs mdl-js-tabs mdl-js-ripple-effect'>
<div className='mdl-tabs__tab-bar'>
<a href='#pending' onClick={() => this.onTabClick('pending')}
className={`mdl-tabs__tab is-active ${styles.tab}`}>{lang.t('modqueue.pending')}</a>
<a href='#rejected' onClick={() => this.onTabClick('rejected')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.rejected')}</a>
<a href='#flagged' onClick={() => this.onTabClick('flagged')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.flagged')}</a>
</div>
<div className={`mdl-tabs__panel is-active ${styles.listContainer}`} id='pending'>
<CommentList
isActive={activeTab === 'pending'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => !comments.get('byId').get(id).get('status'))}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='rejected'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => comments.get('byId').get(id).get('status') === 'rejected')}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='flagged'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => {
const data = comments.get('byId').get(id)
return !data.get('status') && data.get('flagged') === true
})}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<ModerationKeysModal open={modalOpen}
onClose={() => this.setState({ modalOpen: false })} />
</div>
<div className={`mdl-tabs__panel is-active ${styles.listContainer}`} id='pending'>
<CommentList
isActive={activeTab === 'pending'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => !comments.get('byId').get(id).get('status'))}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='rejected'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => comments.get('byId').get(id).get('status') === 'rejected')}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='flagged'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => {
const data = comments.get('byId').get(id)
return !data.get('status') && data.get('flagged') === true
})}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<ModerationKeysModal open={modalOpen}
onClose={() => this.setState({ modalOpen: false })} />
</div>
)
}
+1 -1
View File
@@ -1,7 +1,7 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from 'components/App'
import App from './components/App'
// Render the application into the DOM
ReactDOM.render(<App />, document.querySelector('#root'))
+8 -26
View File
@@ -1,19 +1,6 @@
const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const autoprefixer = require('autoprefixer')
const precss = require('precss')
const config = require('./config.json')
// doing a string replace here because I spent a day trying to do it the "webpack" way
// ond nothing works. just trying to replace a string in an index.html file is
// apparently something no one has ever done in the js community.
let templateString = fs.readFileSync('./index.ejs').toString()
templateString = templateString.replace('<%= basePath %>', config.basePath)
fs.writeFileSync('./public/index.html', templateString)
console.log(templateString)
module.exports = {
entry: {
@@ -25,18 +12,14 @@ module.exports = {
},
module: {
loaders: [
{ test: /.js$/, loaders: 'buble', include: path.join(__dirname, 'src') },
{ test: /.json$/, loaders: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style-loader!css-loader?modules&importLoaders=1!postcss-loader' }
{ test: /.js$/, loader: 'babel', include: path.join(__dirname, 'src'), exclude: /node_modules/ },
{ test: /\.json$/, loaders: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /.css$/, loaders: ['style-loader', 'css-loader?modules&localIdentName=[name]__[local]___[hash:base64:5]', 'postcss-loader'] }
]
},
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
context: __dirname,
postcss: [autoprefixer, precss]
}
}),
autoprefixer,
precss,
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development'),
@@ -45,10 +28,9 @@ module.exports = {
})
],
resolve: {
modules: [
root: [
path.resolve('./src'),
path.resolve('../'),
'node_modules'
path.resolve('../')
]
},
devServer: {
@@ -56,4 +38,4 @@ module.exports = {
index: '/'
}
}
}
}
+8 -28
View File
@@ -1,46 +1,26 @@
const path = require('path')
const fs = require('fs')
const devConfig = require('./webpack.config.dev')
const autoprefixer = require('autoprefixer')
const precss = require('precss')
const Copy = require('copy-webpack-plugin')
const webpack = require('webpack')
const config = require('./config.json')
// doing a string replace here because I spent a day trying to do it the "webpack" way
// ond nothing works. just trying to replace a string in an index.html file is
// apparently something no one has ever done in the js community.
let templateString = fs.readFileSync('./index.ejs').toString()
templateString = templateString.replace('<%= basePath %>', config.basePath)
fs.writeFileSync('./public/index.html', templateString)
module.exports = Object.assign({}, devConfig, {
module: {
context: __dirname,
loaders: [
{ test: /.js$/, loaders: 'buble', include: [path.join(__dirname, 'src'), path.join(__dirname, '../', 'coral-framework')] },
{ test: /.json$/, loaders: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style-loader!css-loader?modules&importLoaders=1!postcss-loader' }
{ test: /.js$/, loader: 'babel', include: [path.join(__dirname, 'src'), path.join(__dirname, '../', 'coral-framework')], exclude: /node_modules/ },
{ test: /.json$/, loader: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /.css$/, loaders: ['style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', 'postcss-loader'] }
]
},
plugins: [
new Copy([{
from: path.join(__dirname, '..', 'coral-embed-stream', 'dist'),
to: './embed/comment-stream'
}]),
new webpack.LoaderOptionsPlugin({
options: {
context: __dirname,
postcss: [autoprefixer, precss],
minimize: true,
debug: false
}
}),
new webpack.optimize.UglifyJsPlugin(),
autoprefixer,
precss,
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production'),
'NODE_ENV': JSON.stringify('development'),
'VERSION': JSON.stringify(require("./package.json").version)
}
})
]
})
})
+27 -1
View File
@@ -28,7 +28,7 @@ ActionSchema.statics.findById = function(id) {
};
/**
* Finds users in an array of ids.
* Finds actions in an array of ids.
* @param {String} ids array of user identifiers (uuid)
*/
ActionSchema.statics.findByItemIdArray = function(item_ids) {
@@ -37,6 +37,32 @@ ActionSchema.statics.findByItemIdArray = function(item_ids) {
});
};
/**
* Finds all comments for a specific action.
* @param {String} action_type type of action
* @param {String} item_type type of item the action is on
*/
ActionSchema.statics.findByType = function(action_type, item_type) {
return Action.find({
'action_type': action_type,
'item_type': item_type
});
};
/**
* Finds all comments ids for a specific action.
* @param {String} action_type type of action
* @param {String} item_type type of item the action is on
*/
ActionSchema.statics.findCommentsIdByActionType = function(action_type, item_type) {
return Action.find({
'action_type': action_type,
'item_type': item_type
},
'item_id'
);
};
const Action = mongoose.model('Action', ActionSchema);
module.exports = Action;
+52
View File
@@ -31,6 +31,23 @@ const CommentSchema = new Schema({
}
});
//==============================================================================
// New Statics
//==============================================================================
/**
* Create a comment.
* @param {String} body content of comment
*/
CommentSchema.statics.new = function(body, author_id, asset_id, parent_id, status, username) {
let comment = new Comment({body, author_id, asset_id, parent_id, status, username});
return comment.save();
};
//==============================================================================
// Find Statics
//==============================================================================
/**
* Finds a comment by the id.
* @param {String} id identifier of comment (uuid)
@@ -47,6 +64,28 @@ CommentSchema.statics.findByAssetId = function(asset_id) {
return Comment.find({asset_id});
};
/**
* Find comments by an action that was performed on them.
* @param {String} action_type the type of action that was performed on the comment
*/
CommentSchema.statics.findByActionType = function(action_type) {
return Action.findCommentsIdByActionType(action_type, 'comment').then((actions) => {
return Comment.find({'id': {'$in': actions.map(function(a){return a.item_id;})}});
});
};
/**
* Find comments by their status.
* @param {String} status the status to search for
*/
CommentSchema.statics.findByStatus = function(status) {
return Comment.find({'status': status});
};
//==============================================================================
// Update Statics
//==============================================================================
/**
* Change the status of a comment.
* @param {String} id identifier of the comment (uuid)
@@ -72,6 +111,19 @@ CommentSchema.statics.addAction = function(id, user_id, action_type) {
return action.save();
};
//==============================================================================
// Remove Statics
//==============================================================================
/**
* Change the status of a comment.
* @param {String} id identifier of the comment (uuid)
* @param {String} status the new status of the comment
*/
CommentSchema.statics.removeById = function(id) {
return Comment.remove({'id': id});
};
const Comment = mongoose.model('Comment', CommentSchema);
module.exports = Comment;
+12 -4
View File
@@ -5,12 +5,12 @@
"main": "app.js",
"scripts": {
"start": "./bin/www",
"build": "./node_modules/webpack/bin/webpack.js --config ./client/coral-embed-stream/webpack.config.js",
"build": "./node_modules/webpack/bin/webpack.js --config ./client/coral-embed-stream/webpack.config.js && cd client/coral-admin && npm install && npm run build",
"lint": "eslint .",
"pretest": "npm install",
"test": "mocha tests --recursive",
"test-watch": "mocha tests --recursive -w",
"embed-start": "./node_modules/webpack/bin/webpack.js --config ./client/coral-embed-stream/webpack.config.dev.js && ./bin/www"
"embed-start": "npm run build && ./bin/www"
},
"config": {
"pre-git": {
@@ -42,14 +42,17 @@
},
"homepage": "https://github.com/coralproject/talk#readme",
"dependencies": {
"autoprefixer": "^6.5.2",
"body-parser": "^1.15.2",
"debug": "^2.2.0",
"ejs": "^2.5.2",
"express": "^4.14.0",
"mongoose": "^4.6.5",
"uuid": "^2.0.3",
"morgan": "^1.7.0"
"morgan": "^1.7.0",
"uuid": "^2.0.3"
},
"devDependencies": {
"autoprefixer": "6.5.0",
"babel-core": "6.14.0",
"babel-jest": "^15.0.0",
"babel-loader": "6.2.5",
@@ -64,6 +67,7 @@
"chai": "^3.5.0",
"chai-http": "^3.0.0",
"copy-webpack-plugin": "^3.0.1",
"css-loader": "0.25.0",
"eslint": "^3.9.1",
"exports-loader": "^0.6.3",
"immutable": "^3.8.1",
@@ -71,6 +75,9 @@
"json-loader": "^0.5.4",
"mocha": "^3.1.2",
"mocha-junit-reporter": "^1.12.1",
"postcss-loader": "0.13.0",
"postcss-modules": "0.5.2",
"precss": "1.4.0",
"pre-git": "^3.10.0",
"pym.js": "^1.1.1",
"react": "15.3.2",
@@ -79,6 +86,7 @@
"redux": "^3.6.0",
"redux-thunk": "^2.1.0",
"regenerator": "^0.8.46",
"style-loader": "0.13.1",
"supertest": "^2.0.1",
"timeago.js": "^2.0.3",
"webpack": "^1.13.2",
+56 -14
View File
@@ -4,7 +4,7 @@ const Comment = require('../../../models/comment');
const router = express.Router();
//==============================================================================
// Routes
// Get Routes
//==============================================================================
router.get('/', (req, res, next) => {
@@ -23,16 +23,54 @@ router.get('/:comment_id', (req, res, next) => {
});
});
router.post('/', (req, res, next) => {
const {body, author_id, asset_id, parent_id, status, username} = req.body;
let comment = new Comment({body, author_id, asset_id, parent_id, status, username});
comment.save().then(({id}) => {
res.status(200).send({'id': id});
//==============================================================================
// Moderation Queues Routes
//==============================================================================
router.get('/action/:action_type', (req, res, next) => {
Comment.findByActionType(req.params.action_type).then((comments) => {
res.status(200).json(comments);
}).catch(error => {
next(error);
});
});
router.get('/status/rejected', (req, res, next) => {
Comment.findByStatus('rejected').then((comments) => {
res.status(200).json(comments);
}).catch(error => {
next(error);
});
});
router.get('/status/pending', (req, res, next) => {
Comment.findByStatus('').then((comments) => {
res.status(200).json(comments);
}).catch(error => {
next(error);
});
});
//==============================================================================
// Post Routes
//==============================================================================
router.post('/', (req, res, next) => {
const {body, author_id, asset_id, parent_id, status, username} = req.body;
Comment.new(body, author_id, asset_id, parent_id, status, username).then((comment) => {
res.status(200).send({'id': comment.id});
}).catch(error => {
next(error);
});
// let comment = new Comment({body, author_id, asset_id, parent_id, status, username});
// comment.save().then(({id}) => {
// res.status(200).send({'id': id});
// }).catch(error => {
// next(error);
// });
});
router.post('/:comment_id', (req, res, next) => {
Comment.findById(req.params.comment_id).then((comment) => {
comment.body = req.body.body;
@@ -48,14 +86,6 @@ router.post('/:comment_id', (req, res, next) => {
});
});
router.delete('/:comment_id', (req, res, next) => {
Comment.remove(req.params.comment_id).then(() => {
res.status(201).send('OK. Deleted');
}).catch(error => {
next(error);
});
});
router.post('/:comment_id/status', (req, res, next) => {
Comment.changeStatus(req.params.comment_id, req.body.status).then((comment) => {
res.status(200).send(comment);
@@ -72,4 +102,16 @@ router.post('/:comment_id/actions', (req, res, next) => {
});
});
//==============================================================================
// Delete Routes
//==============================================================================
router.delete('/:comment_id', (req, res, next) => {
Comment.removeById(req.params.comment_id).then(() => {
res.status(201).send('OK. Removed');
}).catch(error => {
next(error);
});
});
module.exports = router;
+97 -21
View File
@@ -66,6 +66,86 @@ describe('Get /comments', () => {
});
});
describe('Get moderation queues rejected, pending, flags', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
asset_id: 'asset',
author_id: '123',
status: 'rejected'
}, {
id: 'def',
body: 'comment 20',
asset_id: 'asset',
author_id: '456'
}, {
id: 'hij',
body: 'comment 30',
asset_id: '456',
status: 'accepted'
}];
const users = [{
id: '123',
display_name: 'Ana',
}, {
id: '456',
display_name: 'Maria',
}];
const actions = [{
action_type: 'flag',
item_id: 'abc',
item_type: 'comment'
}, {
action_type: 'like',
item_id: 'hij',
item_type: 'comment'
}];
beforeEach(() => {
return Comment.create(comments).then(() => {
return User.create(users);
}).then(() => {
return Action.create(actions);
});
});
it('should return all the rejected comments', function(done){
chai.request(app)
.get('/api/v1/comments/status/rejected')
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'abc');
done();
});
});
it('should return all the pending comments', function(done){
chai.request(app)
.get('/api/v1/comments/status/pending')
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('id', 'def');
done();
});
});
it('should return all the flagged comments', function(done){
chai.request(app)
.get('/api/v1/comments/action/flag')
.end(function(err, res){
expect(res).to.have.status(200);
expect(err).to.be.null;
expect(res.body.length).to.equal(1);
expect(res.body[0]).to.have.property('id', 'abc');
done();
});
});
});
describe('Post /comments', () => {
const users = [{
id: '123',
@@ -128,10 +208,12 @@ describe('Get /:comment_id', () => {
const actions = [{
action_type: 'flag',
item_id: 'abc'
item_id: 'abc',
item_type: 'comment'
}, {
action_type: 'like',
item_id: 'hij'
item_id: 'hij',
item_type: 'comment'
}];
beforeEach(() => {
@@ -144,13 +226,12 @@ describe('Get /:comment_id', () => {
it('should return the right comment for the comment_id', function(done){
chai.request(app)
.get('/api/v1/comments')
.query({'comment_id': 'abc'})
.get('/api/v1/comments/abc')
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res.body[0]).to.have.property('body');
expect(res.body[0].body).to.equal('comment 10');
expect(res).to.have.property('body');
expect(res.body).to.have.property('body', 'comment 10');
done();
});
});
@@ -205,14 +286,13 @@ describe('Put /:comment_id', () => {
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res.body).to.have.property('body');
expect(res.body.body).to.equal('Something body.');
expect(res.body).to.have.property('body', 'Something body.');
done();
});
});
});
describe('Delete /:comment_id', () => {
describe('Remove /:comment_id', () => {
const comments = [{
id: 'abc',
@@ -258,9 +338,10 @@ describe('Delete /:comment_id', () => {
chai.request(app)
.delete('/api/v1/comments/abc')
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(201);
Comment.findById({'id': 'abc'}).then((comment) => {
expect(comment).to.be.null;
Comment.findById('abc').then((comment) => {
expect(comment).to.be.empty;
});
done();
});
@@ -320,8 +401,7 @@ describe('Post /:comment_id/status', () => {
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res).to.have.body;
expect(res.body).to.have.property('status');
expect(res.body.status).to.equal('accepted');
expect(res.body).to.have.property('status', 'accepted');
done();
});
});
@@ -380,14 +460,10 @@ describe('Post /:comment_id/actions', () => {
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res).to.have.body;
expect(res.body).to.have.property('item_type');
expect(res.body.item_type).to.equal('comment');
expect(res.body).to.have.property('action_type');
expect(res.body.action_type).to.equal('flag');
expect(res.body).to.have.property('item_id');
expect(res.body.item_id).to.equal('abc');
expect(res.body).to.have.property('user_id');
expect(res.body.user_id).to.equal('456');
expect(res.body).to.have.property('item_type', 'comment');
expect(res.body).to.have.property('action_type', 'flag');
expect(res.body).to.have.property('item_id', 'abc');
expect(res.body).to.have.property('user_id', '456');
done();
});
});
+17 -24
View File
@@ -24,8 +24,7 @@ describe('GET /settings', () => {
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.have.property('moderation');
expect(res.body.moderation).to.equal('pre');
expect(res.body).to.have.property('moderation', 'pre');
done(err);
});
});
@@ -33,30 +32,24 @@ describe('GET /settings', () => {
// update the settings.
describe('update settings', () => {
it('should respond ok to a PUT', () => {
return Setting.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true})
.then(() => {
return chai.request(app)
.put('/api/v1/settings')
.send({moderation: 'post'})
.then(res => {
expect(res).to.have.status(204);
before(() => {
return Setting.update({id: '1'}, {$setOnInsert: defaults}, {upsert: true});
});
return Setting.getSettings();
it('should respond to a PUT with new settings', () => {
chai.request(app)
.put('/api/v1/settings')
.send({moderation: 'post'}, (err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(204);
done(err);
});
});
it('should have updates settings', () => {
chai.request(app)
.get('/api/v1/settings')
.end((err, res) => {
expect(err).to.be.null;
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.have.property('moderation');
expect(res.body.moderation).to.equal('post');
}).then(settings => {
// confirm updated settings in db
expect(settings).to.have.property('moderation');
expect(settings.moderation).to.equal('post');
}).catch(err => {
throw err;
});
});
});
});
-1
View File
@@ -60,7 +60,6 @@ describe('api/stream: routes', () => {
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
if (err) {return done(err);}
done();
});
});