Merge branch 'master' into develop

pull/22627/head^2
Diego Sampaio 4 years ago
commit f0b9cf2d58
No known key found for this signature in database
GPG Key ID: E060152B30502562
  1. 11
      .github/history-manual.json
  2. 131
      .github/history.json
  3. 54
      .github/workflows/build_and_test.yml
  4. 2107
      HISTORY.md
  5. 13
      app/api/server/v1/channels.js
  6. 10
      app/api/server/v1/groups.js
  7. 6
      app/markdown/lib/parser/original/markdown.js
  8. 2
      app/markdown/tests/client.tests.js
  9. 4
      app/message-star/client/starMessage.js
  10. 3
      app/message-star/server/starMessage.js
  11. 9
      app/models/client/models/ChatMessage.js
  12. 9
      app/models/server/models/Messages.js
  13. 2
      app/ui/client/lib/Tooltip.js
  14. 5
      client/components/MarkdownText.tsx
  15. 2
      client/components/Message/Body/Link.tsx
  16. 11
      client/views/blocks/MessageBlock.js
  17. 22
      package-lock.json
  18. 9
      tests/data/rooms.helper.js
  19. 106
      tests/end-to-end/api/02-channels.js
  20. 109
      tests/end-to-end/api/03-groups.js
  21. 36
      tests/end-to-end/api/05-chat.js

@ -104,5 +104,16 @@
"contributors": [
"ggazzo"
]
}],
"3.16.3": [{
"title": "[FIX] Security Hotfix (https://docs.rocket.chat/guides/security/security-updates)",
"userLogin": "sampaiodiego",
"contributors": [
"g-thome",
"ggazzo",
"sampaiodiego",
"matheusbsilva137",
"pierre-lehnen-rc"
]
}]
}

@ -62190,6 +62190,135 @@
"4.2"
],
"pull_requests": []
},
"3.16.1": {
"node_version": "12.22.1",
"npm_version": "6.14.1",
"apps_engine_version": "1.27.0",
"mongo_versions": [
"3.4",
"3.6",
"4.0",
"4.2"
],
"pull_requests": [
{
"pr": "22511",
"title": "[FIX] Livechat apps permission error",
"userLogin": "d-gubert",
"description": "Updated Apps-Engine version fixes errors with apps using livechat features.",
"milestone": "3.16.1",
"contributors": [
"d-gubert",
"web-flow"
]
},
{
"pr": "22506",
"title": "[FIX] Prune messages not applying the user filter",
"userLogin": "sampaiodiego",
"milestone": "3.15.3",
"contributors": [
"sampaiodiego"
]
}
]
},
"3.15.3": {
"node_version": "12.22.1",
"npm_version": "6.14.1",
"apps_engine_version": "1.26.0",
"mongo_versions": [
"3.4",
"3.6",
"4.0",
"4.2"
],
"pull_requests": [
{
"pr": "22506",
"title": "[FIX] Prune messages not applying the user filter",
"userLogin": "sampaiodiego",
"milestone": "3.15.3",
"contributors": [
"sampaiodiego"
]
}
]
},
"3.16.2": {
"node_version": "12.22.1",
"npm_version": "6.14.1",
"apps_engine_version": "1.27.1",
"mongo_versions": [
"3.4",
"3.6",
"4.0",
"4.2"
],
"pull_requests": [
{
"pr": "22620",
"title": "Regression: Change the name of called methods in Users model",
"userLogin": "tiagoevanp",
"milestone": "3.16.2",
"contributors": [
"tiagoevanp",
"sampaiodiego"
]
},
{
"pr": "22619",
"title": "[FIX] Markdown for UiKit blocks",
"userLogin": "ggazzo",
"milestone": "3.16.2",
"contributors": [
"ggazzo"
]
},
{
"pr": "22584",
"title": "[FIX] Checks the list of agents if at least one is online",
"userLogin": "tiagoevanp",
"milestone": "3.16.2",
"contributors": [
"tiagoevanp",
"web-flow",
"sampaiodiego"
]
},
{
"pr": "22545",
"title": "[FIX] Error in permission check for getLivechatDepartmentByNameOrId method in Apps",
"userLogin": "d-gubert",
"description": "Update the Apps-Engine with a fix for the permission check on the `getLivechatDepartmentByNameOrId` method",
"milestone": "3.16.2",
"contributors": [
"d-gubert"
]
},
{
"pr": "22589",
"title": "[FIX] Livechat webhook request without headers",
"userLogin": "ggazzo",
"milestone": "3.16.2",
"contributors": [
"ggazzo"
]
}
]
},
"3.16.3": {
"node_version": "12.22.1",
"npm_version": "6.14.1",
"apps_engine_version": "1.27.1",
"mongo_versions": [
"3.4",
"3.6",
"4.0",
"4.2"
],
"pull_requests": []
}
}
}
}

@ -56,14 +56,14 @@ jobs:
path: /home/runner/.cache/Cypress
key: ${{ runner.OS }}-cache-cypress-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: Cache node modules
id: cache-nodemodules
uses: actions/cache@v2
with:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
# - name: Cache node modules
# id: cache-nodemodules
# uses: actions/cache@v2
# with:
# path: |
# ./node_modules
# ./ee/server/services/node_modules
# key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: Cache meteor local
uses: actions/cache@v2
@ -106,7 +106,7 @@ jobs:
git version
- name: npm install
if: steps.cache-nodemodules.outputs.cache-hit != 'true' || steps.cache-cypress.outputs.cache-hit != 'true'
# if: steps.cache-nodemodules.outputs.cache-hit != 'true' || steps.cache-cypress.outputs.cache-hit != 'true'
run: |
meteor npm install
cd ./ee/server/services
@ -235,17 +235,17 @@ jobs:
path: /home/runner/.cache/Cypress
key: ${{ runner.OS }}-cache-cypress-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: Cache node modules
id: cache-nodemodules
uses: actions/cache@v2
with:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
# - name: Cache node modules
# id: cache-nodemodules
# uses: actions/cache@v2
# with:
# path: |
# ./node_modules
# ./ee/server/services/node_modules
# key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: NPM install
if: steps.cache-nodemodules.outputs.cache-hit != 'true' || steps.cache-cypress.outputs.cache-hit != 'true'
# if: steps.cache-nodemodules.outputs.cache-hit != 'true' || steps.cache-cypress.outputs.cache-hit != 'true'
run: |
npm install
@ -299,14 +299,14 @@ jobs:
docker rmi $(docker image ls -aq)
df -h
- name: Cache node modules
id: cache-nodemodules
uses: actions/cache@v2
with:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
# - name: Cache node modules
# id: cache-nodemodules
# uses: actions/cache@v2
# with:
# path: |
# ./node_modules
# ./ee/server/services/node_modules
# key: ${{ runner.OS }}-node_modules-4-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
- name: Cache meteor local
uses: actions/cache@v2
@ -354,7 +354,7 @@ jobs:
git version
- name: npm install
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
# if: steps.cache-nodemodules.outputs.cache-hit != 'true'
run: |
meteor npm install

File diff suppressed because it is too large Load Diff

@ -3,7 +3,7 @@ import { Match, check } from 'meteor/check';
import _ from 'underscore';
import { Rooms, Subscriptions, Messages, Uploads, Integrations, Users } from '../../../models/server';
import { hasPermission, hasAtLeastOnePermission, hasAllPermission } from '../../../authorization/server';
import { canAccessRoom, hasPermission, hasAtLeastOnePermission, hasAllPermission } from '../../../authorization/server';
import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { API } from '../api';
@ -676,14 +676,23 @@ API.v1.addRoute('channels.messages', { authRequired: true }, {
API.v1.addRoute('channels.online', { authRequired: true }, {
get() {
const { query } = this.parseJsonQuery();
if (!query || Object.keys(query).length === 0) {
return API.v1.failure('Invalid query');
}
const ourQuery = Object.assign({}, query, { t: 'c' });
const room = Rooms.findOne(ourQuery);
if (room == null) {
return API.v1.failure('Channel does not exists');
}
const user = this.getLoggedInUser();
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed');
}
const online = Users.findUsersNotOffline({
fields: { username: 1 },
}).fetch();

@ -568,6 +568,10 @@ API.v1.addRoute('groups.messages', { authRequired: true }, {
API.v1.addRoute('groups.online', { authRequired: true }, {
get() {
const { query } = this.parseJsonQuery();
if (!query || Object.keys(query).length === 0) {
return API.v1.failure('Invalid query');
}
const ourQuery = Object.assign({}, query, { t: 'p' });
const room = Rooms.findOne(ourQuery);
@ -576,6 +580,12 @@ API.v1.addRoute('groups.online', { authRequired: true }, {
return API.v1.failure('Group does not exists');
}
const user = this.getLoggedInUser();
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed');
}
const online = Users.findUsersNotOffline({
fields: {
username: 1,

@ -126,7 +126,7 @@ const parseNotEscaped = (message, {
url = encodeURI(url);
const target = url.indexOf(rootUrl) === 0 ? '' : '_blank';
return addAsToken(message, `<a href="${ url }" title="${ title }" target="${ target }" rel="noopener noreferrer"><div class="inline-image" style="background-image: url(${ url });"></div></a>`, 'link');
return addAsToken(message, `<a data-title="${ url }" href="${ url }" title="${ title }" target="${ target }" rel="noopener noreferrer"><div class="inline-image" style="background-image: url(${ url });"></div></a>`, 'link');
});
// Support [Text](http://link)
@ -142,7 +142,7 @@ const parseNotEscaped = (message, {
const escapedUrl = encodeURI(url);
return addAsToken(message, `<a href="${ escapedUrl }" target="${ target }" rel="noopener noreferrer">${ title }</a>`, 'link');
return addAsToken(message, `<a data-title="${ escapedUrl }" href="${ escapedUrl }" target="${ target }" rel="noopener noreferrer">${ title }</a>`, 'link');
});
// Support <http://link|Text>
@ -155,7 +155,7 @@ const parseNotEscaped = (message, {
}
url = encodeURI(url);
const target = url.indexOf(rootUrl) === 0 ? '' : '_blank';
return addAsToken(message, `<a href="${ url }" target="${ target }" rel="noopener noreferrer">${ title }</a>`, 'link');
return addAsToken(message, `<a data-title="${ url }" href="${ url }" target="${ target }" rel="noopener noreferrer">${ title }</a>`, 'link');
});
return msg;
};

@ -15,7 +15,7 @@ const italicWrapper = (text) => wrapper(`<em>${ text }</em>`, '_');
const strikeWrapper = (text) => wrapper(`<strike>${ text }</strike>`, '~');
const headerWrapper = (text, level) => `<h${ level }>${ text }</h${ level }>`;
const quoteWrapper = (text) => `<blockquote class="background-transparent-darker-before"><span class="copyonly">&gt;</span>${ text }</blockquote>`;
const linkWrapped = (link, title) => `<a href="${ link }" target="_blank" rel="noopener noreferrer">${ title }</a>`;
const linkWrapped = (link, title) => `<a data-title="${ link }" href="${ link }" target="_blank" rel="noopener noreferrer">${ title }</a>`;
const inlinecodeWrapper = (text) => wrapper(`<span><code class="code-colors inline">${ text }</code></span>`, '`');
const codeWrapper = (text, lang) => `<pre><code class='code-colors hljs ${ lang }'><span class='copyonly'>\`\`\`<br></span>${ text }<span class='copyonly'><br>\`\`\`</span></code></pre>`;

@ -15,6 +15,10 @@ Meteor.methods({
toastr.error(TAPi18n.__('error-starring-message'));
return false;
}
if (!ChatMessage.findOneByRoomIdAndMessageId(message.rid, message._id)) {
toastr.error(TAPi18n.__('error-starring-message'));
return false;
}
if (!settings.get('Message_AllowStarring')) {
toastr.error(TAPi18n.__('error-starring-message'));
return false;

@ -23,6 +23,9 @@ Meteor.methods({
if (!subscription) {
return false;
}
if (!Messages.findOneByRoomIdAndMessageId(message.rid, message._id)) {
return false;
}
const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
if (isTheLastMessage(room, message)) {
Rooms.updateLastMessageStar(room._id, Meteor.userId(), message.starred);

@ -9,3 +9,12 @@ ChatMessage.setReactions = function(messageId, reactions) {
ChatMessage.unsetReactions = function(messageId) {
return this.update({ _id: messageId }, { $unset: { reactions: 1 } });
};
ChatMessage.findOneByRoomIdAndMessageId = function(rid, messageId, options) {
const query = {
rid,
_id: messageId,
};
return this.findOne(query, options);
};

@ -457,6 +457,15 @@ export class Messages extends Base {
return this.findOne(query);
}
findOneByRoomIdAndMessageId(rid, messageId, options) {
const query = {
rid,
_id: messageId,
};
return this.findOne(query, options);
}
findByRoomIdAndType(roomId, type, options) {
const query = {
rid: roomId,

@ -49,7 +49,7 @@ document.body.addEventListener('mouseover', (() => {
element.removeAttribute('title');
openToolTip(element.dataset.title, element);
}
}, 1000);
}, 300);
closeTooltip();
};
})());

@ -86,7 +86,10 @@ const MarkdownText: FC<Partial<MarkdownTextParams>> = ({
}
const __html = useMemo(() => {
const html = content && typeof content === 'string' && marked(content, markedOptions);
const html =
content &&
typeof content === 'string' &&
marked(new Option(content).innerHTML, markedOptions);
return preserveHtml ? html : html && sanitizer(html, { ADD_ATTR: ['target'] });
}, [content, preserveHtml, sanitizer, markedOptions]);

@ -14,7 +14,7 @@ const Link: FC<LinkProps> = ({ value }) => {
const { src, label } = value;
const target = src.value.indexOf(baseURI) === 0 ? '' : '_blank';
return (
<a href={src.value} target={target} rel='noopener noreferrer'>
<a href={src.value} data-title={src.value} target={target} rel='noopener noreferrer'>
{((block: ASTLink['value']['label']): JSX.Element | string | null => {
switch (block.type) {
case 'PLAIN_TEXT':

@ -5,9 +5,10 @@ import {
kitContext,
messageParser,
} from '@rocket.chat/fuselage-ui-kit';
import React, { useRef, useEffect } from 'react';
import React from 'react';
import * as ActionManager from '../../../app/ui-message/client/ActionManager';
import { useBlockRendered } from '../../components/Message/hooks/useBlockRendered';
import { renderMessageBody } from '../../lib/renderMessageBody';
import './textParsers';
@ -17,6 +18,7 @@ const mrkdwn = ({ text } = {}) =>
messageParser.mrkdwn = mrkdwn;
function MessageBlock({ mid: _mid, rid, blocks, appId }) {
const { ref, className } = useBlockRendered();
const context = {
action: ({ actionId, value, blockId, mid = _mid }) => {
ActionManager.triggerBlockAction({
@ -36,14 +38,9 @@ function MessageBlock({ mid: _mid, rid, blocks, appId }) {
rid,
};
const ref = useRef();
useEffect(() => {
ref.current.dispatchEvent(new Event('rendered'));
}, []);
return (
<kitContext.Provider value={context}>
<div className='js-block-wrapper' ref={ref} />
<div className={className} ref={ref} />
<UiKitComponent render={UiKitMessage} blocks={blocks} />
</kitContext.Provider>
);

22
package-lock.json generated

@ -19168,7 +19168,7 @@
},
"debug": {
"version": "4.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
@ -19191,7 +19191,7 @@
},
"fs-minipass": {
"version": "1.2.5",
"resolved": false,
"resolved": "",
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"requires": {
"minipass": "^2.2.1"
@ -19219,7 +19219,7 @@
},
"glob": {
"version": "7.1.3",
"resolved": false,
"resolved": "",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"requires": {
"fs.realpath": "^1.0.0",
@ -19245,7 +19245,7 @@
},
"ignore-walk": {
"version": "3.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"requires": {
"minimatch": "^3.0.4"
@ -19298,7 +19298,7 @@
},
"minipass": {
"version": "2.3.5",
"resolved": false,
"resolved": "",
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"requires": {
"safe-buffer": "^5.1.2",
@ -19307,7 +19307,7 @@
},
"minizlib": {
"version": "1.2.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"requires": {
"minipass": "^2.2.1"
@ -19335,7 +19335,7 @@
},
"needle": {
"version": "2.3.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
"requires": {
"debug": "^4.1.0",
@ -19345,7 +19345,7 @@
},
"node-pre-gyp": {
"version": "0.12.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
"requires": {
"detect-libc": "^1.0.2",
@ -19376,7 +19376,7 @@
},
"npm-packlist": {
"version": "1.4.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
"requires": {
"ignore-walk": "^3.0.1",
@ -19475,7 +19475,7 @@
},
"rimraf": {
"version": "2.6.3",
"resolved": false,
"resolved": "",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"requires": {
"glob": "^7.1.3"
@ -19544,7 +19544,7 @@
},
"tar": {
"version": "4.4.8",
"resolved": false,
"resolved": "",
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"requires": {
"chownr": "^1.1.1",

@ -1,6 +1,6 @@
import { api, credentials, request } from './api-data';
export const createRoom = ({ name, type, username, members = [] }) => {
export const createRoom = ({ name, type, username, members = [], credentials: customCredentials }) => {
if (!type) {
throw new Error('"type" is required in "createRoom" test helper');
}
@ -17,13 +17,18 @@ export const createRoom = ({ name, type, username, members = [] }) => {
: { name };
return request.post(api(endpoints[type]))
.set(credentials)
.set(customCredentials || credentials)
.send({
...params,
members,
});
};
export const asyncCreateRoom = ({ name, type, username, members = [] }) => new Promise((resolve) => {
createRoom({ name, type, username, members })
.end(resolve);
});
export const closeRoom = ({ type, roomId }) => {
if (!type) {
throw new Error('"type" is required in "closeRoom" test helper');

@ -183,19 +183,99 @@ describe('[Channels]', function() {
});
});
it('/channels.online', (done) => {
request.get(api('channels.online'))
.set(credentials)
.query({
query: '{ "_id": "GENERAL" }',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('online').and.to.be.an('array');
})
.end(done);
describe('[/channels.online]', () => {
const createUserAndChannel = async () => {
const testUser = await createUser();
const testUserCredentials = await login(testUser.username, password);
await request.post(api('users.setStatus'))
.set(testUserCredentials)
.send({
message: '',
status: 'online',
});
const roomName = `group-test-${ Date.now() }`;
const roomResponse = await createRoom({ name: roomName, type: 'c', members: [testUser.username] });
return {
testUser,
testUserCredentials,
room: roomResponse.body.channel,
};
};
it('should return an error if no query', () =>
request.get(api('channels.online'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Invalid query');
}));
it('should return an error if passing an empty query', () =>
request.get(api('channels.online'))
.set(credentials)
.query('query={}')
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Invalid query');
}));
it('should return an array with online members', async () => {
const {
testUser,
testUserCredentials,
room,
} = await createUserAndChannel();
return request.get(api('channels.online'))
.set(testUserCredentials)
.query(`query={"_id": "${ room._id }"}`)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('online');
const expected = {
_id: testUser._id,
username: testUser.username,
};
expect(res.body.online).to.deep.include(expected);
});
});
it('should return an empty array if requesting user is not in channel', async () => {
const outsider = await createUser();
const outsiderCredentials = await login(outsider.username, password);
const {
testUser,
room,
} = await createUserAndChannel();
return request.get(api('channels.online'))
.set(outsiderCredentials)
.query(`query={"_id": "${ room._id }"}`)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('online');
const expected = {
_id: testUser._id,
username: testUser.username,
};
expect(res.body.online).to.deep.include(expected);
});
});
});
describe('[/channels.files]', () => {

@ -537,16 +537,105 @@ describe('[Groups]', function() {
.end(done);
});
it('/groups.online', (done) => {
request.get(api('groups.online'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('online').and.to.be.an('array');
})
.end(done);
describe('[/groups.online]', () => {
const createUserAndChannel = async (setAsOnline = true) => {
const testUser = await createUser();
const testUserCredentials = await login(testUser.username, password);
if (setAsOnline) {
await request.post(api('users.setStatus'))
.set(testUserCredentials)
.send({
message: '',
status: 'online',
});
}
const roomName = `group-test-${ Date.now() }`;
const roomResponse = await createRoom({ name: roomName, type: 'p', members: [testUser.username], credentials: testUserCredentials });
return {
testUser,
testUserCredentials,
room: roomResponse.body.group,
};
};
it('should return an error if no query', () =>
request.get(api('groups.online'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Invalid query');
}));
it('should return an error if passing an empty query', () =>
request.get(api('groups.online'))
.set(credentials)
.query('query={}')
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Invalid query');
}));
it('should return an array with online members', async () => {
const {
testUser,
testUserCredentials,
room,
} = await createUserAndChannel();
const response = await request.get(api('groups.online'))
.set(testUserCredentials)
.query(`query={"_id": "${ room._id }"}`);
const { body } = response;
const expected = {
_id: testUser._id,
username: testUser.username,
};
expect(body.online).to.deep.include(expected);
});
it('should return an empty array if members are offline', async () => {
const {
testUserCredentials,
room,
} = await createUserAndChannel(false);
const response = await request.get(api('groups.online'))
.set(testUserCredentials)
.query(`query={"_id": "${ room._id }"}`);
const { body } = response;
expect(body.online).to.deep.equal([]);
});
it('should return an error if requesting user is not in group', async () => {
const outsider = await createUser();
const outsiderCredentials = await login(outsider.username, password);
const {
room,
} = await createUserAndChannel();
return request.get(api('groups.online'))
.set(outsiderCredentials)
.query(`query={"_id": "${ room._id }"}`)
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-not-allowed');
});
});
});
it('/groups.members', (done) => {

@ -1543,6 +1543,42 @@ describe('[Chat]', function() {
});
});
describe('[/chat.starMessage]', () => {
it('should return an error when starMessage is not allowed in this server', (done) => {
updateSetting('Message_AllowStarring', false).then(() => {
request.post(api('chat.starMessage'))
.set(credentials)
.send({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
});
it('should star Message successfully', (done) => {
updateSetting('Message_AllowStarring', true).then(() => {
request.post(api('chat.starMessage'))
.set(credentials)
.send({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.not.have.property('error');
})
.end(done);
});
});
});
describe('[/chat.ignoreUser]', () => {
it('should fail if invalid roomId', (done) => {
request.get(api('chat.ignoreUser'))

Loading…
Cancel
Save