From 15009d93ea9e4a7e1fa27cf86107f708ebb9167b Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 12:37:18 -0300 Subject: [PATCH 01/23] Add helper for blob URLs in tests --- client/lib/download.spec.ts | 37 ++++-------------------------- tests/utils/client/withBlobUrls.ts | 29 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 32 deletions(-) create mode 100644 tests/utils/client/withBlobUrls.ts diff --git a/client/lib/download.spec.ts b/client/lib/download.spec.ts index 12a9809afdca..d28c60abbbff 100644 --- a/client/lib/download.spec.ts +++ b/client/lib/download.spec.ts @@ -1,40 +1,13 @@ import 'jsdom-global/register'; import chai from 'chai'; import chaiSpies from 'chai-spies'; -import { after, before, describe, it } from 'mocha'; +import { describe, it } from 'mocha'; +import { withBlobUrls } from '../../tests/utils/client/withBlobUrls'; import { download, downloadAs, downloadCsvAs, downloadJsonAs } from './download'; chai.use(chaiSpies); -const withURL = (): void => { - let createObjectURL: typeof URL.createObjectURL; - let revokeObjectURL: typeof URL.revokeObjectURL; - - before(() => { - const blobs = new Map(); - - createObjectURL = window.URL.createObjectURL; - revokeObjectURL = window.URL.revokeObjectURL; - - window.URL.createObjectURL = (blob: Blob): string => { - const uuid = Math.random().toString(36).slice(2); - const url = `blob://${uuid}`; - blobs.set(url, blob); - return url; - }; - - window.URL.revokeObjectURL = (url: string): void => { - blobs.delete(url); - }; - }); - - after(() => { - window.URL.createObjectURL = createObjectURL; - window.URL.revokeObjectURL = revokeObjectURL; - }); -}; - describe('download', () => { it('should work', () => { const listener = chai.spy(); @@ -48,7 +21,7 @@ describe('download', () => { }); describe('downloadAs', () => { - withURL(); + withBlobUrls(); it('should work', () => { const listener = chai.spy(); @@ -62,7 +35,7 @@ describe('downloadAs', () => { }); describe('downloadJsonAs', () => { - withURL(); + withBlobUrls(); it('should work', () => { const listener = chai.spy(); @@ -76,7 +49,7 @@ describe('downloadJsonAs', () => { }); describe('downloadCsvAs', () => { - withURL(); + withBlobUrls(); it('should work', () => { const listener = chai.spy(); diff --git a/tests/utils/client/withBlobUrls.ts b/tests/utils/client/withBlobUrls.ts new file mode 100644 index 000000000000..c375672d8396 --- /dev/null +++ b/tests/utils/client/withBlobUrls.ts @@ -0,0 +1,29 @@ +import { before, after } from 'mocha'; + +export const withBlobUrls = (): void => { + let createObjectURL: typeof URL.createObjectURL; + let revokeObjectURL: typeof URL.revokeObjectURL; + + before(() => { + const blobs = new Map(); + + createObjectURL = window.URL.createObjectURL; + revokeObjectURL = window.URL.revokeObjectURL; + + window.URL.createObjectURL = (blob: Blob): string => { + const uuid = Math.random().toString(36).slice(2); + const url = `blob://${ uuid }`; + blobs.set(url, blob); + return url; + }; + + window.URL.revokeObjectURL = (url: string): void => { + blobs.delete(url); + }; + }); + + after(() => { + window.URL.createObjectURL = createObjectURL; + window.URL.revokeObjectURL = revokeObjectURL; + }); +}; From 0d72823f6f90c76285b0569360873ef06cd45cce Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 12:57:03 -0300 Subject: [PATCH 02/23] Setup all chai plugins at once --- .mocharc.js | 1 + app/apps/server/tests/messages.tests.js | 2 -- app/markdown/tests/client.tests.js | 2 +- app/settings/server/functions/settings.tests.ts | 3 --- app/settings/server/raw.tests.js | 3 --- app/utils/lib/getURL.tests.js | 2 +- client/lib/download.spec.ts | 3 --- package-lock.json | 9 +++++++++ package.json | 1 + tests/setup/chaiPlugins.ts | 6 ++++++ 10 files changed, 19 insertions(+), 13 deletions(-) create mode 100644 tests/setup/chaiPlugins.ts diff --git a/.mocharc.js b/.mocharc.js index c939a10c0cc5..5f2dc164bca3 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -4,6 +4,7 @@ module.exports = { require: [ 'ts-node/register', '@babel/register', + './tests/setup/chaiPlugins.ts', ], reporter: 'spec', ui: 'bdd', diff --git a/app/apps/server/tests/messages.tests.js b/app/apps/server/tests/messages.tests.js index 9dee0c68bcda..1ba8549ef5a5 100644 --- a/app/apps/server/tests/messages.tests.js +++ b/app/apps/server/tests/messages.tests.js @@ -9,8 +9,6 @@ import { MessagesMock } from './mocks/models/Messages.mock'; import { RoomsMock } from './mocks/models/Rooms.mock'; import { UsersMock } from './mocks/models/Users.mock'; -chai.use(require('chai-datetime')); - const { expect } = chai; mock('../../../models', './mocks/models'); diff --git a/app/markdown/tests/client.tests.js b/app/markdown/tests/client.tests.js index 83567dff6323..e7ef58713ef2 100644 --- a/app/markdown/tests/client.tests.js +++ b/app/markdown/tests/client.tests.js @@ -435,7 +435,7 @@ describe('Filtered', function() { describe('blockcodeFilter', () => testObject(blockcodeFiltered, filtered)); }); -// describe.only('Marked', function() { +// describe('Marked', function() { // describe('Bold', () => testObject(bold, marked)); // describe('Italic', () => testObject(italic, marked)); diff --git a/app/settings/server/functions/settings.tests.ts b/app/settings/server/functions/settings.tests.ts index 4028486beb2a..30dc4f07aa79 100644 --- a/app/settings/server/functions/settings.tests.ts +++ b/app/settings/server/functions/settings.tests.ts @@ -1,14 +1,11 @@ /* eslint-disable @typescript-eslint/camelcase */ /* eslint-env mocha */ import chai, { expect } from 'chai'; -import spies from 'chai-spies'; import { Settings } from './settings.mocks'; import { SettingsRegistry } from '../SettingsRegistry'; import { CachedSettings } from '../CachedSettings'; -chai.use(spies); - describe('Settings', () => { beforeEach(() => { Settings.insertCalls = 0; diff --git a/app/settings/server/raw.tests.js b/app/settings/server/raw.tests.js index 7a9d6bccd6cf..6fff45c19f80 100644 --- a/app/settings/server/raw.tests.js +++ b/app/settings/server/raw.tests.js @@ -1,10 +1,7 @@ /* eslint-env mocha */ import chai, { expect } from 'chai'; -import spies from 'chai-spies'; import rewire from 'rewire'; -chai.use(spies); - describe('Raw Settings', () => { let rawModule; const cache = new Map(); diff --git a/app/utils/lib/getURL.tests.js b/app/utils/lib/getURL.tests.js index 5ef7af6a4888..debd55c742ff 100644 --- a/app/utils/lib/getURL.tests.js +++ b/app/utils/lib/getURL.tests.js @@ -119,7 +119,7 @@ const testCasesForOptions = (description, options) => { }); }; -describe.only('getURL', () => { +describe('getURL', () => { testCasesForOptions('getURL with no CDN, no PREFIX for http://localhost:3000/', { _cdn_prefix: '', _root_url_path_prefix: '', diff --git a/client/lib/download.spec.ts b/client/lib/download.spec.ts index d28c60abbbff..29748fae10d3 100644 --- a/client/lib/download.spec.ts +++ b/client/lib/download.spec.ts @@ -1,13 +1,10 @@ import 'jsdom-global/register'; import chai from 'chai'; -import chaiSpies from 'chai-spies'; import { describe, it } from 'mocha'; import { withBlobUrls } from '../../tests/utils/client/withBlobUrls'; import { download, downloadAs, downloadCsvAs, downloadJsonAs } from './download'; -chai.use(chaiSpies); - describe('download', () => { it('should work', () => { const listener = chai.spy(); diff --git a/package-lock.json b/package-lock.json index fb1db4f80cf9..ebb4490da964 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9481,6 +9481,15 @@ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.19.tgz", "integrity": "sha512-jRJgpRBuY+7izT7/WNXP/LsMO9YonsstuL+xuvycDyESpoDoIAsMd7suwpB4h9oEWB+ZlPTqJJ8EHomzNhwTPQ==" }, + "@types/chai-datetime": { + "version": "0.0.37", + "resolved": "https://registry.npmjs.org/@types/chai-datetime/-/chai-datetime-0.0.37.tgz", + "integrity": "sha512-teAlKuUV2mxuN0hRxfSXnk7v5lDZUtQWMZ72pIvm5OJ8SuMmgjQgNiebha+MYr7EiSVCQxDY8yH1j7TIXy3nEQ==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, "@types/chai-spies": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.3.tgz", diff --git a/package.json b/package.json index c5865032264d..b5fc30f02243 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "@types/bcrypt": "^5.0.0", "@types/body-parser": "^1.19.0", "@types/chai": "^4.2.19", + "@types/chai-datetime": "0.0.37", "@types/chai-spies": "^1.0.3", "@types/clipboard": "^2.0.1", "@types/dompurify": "^2.2.2", diff --git a/tests/setup/chaiPlugins.ts b/tests/setup/chaiPlugins.ts new file mode 100644 index 000000000000..86077951000b --- /dev/null +++ b/tests/setup/chaiPlugins.ts @@ -0,0 +1,6 @@ +import chai from 'chai'; +import chaiSpies from 'chai-spies'; +import chaiDatetime from 'chai-datetime'; + +chai.use(chaiSpies); +chai.use(chaiDatetime); From ddc6e015464dea41686fd046bcf1d93101cd2960 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 15:45:39 -0300 Subject: [PATCH 03/23] Replace assert with chai --- app/highlight-words/tests/helper.tests.js | 11 +- app/lib/tests/server.tests.js | 11 +- app/mailer/tests/api.spec.ts | 21 ++-- app/markdown/tests/client.tests.js | 6 +- app/mentions/tests/client.tests.js | 108 ++++++++--------- app/mentions/tests/server.tests.js | 42 +++---- app/models/server/raw/Sessions.tests.js | 113 +++++++++--------- app/ui-utils/tests/server.tests.js | 4 +- .../client/views/app/tests/helpers.tests.js | 24 ++-- app/utils/lib/getURL.tests.js | 25 ++-- 10 files changed, 182 insertions(+), 183 deletions(-) diff --git a/app/highlight-words/tests/helper.tests.js b/app/highlight-words/tests/helper.tests.js index 28c5fd075164..7c3c4e13ba7d 100644 --- a/app/highlight-words/tests/helper.tests.js +++ b/app/highlight-words/tests/helper.tests.js @@ -1,7 +1,8 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; + +import { expect } from 'chai'; import { highlightWords, getRegexHighlight, getRegexHighlightUrl } from '../client/helper'; @@ -14,7 +15,7 @@ describe('helper', () => { urlRegex: getRegexHighlightUrl(highlight), }))); - assert.equal(res, 'here is some word'); + expect(res).to.be.equal('here is some word'); }); describe('handles links', () => { @@ -25,7 +26,7 @@ describe('helper', () => { urlRegex: getRegexHighlightUrl(highlight), }))); - assert.equal(res, 'here we go https://somedomain.com/here-some.word/pulls more words after'); + expect(res).to.be.equal('here we go https://somedomain.com/here-some.word/pulls more words after'); }); it('not highlighting two links', () => { @@ -36,7 +37,7 @@ describe('helper', () => { urlRegex: getRegexHighlightUrl(highlight), }))); - assert.equal(res, msg); + expect(res).to.be.equal(msg); }); it('not highlighting link but keep words on message highlighted', () => { @@ -46,7 +47,7 @@ describe('helper', () => { urlRegex: getRegexHighlightUrl(highlight), }))); - assert.equal(res, 'here we go https://somedomain.com/here-some.foo/pulls more foo after'); + expect(res).to.be.equal('here we go https://somedomain.com/here-some.foo/pulls more foo after'); }); }); }); diff --git a/app/lib/tests/server.tests.js b/app/lib/tests/server.tests.js index cc6a4de04b1a..c7b684eb4a9e 100644 --- a/app/lib/tests/server.tests.js +++ b/app/lib/tests/server.tests.js @@ -1,6 +1,5 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; import { expect } from 'chai'; import './server.mocks.js'; @@ -41,11 +40,11 @@ describe('PasswordPolicyClass', () => { describe('Password tests with default options', () => { it('should allow all passwords', () => { const passwordPolice = new PasswordPolicyClass({ throwError: false }); - assert.equal(passwordPolice.validate(), false); - assert.equal(passwordPolice.validate(''), false); - assert.equal(passwordPolice.validate(' '), false); - assert.equal(passwordPolice.validate('a'), true); - assert.equal(passwordPolice.validate('aaaaaaaaa'), true); + expect(passwordPolice.validate()).to.be.equal(false); + expect(passwordPolice.validate('')).to.be.equal(false); + expect(passwordPolice.validate(' ')).to.be.equal(false); + expect(passwordPolice.validate('a')).to.be.equal(true); + expect(passwordPolice.validate('aaaaaaaaa')).to.be.equal(true); }); }); }); diff --git a/app/mailer/tests/api.spec.ts b/app/mailer/tests/api.spec.ts index 4c858c1b23e5..c51cf7bf3bca 100644 --- a/app/mailer/tests/api.spec.ts +++ b/app/mailer/tests/api.spec.ts @@ -1,5 +1,6 @@ /* eslint-env mocha */ -import assert from 'assert'; + +import { expect } from 'chai'; import { replaceVariables } from '../server/replaceVariables'; @@ -13,55 +14,55 @@ describe('Mailer-API', function() { describe('single key', function functionName() { it(`should be equal to test ${ i18n.key }`, () => { - assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test {key}', (_match, key) => i18n[key])); + expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test {key}', (_match, key) => i18n[key])); }); }); describe('multiple keys', function functionName() { it(`should be equal to test ${ i18n.key } and ${ i18n.key }`, () => { - assert.strictEqual(`test ${ i18n.key } and ${ i18n.key }`, replaceVariables('test {key} and {key}', (_match, key) => i18n[key])); + expect(`test ${ i18n.key } and ${ i18n.key }`).to.be.equal(replaceVariables('test {key} and {key}', (_match, key) => i18n[key])); }); }); describe('key with a trailing space', function functionName() { it(`should be equal to test ${ i18n.key }`, () => { - assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test {key }', (_match, key) => i18n[key])); + expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test {key }', (_match, key) => i18n[key])); }); }); describe('key with a leading space', function functionName() { it(`should be equal to test ${ i18n.key }`, () => { - assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test { key}', (_match, key) => i18n[key])); + expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test { key}', (_match, key) => i18n[key])); }); }); describe('key with leading and trailing spaces', function functionName() { it(`should be equal to test ${ i18n.key }`, () => { - assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test { key }', (_match, key) => i18n[key])); + expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test { key }', (_match, key) => i18n[key])); }); }); describe('key with multiple words', function functionName() { it(`should be equal to test ${ i18n.key }`, () => { - assert.strictEqual(`test ${ i18n.key }`, replaceVariables('test {key ignore}', (_match, key) => i18n[key])); + expect(`test ${ i18n.key }`).to.be.equal(replaceVariables('test {key ignore}', (_match, key) => i18n[key])); }); }); describe('key with multiple opening brackets', function functionName() { it(`should be equal to test {${ i18n.key }`, () => { - assert.strictEqual(`test {${ i18n.key }`, replaceVariables('test {{key}', (_match, key) => i18n[key])); + expect(`test {${ i18n.key }`).to.be.equal(replaceVariables('test {{key}', (_match, key) => i18n[key])); }); }); describe('key with multiple closing brackets', function functionName() { it(`should be equal to test ${ i18n.key }}`, () => { - assert.strictEqual(`test ${ i18n.key }}`, replaceVariables('test {key}}', (_match, key) => i18n[key])); + expect(`test ${ i18n.key }}`).to.be.equal(replaceVariables('test {key}}', (_match, key) => i18n[key])); }); }); describe('key with multiple opening and closing brackets', function functionName() { it(`should be equal to test {${ i18n.key }}`, () => { - assert.strictEqual(`test {${ i18n.key }}`, replaceVariables('test {{key}}', (_match, key) => i18n[key])); + expect(`test {${ i18n.key }}`).to.be.equal(replaceVariables('test {{key}}', (_match, key) => i18n[key])); }); }); }); diff --git a/app/markdown/tests/client.tests.js b/app/markdown/tests/client.tests.js index e7ef58713ef2..b33c74806062 100644 --- a/app/markdown/tests/client.tests.js +++ b/app/markdown/tests/client.tests.js @@ -1,8 +1,8 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; - import './client.mocks.js'; + +import { expect } from 'chai'; import { escapeHTML } from '@rocket.chat/string-helpers'; import { original } from '../lib/parser/original/original'; @@ -375,7 +375,7 @@ const blockcodeFiltered = { 'Here```code```lies': 'Herecodelies', }; -const defaultObjectTest = (result, object, objectKey) => assert.equal(result.html, object[objectKey]); +const defaultObjectTest = (result, object, objectKey) => expect(result.html).to.be.equal(object[objectKey]); const testObject = (object, parser = original, test = defaultObjectTest) => { Object.keys(object).forEach((objectKey) => { diff --git a/app/mentions/tests/client.tests.js b/app/mentions/tests/client.tests.js index 5854ec14ba6a..dd65a323dc79 100644 --- a/app/mentions/tests/client.tests.js +++ b/app/mentions/tests/client.tests.js @@ -1,11 +1,11 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; +import { expect } from 'chai'; import { MentionsParser } from '../lib/MentionsParser'; let mentionsParser; -beforeEach(function functionName() { +beforeEach(() => { mentionsParser = new MentionsParser({ pattern: '[0-9a-zA-Z-_.]+', me: () => 'me', @@ -17,15 +17,15 @@ describe('Mention', function() { const regexp = '[0-9a-zA-Z-_.]+'; beforeEach(() => { mentionsParser.pattern = () => regexp; }); - describe('by function', function functionName() { + describe('by function', () => { it(`should be equal to ${ regexp }`, () => { - assert.equal(regexp, mentionsParser.pattern); + expect(regexp).to.be.equal(mentionsParser.pattern); }); }); - describe('by const', function functionName() { + describe('by const', () => { it(`should be equal to ${ regexp }`, () => { - assert.equal(regexp, mentionsParser.pattern); + expect(regexp).to.be.equal(mentionsParser.pattern); }); }); }); @@ -33,15 +33,15 @@ describe('Mention', function() { describe('get useRealName', () => { beforeEach(() => { mentionsParser.useRealName = () => true; }); - describe('by function', function functionName() { + describe('by function', () => { it('should be true', () => { - assert.equal(true, mentionsParser.useRealName); + expect(true).to.be.equal(mentionsParser.useRealName); }); }); - describe('by const', function functionName() { + describe('by const', () => { it('should be true', () => { - assert.equal(true, mentionsParser.useRealName); + expect(true).to.be.equal(mentionsParser.useRealName); }); }); }); @@ -49,24 +49,24 @@ describe('Mention', function() { describe('get me', () => { const me = 'me'; - describe('by function', function functionName() { + describe('by function', () => { beforeEach(() => { mentionsParser.me = () => me; }); it(`should be equal to ${ me }`, () => { - assert.equal(me, mentionsParser.me); + expect(me).to.be.equal(mentionsParser.me); }); }); - describe('by const', function functionName() { + describe('by const', () => { beforeEach(() => { mentionsParser.me = me; }); it(`should be equal to ${ me }`, () => { - assert.equal(me, mentionsParser.me); + expect(me).to.be.equal(mentionsParser.me); }); }); }); - describe('getUserMentions', function functionName() { + describe('getUserMentions', () => { describe('for simple text, no mentions', () => { const result = []; [ @@ -75,7 +75,7 @@ describe('Mention', function() { ] .forEach((text) => { it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getUserMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions(text)); }); }); }); @@ -93,20 +93,20 @@ describe('Mention', function() { ] .forEach((text) => { it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getUserMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions(text)); }); }); it.skip('should return without the "." from "@rocket.cat."', () => { - assert.deepEqual(result, mentionsParser.getUserMentions('@rocket.cat.')); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions('@rocket.cat.')); }); it.skip('should return without the "_" from "@rocket.cat_"', () => { - assert.deepEqual(result, mentionsParser.getUserMentions('@rocket.cat_')); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions('@rocket.cat_')); }); it.skip('should return without the "-" from "@rocket.cat-"', () => { - assert.deepEqual(result, mentionsParser.getUserMentions('@rocket.cat-')); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions('@rocket.cat-')); }); }); @@ -121,13 +121,13 @@ describe('Mention', function() { ] .forEach((text) => { it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getUserMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions(text)); }); }); }); }); - describe('getChannelMentions', function functionName() { + describe('getChannelMentions', () => { describe('for simple text, no mentions', () => { const result = []; [ @@ -136,7 +136,7 @@ describe('Mention', function() { ] .forEach((text) => { it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getChannelMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text)); }); }); }); @@ -151,20 +151,20 @@ describe('Mention', function() { 'hello #general, how are you?', ].forEach((text) => { it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getChannelMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text)); }); }); it.skip('should return without the "." from "#general."', () => { - assert.deepEqual(result, mentionsParser.getUserMentions('#general.')); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions('#general.')); }); it.skip('should return without the "_" from "#general_"', () => { - assert.deepEqual(result, mentionsParser.getUserMentions('#general_')); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions('#general_')); }); it.skip('should return without the "-" from "#general."', () => { - assert.deepEqual(result, mentionsParser.getUserMentions('#general-')); + expect(result).to.be.deep.equal(mentionsParser.getUserMentions('#general-')); }); }); @@ -178,7 +178,7 @@ describe('Mention', function() { 'hello #general #other, how are you?', ].forEach((text) => { it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getChannelMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text)); }); }); }); @@ -189,7 +189,7 @@ describe('Mention', function() { 'http://localhost/#general', ].forEach((text) => { it(`should return nothing from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getChannelMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text)); }); }); }); @@ -200,7 +200,7 @@ describe('Mention', function() { 'http://localhost/#general #general', ].forEach((text) => { it(`should return "${ JSON.stringify(result) }" from "${ text }"`, () => { - assert.deepEqual(result, mentionsParser.getChannelMentions(text)); + expect(result).to.be.deep.equal(mentionsParser.getChannelMentions(text)); }); }); }); @@ -216,29 +216,29 @@ describe('replace methods', function() { describe('replaceUsers', () => { it('should render for @all', () => { const result = mentionsParser.replaceUsers('@all', message, 'me'); - assert.equal(result, 'all'); + expect(result).to.be.equal('all'); }); const str2 = 'rocket.cat'; it(`should render for "@${ str2 }"`, () => { const result = mentionsParser.replaceUsers(`@${ str2 }`, message, 'me'); - assert.equal(result, `${ str2 }`); + expect(result).to.be.equal(`${ str2 }`); }); it(`should render for "hello ${ str2 }"`, () => { const result = mentionsParser.replaceUsers(`hello @${ str2 }`, message, 'me'); - assert.equal(result, `hello ${ str2 }`); + expect(result).to.be.equal(`hello ${ str2 }`); }); it('should render for unknow/private user "hello @unknow"', () => { const result = mentionsParser.replaceUsers('hello @unknow', message, 'me'); - assert.equal(result, 'hello @unknow'); + expect(result).to.be.equal('hello @unknow'); }); it('should render for me', () => { const result = mentionsParser.replaceUsers('hello @me', message, 'me'); - assert.equal(result, 'hello me'); + expect(result).to.be.equal('hello me'); }); }); @@ -249,7 +249,7 @@ describe('replace methods', function() { it('should render for @all', () => { const result = mentionsParser.replaceUsers('@all', message, 'me'); - assert.equal(result, 'all'); + expect(result).to.be.equal('all'); }); const str2 = 'rocket.cat'; @@ -257,12 +257,12 @@ describe('replace methods', function() { it(`should render for "@${ str2 }"`, () => { const result = mentionsParser.replaceUsers(`@${ str2 }`, message, 'me'); - assert.equal(result, `${ str2Name }`); + expect(result).to.be.equal(`${ str2Name }`); }); it(`should render for "hello @${ str2 }"`, () => { const result = mentionsParser.replaceUsers(`hello @${ str2 }`, message, 'me'); - assert.equal(result, `hello ${ str2Name }`); + expect(result).to.be.equal(`hello ${ str2Name }`); }); const specialchars = 'specialchars'; @@ -270,46 +270,46 @@ describe('replace methods', function() { it(`should escape special characters in "hello @${ specialchars }"`, () => { const result = mentionsParser.replaceUsers(`hello @${ specialchars }`, message, 'me'); - assert.equal(result, `hello ${ specialcharsName }`); + expect(result).to.be.equal(`hello ${ specialcharsName }`); }); it(`should render for "hello
@${ str2 }
"`, () => { const result = mentionsParser.replaceUsers(`hello
@${ str2 }
`, message, 'me'); - assert.equal(result, `hello
${ str2Name }
`); + expect(result).to.be.equal(`hello
${ str2Name }
`); }); it('should render for unknow/private user "hello @unknow"', () => { const result = mentionsParser.replaceUsers('hello @unknow', message, 'me'); - assert.equal(result, 'hello @unknow'); + expect(result).to.be.equal('hello @unknow'); }); it('should render for me', () => { const result = mentionsParser.replaceUsers('hello @me', message, 'me'); - assert.equal(result, 'hello Me'); + expect(result).to.be.equal('hello Me'); }); }); describe('replaceChannels', () => { it('should render for #general', () => { const result = mentionsParser.replaceChannels('#general', message); - assert.equal('#general', result); + expect('<).to.be.equal(class="mention-link mention-link--room" data-channel="42">#general', result); }); const str2 = '#rocket.cat'; it(`should render for ${ str2 }`, () => { const result = mentionsParser.replaceChannels(str2, message); - assert.equal(result, `${ str2 }`); + expect(result).to.be.equal(`${ str2 }`); }); it(`should render for "hello ${ str2 }"`, () => { const result = mentionsParser.replaceChannels(`hello ${ str2 }`, message); - assert.equal(result, `hello ${ str2 }`); + expect(result).to.be.equal(`hello ${ str2 }`); }); it('should render for unknow/private channel "hello #unknow"', () => { const result = mentionsParser.replaceChannels('hello #unknow', message); - assert.equal(result, 'hello #unknow'); + expect(result).to.be.equal('hello #unknow'); }); }); @@ -317,25 +317,25 @@ describe('replace methods', function() { it('should render for #general', () => { message.html = '#general'; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, '#general'); + expect(result.html).to.be.equal('#general'); }); it('should render for "#general and @rocket.cat', () => { message.html = '#general and @rocket.cat'; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, '#general and rocket.cat'); + expect(result.html).to.be.equal('#general and rocket.cat'); }); it('should render for "', () => { message.html = ''; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, ''); + expect(result.html).to.be.equal(''); }); it('should render for "simple text', () => { message.html = 'simple text'; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, 'simple text'); + expect(result.html).to.be.equal('simple text'); }); }); @@ -347,25 +347,25 @@ describe('replace methods', function() { it('should render for #general', () => { message.html = '#general'; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, '#general'); + expect(result.html).to.be.equal('#general'); }); it('should render for "#general and @rocket.cat', () => { message.html = '#general and @rocket.cat'; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, '#general and Rocket.Cat'); + expect(result.html).to.be.equal('#general and Rocket.Cat'); }); it('should render for "', () => { message.html = ''; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, ''); + expect(result.html).to.be.equal(''); }); it('should render for "simple text', () => { message.html = 'simple text'; const result = mentionsParser.parse(message, 'me'); - assert.equal(result.html, 'simple text'); + expect(result.html).to.be.equal('simple text'); }); }); }); diff --git a/app/mentions/tests/server.tests.js b/app/mentions/tests/server.tests.js index 30bc07564984..4ed2a5d80bcb 100644 --- a/app/mentions/tests/server.tests.js +++ b/app/mentions/tests/server.tests.js @@ -1,6 +1,6 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; +import { expect } from 'chai'; import MentionsServer from '../server/Mentions'; @@ -43,7 +43,7 @@ describe('Mention Server', () => { }; const expected = []; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); }); describe('for one user', () => { @@ -69,7 +69,7 @@ describe('Mention Server', () => { username: 'all', }]; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); it('should return "here"', () => { const message = { @@ -80,7 +80,7 @@ describe('Mention Server', () => { username: 'here', }]; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); it('should return "rocket.cat"', () => { const message = { @@ -91,7 +91,7 @@ describe('Mention Server', () => { username: 'rocket.cat', }]; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); }); describe('for two user', () => { @@ -107,7 +107,7 @@ describe('Mention Server', () => { username: 'here', }]; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); it('should return "here and rocket.cat"', () => { const message = { @@ -121,7 +121,7 @@ describe('Mention Server', () => { username: 'rocket.cat', }]; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); it('should return "here, rocket.cat, jon"', () => { @@ -139,7 +139,7 @@ describe('Mention Server', () => { username: 'jon', }]; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); }); @@ -150,7 +150,7 @@ describe('Mention Server', () => { }; const expected = []; const result = mention.getUsersByMentions(message); - assert.deepEqual(expected, result); + expect(expected).to.be.deep.equal(result); }); }); }); @@ -164,7 +164,7 @@ describe('Mention Server', () => { name: 'general', }]; const result = mention.getChannelbyMentions(message); - assert.deepEqual(result, expected); + expect(result).to.be.deep.equal(expected); }); it('should return nothing"', () => { const message = { @@ -172,7 +172,7 @@ describe('Mention Server', () => { }; const expected = []; const result = mention.getChannelbyMentions(message); - assert.deepEqual(result, expected); + expect(result).to.be.deep.equal(expected); }); }); describe('execute', () => { @@ -185,7 +185,7 @@ describe('Mention Server', () => { name: 'general', }]; const result = mention.getChannelbyMentions(message); - assert.deepEqual(result, expected); + expect(result).to.be.deep.equal(expected); }); it('should return nothing"', () => { const message = { @@ -197,7 +197,7 @@ describe('Mention Server', () => { channels: [], }; const result = mention.execute(message); - assert.deepEqual(result, expected); + expect(result).to.be.deep.equal(expected); }); }); @@ -207,13 +207,13 @@ describe('Mention Server', () => { describe('constant', () => { it('should return the informed value', () => { mention.messageMaxAll = 4; - assert.deepEqual(mention.messageMaxAll, 4); + expect(mention.messageMaxAll).to.be.deep.equal(4); }); }); describe('function', () => { it('should return the informed value', () => { mention.messageMaxAll = () => 4; - assert.deepEqual(mention.messageMaxAll, 4); + expect(mention.messageMaxAll).to.be.deep.equal(4); }); }); }); @@ -222,13 +222,13 @@ describe('Mention Server', () => { describe('constant', () => { it('should return the informed value', () => { mention.getUsers = 4; - assert.deepEqual(mention.getUsers(), 4); + expect(mention.getUsers()).to.be.deep.equal(4); }); }); describe('function', () => { it('should return the informed value', () => { mention.getUsers = () => 4; - assert.deepEqual(mention.getUsers(), 4); + expect(mention.getUsers()).to.be.deep.equal(4); }); }); }); @@ -237,13 +237,13 @@ describe('Mention Server', () => { describe('constant', () => { it('should return the informed value', () => { mention.getChannels = 4; - assert.deepEqual(mention.getChannels(), 4); + expect(mention.getChannels()).to.be.deep.equal(4); }); }); describe('function', () => { it('should return the informed value', () => { mention.getChannels = () => 4; - assert.deepEqual(mention.getChannels(), 4); + expect(mention.getChannels()).to.be.deep.equal(4); }); }); }); @@ -252,13 +252,13 @@ describe('Mention Server', () => { describe('constant', () => { it('should return the informed value', () => { mention.getChannel = true; - assert.deepEqual(mention.getChannel(), true); + expect(mention.getChannel()).to.be.deep.equal(true); }); }); describe('function', () => { it('should return the informed value', () => { mention.getChannel = () => true; - assert.deepEqual(mention.getChannel(), true); + expect(mention.getChannel()).to.be.deep.equal(true); }); }); }); diff --git a/app/models/server/raw/Sessions.tests.js b/app/models/server/raw/Sessions.tests.js index 7dd157e9e681..195dd3c8af7b 100644 --- a/app/models/server/raw/Sessions.tests.js +++ b/app/models/server/raw/Sessions.tests.js @@ -1,7 +1,6 @@ /* eslint-env mocha */ -import assert from 'assert'; - +import { expect } from 'chai'; import { MongoMemoryServer } from 'mongodb-memory-server'; const { MongoClient } = require('mongodb'); @@ -280,14 +279,14 @@ describe('Sessions Aggregates', () => { it('should have sessions_dates data saved', () => { const collection = db.collection('sessions_dates'); return collection.find().toArray() - .then((docs) => assert.strictEqual(docs.length, DATA.sessions_dates.length)); + .then((docs) => expect(docs.length).to.be.equal(DATA.sessions_dates.length)); }); it('should match sessions between 2018-12-11 and 2019-1-10', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 1, day: 10 }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ $and: [{ $or: [ { year: { $gt: 2018 } }, @@ -307,8 +306,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 31); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(31); + expect(docs).to.be.deep.equal([ { _id: '2018-12-11', year: 2018, month: 12, day: 11 }, { _id: '2018-12-12', year: 2018, month: 12, day: 12 }, { _id: '2018-12-13', year: 2018, month: 12, day: 13 }, @@ -348,7 +347,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 2, day: 10 }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, $and: [{ $or: [ @@ -367,8 +366,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 31); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.deep.equal(31); + expect(docs).to.be.deep.equal([ { _id: '2019-1-11', year: 2019, month: 1, day: 11 }, { _id: '2019-1-12', year: 2019, month: 1, day: 12 }, { _id: '2019-1-13', year: 2019, month: 1, day: 13 }, @@ -408,7 +407,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 5, day: 31 }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, month: 5, day: { $gte: 1, $lte: 31 }, @@ -418,8 +417,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 31); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(31); + expect(docs).to.be.deep.equal([ { _id: '2019-5-1', year: 2019, month: 5, day: 1 }, { _id: '2019-5-2', year: 2019, month: 5, day: 2 }, { _id: '2019-5-3', year: 2019, month: 5, day: 3 }, @@ -459,7 +458,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 4, day: 30 }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, month: 4, day: { $gte: 1, $lte: 30 }, @@ -469,8 +468,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 30); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(30); + expect(docs).to.be.deep.equal([ { _id: '2019-4-1', year: 2019, month: 4, day: 1 }, { _id: '2019-4-2', year: 2019, month: 4, day: 2 }, { _id: '2019-4-3', year: 2019, month: 4, day: 3 }, @@ -509,7 +508,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 2, day: 28 }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, month: 2, day: { $gte: 1, $lte: 28 }, @@ -519,8 +518,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 28); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(28); + expect(docs).to.be.deep.equal([ { _id: '2019-2-1', year: 2019, month: 2, day: 1 }, { _id: '2019-2-2', year: 2019, month: 2, day: 2 }, { _id: '2019-2-3', year: 2019, month: 2, day: 3 }, @@ -557,7 +556,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 2, day: 27 }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, $and: [{ $or: [ @@ -576,8 +575,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 31); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(31); + expect(docs).to.be.deep.equal([ { _id: '2019-1-28', year: 2019, month: 1, day: 28 }, { _id: '2019-1-29', year: 2019, month: 1, day: 29 }, { _id: '2019-1-30', year: 2019, month: 1, day: 30 }, @@ -616,7 +615,7 @@ describe('Sessions Aggregates', () => { it('should have sessions data saved', () => { const collection = db.collection('sessions'); return collection.find().toArray() - .then((docs) => assert.strictEqual(docs.length, DATA.sessions.length)); + .then((docs) => expect(docs.length).to.be.equal(DATA.sessions.length)); }); it('should generate daily sessions', () => { @@ -629,8 +628,8 @@ describe('Sessions Aggregates', () => { await collection.insertMany(docs); - assert.strictEqual(docs.length, 3); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(3); + expect(docs).to.be.deep.equal([{ _id: 'xPZXw9xqM3kKshsse-2019-5-2', time: 5814, sessions: 3, @@ -726,8 +725,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueUsersOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31 }) .then((docs) => { - assert.strictEqual(docs.length, 1); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(1); + expect(docs).to.be.deep.equal([{ count: 2, roles: [{ count: 1, @@ -750,8 +749,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueUsersOfYesterday(collection, { year: 2019, month: 5, day: 1 }) .then((docs) => { - assert.strictEqual(docs.length, 1); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(1); + expect(docs).to.be.deep.equal([{ count: 1, roles: [{ count: 1, @@ -769,8 +768,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueUsersOfYesterday(collection, { year: 2019, month: 5, day: 2 }) .then((docs) => { - assert.strictEqual(docs.length, 1); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(1); + expect(docs).to.be.deep.equal([{ count: 1, roles: [{ count: 1, @@ -788,8 +787,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueDevicesOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31 }) .then((docs) => { - assert.strictEqual(docs.length, 2); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(2); + expect(docs).to.be.deep.equal([{ count: 3, time: 9695, type: 'browser', @@ -809,8 +808,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueDevicesOfYesterday(collection, { year: 2019, month: 5, day: 2 }) .then((docs) => { - assert.strictEqual(docs.length, 2); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(2); + expect(docs).to.be.deep.equal([{ count: 2, time: 5528, type: 'browser', @@ -830,8 +829,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueOSOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31 }) .then((docs) => { - assert.strictEqual(docs.length, 2); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(2); + expect(docs).to.be.deep.equal([{ count: 3, time: 9695, name: 'Mac OS', @@ -849,8 +848,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueOSOfYesterday(collection, { year: 2019, month: 5, day: 2 }) .then((docs) => { - assert.strictEqual(docs.length, 2); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(2); + expect(docs).to.be.deep.equal([{ count: 2, time: 5528, name: 'Mac OS', @@ -868,7 +867,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 1, day: 4, type: 'week' }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ $and: [{ $or: [ { year: { $gt: 2018 } }, @@ -888,8 +887,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 7); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(7); + expect(docs).to.be.deep.equal([ { _id: '2018-12-29', year: 2018, month: 12, day: 29 }, { _id: '2018-12-30', year: 2018, month: 12, day: 30 }, { _id: '2018-12-31', year: 2018, month: 12, day: 31 }, @@ -905,7 +904,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 2, day: 4, type: 'week' }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, $and: [{ $or: [ @@ -924,8 +923,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 7); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(7); + expect(docs).to.be.deep.equal([ { _id: '2019-1-29', year: 2019, month: 1, day: 29 }, { _id: '2019-1-30', year: 2019, month: 1, day: 30 }, { _id: '2019-1-31', year: 2019, month: 1, day: 31 }, @@ -941,7 +940,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 5, day: 7, type: 'week' }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, month: 5, day: { $gte: 1, $lte: 7 }, @@ -951,8 +950,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 7); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(7); + expect(docs).to.be.deep.equal([ { _id: '2019-5-1', year: 2019, month: 5, day: 1 }, { _id: '2019-5-2', year: 2019, month: 5, day: 2 }, { _id: '2019-5-3', year: 2019, month: 5, day: 3 }, @@ -968,7 +967,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions_dates'); const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 5, day: 14, type: 'week' }); - assert.deepStrictEqual($match, { + expect($match).to.be.deep.equal({ year: 2019, month: 5, day: { $gte: 8, $lte: 14 }, @@ -978,8 +977,8 @@ describe('Sessions Aggregates', () => { $match, }]).toArray() .then((docs) => { - assert.strictEqual(docs.length, 7); - assert.deepStrictEqual(docs, [ + expect(docs.length).to.be.equal(7); + expect(docs).to.be.deep.equal([ { _id: '2019-5-8', year: 2019, month: 5, day: 8 }, { _id: '2019-5-9', year: 2019, month: 5, day: 9 }, { _id: '2019-5-10', year: 2019, month: 5, day: 10 }, @@ -995,7 +994,7 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueUsersOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31, type: 'week' }) .then((docs) => { - assert.strictEqual(docs.length, 0); + expect(docs.length).to.be.equal(0); }); }); @@ -1003,8 +1002,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueUsersOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 7, type: 'week' }) .then((docs) => { - assert.strictEqual(docs.length, 1); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(1); + expect(docs).to.be.deep.equal([{ count: 2, roles: [{ count: 1, @@ -1027,8 +1026,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueDevicesOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 7, type: 'week' }) .then((docs) => { - assert.strictEqual(docs.length, 2); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(2); + expect(docs).to.be.deep.equal([{ count: 3, time: 9695, type: 'browser', @@ -1048,8 +1047,8 @@ describe('Sessions Aggregates', () => { const collection = db.collection('sessions'); return aggregates.getUniqueOSOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 7 }) .then((docs) => { - assert.strictEqual(docs.length, 2); - assert.deepStrictEqual(docs, [{ + expect(docs.length).to.be.equal(2); + expect(docs).to.be.deep.equal([{ count: 3, time: 9695, name: 'Mac OS', diff --git a/app/ui-utils/tests/server.tests.js b/app/ui-utils/tests/server.tests.js index a1592b60602a..1df993a1f35a 100644 --- a/app/ui-utils/tests/server.tests.js +++ b/app/ui-utils/tests/server.tests.js @@ -1,6 +1,6 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; +import { expect } from 'chai'; import './server.mocks.js'; import { messageProperties } from '../lib/MessageProperties'; @@ -16,7 +16,7 @@ describe('Message Properties', () => { describe('Check Message Length', () => { Object.keys(messages).forEach((objectKey) => { it('should treat emojis as single characters', () => { - assert.equal(messageProperties.length(objectKey), messages[objectKey]); + expect(messageProperties.length(objectKey)).to.be.equal(messages[objectKey]); }); }); }); diff --git a/app/ui/client/views/app/tests/helpers.tests.js b/app/ui/client/views/app/tests/helpers.tests.js index 110a050454eb..15672f4293cb 100644 --- a/app/ui/client/views/app/tests/helpers.tests.js +++ b/app/ui/client/views/app/tests/helpers.tests.js @@ -1,6 +1,6 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; +import { expect } from 'chai'; import { timeAgo } from '../helpers'; @@ -16,9 +16,9 @@ describe('Helpers', () => { const func = (a) => a; - assert.equal(timeAgo(t1, func, now), '1:00 AM'); - assert.equal(timeAgo(t2, func, now), '10:00 AM'); - assert.equal(timeAgo(t3, func, now), '2:30 PM'); + expect(timeAgo(t1, func, now)).to.be.equal('1:00 AM'); + expect(timeAgo(t2, func, now)).to.be.equal('10:00 AM'); + expect(timeAgo(t3, func, now)).to.be.equal('2:30 PM'); }); it('returns "yesterday" when the passed value is on the day before', () => { @@ -30,9 +30,9 @@ describe('Helpers', () => { const func = (a) => a; - assert.equal(timeAgo(t1, func, now), 'yesterday'); - assert.equal(timeAgo(t2, func, now), 'yesterday'); - assert.equal(timeAgo(t3, func, now), 'yesterday'); + expect(timeAgo(t1, func, now)).to.be.equal('yesterday'); + expect(timeAgo(t2, func, now)).to.be.equal('yesterday'); + expect(timeAgo(t3, func, now)).to.be.equal('yesterday'); }); it('returns formated date when the passed value two or more days before', () => { @@ -46,11 +46,11 @@ describe('Helpers', () => { const func = () => 'should not be called'; - assert.equal(timeAgo(t1, func, now), 'Jun 18, 2018'); - assert.equal(timeAgo(t2, func, now), 'Jun 10, 2018'); - assert.equal(timeAgo(t3, func, now), 'May 10, 2018'); - assert.equal(timeAgo(t4, func, now), 'May 20, 2018'); - assert.equal(timeAgo(t5, func, now), 'Nov 10, 2017'); + expect(timeAgo(t1, func, now)).to.be.equal('Jun 18, 2018'); + expect(timeAgo(t2, func, now)).to.be.equal('Jun 10, 2018'); + expect(timeAgo(t3, func, now)).to.be.equal('May 10, 2018'); + expect(timeAgo(t4, func, now)).to.be.equal('May 20, 2018'); + expect(timeAgo(t5, func, now)).to.be.equal('Nov 10, 2017'); }); }); }); diff --git a/app/utils/lib/getURL.tests.js b/app/utils/lib/getURL.tests.js index debd55c742ff..6e0396f27f40 100644 --- a/app/utils/lib/getURL.tests.js +++ b/app/utils/lib/getURL.tests.js @@ -1,7 +1,6 @@ /* eslint-env mocha */ import 'babel-polyfill'; -import assert from 'assert'; - +import { expect } from 'chai'; import s from 'underscore.string'; import { _getURL } from './getURL'; @@ -12,17 +11,17 @@ const testPaths = (o, _processPath) => { processPath = (path) => _processPath(o._root_url_path_prefix + path); } - assert.equal(_getURL('', o), processPath('')); - assert.equal(_getURL('/', o), processPath('')); - assert.equal(_getURL('//', o), processPath('')); - assert.equal(_getURL('///', o), processPath('')); - assert.equal(_getURL('/channel', o), processPath('/channel')); - assert.equal(_getURL('/channel/', o), processPath('/channel')); - assert.equal(_getURL('/channel//', o), processPath('/channel')); - assert.equal(_getURL('/channel/123', o), processPath('/channel/123')); - assert.equal(_getURL('/channel/123/', o), processPath('/channel/123')); - assert.equal(_getURL('/channel/123?id=456&name=test', o), processPath('/channel/123?id=456&name=test')); - assert.equal(_getURL('/channel/123/?id=456&name=test', o), processPath('/channel/123?id=456&name=test')); + expect(_getURL('', o)).to.be.equal(processPath('')); + expect(_getURL('/', o)).to.be.equal(processPath('')); + expect(_getURL('//', o)).to.be.equal(processPath('')); + expect(_getURL('///', o)).to.be.equal(processPath('')); + expect(_getURL('/channel', o)).to.be.equal(processPath('/channel')); + expect(_getURL('/channel/', o)).to.be.equal(processPath('/channel')); + expect(_getURL('/channel//', o)).to.be.equal(processPath('/channel')); + expect(_getURL('/channel/123', o)).to.be.equal(processPath('/channel/123')); + expect(_getURL('/channel/123/', o)).to.be.equal(processPath('/channel/123')); + expect(_getURL('/channel/123?id=456&name=test', o)).to.be.equal(processPath('/channel/123?id=456&name=test')); + expect(_getURL('/channel/123/?id=456&name=test', o)).to.be.equal(processPath('/channel/123?id=456&name=test')); }; const getCloudUrl = (_site_url, path) => { From da87bfa8fefc4fbcf52fe138632d74c00bbb4c76 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 16:59:23 -0300 Subject: [PATCH 04/23] Set eslint mocha env for test files --- .eslintrc | 10 ++++++++++ app/apps/server/tests/messages.tests.js | 1 - app/custom-oauth/server/transform_helpers.tests.js | 2 -- app/highlight-words/tests/helper.tests.js | 2 -- app/lib/tests/server.tests.js | 1 - app/mailer/tests/api.spec.ts | 2 -- app/markdown/tests/client.tests.js | 1 - app/mentions/tests/client.tests.js | 1 - app/mentions/tests/server.tests.js | 1 - app/meteor-accounts-saml/tests/server.tests.ts | 1 - app/models/server/raw/Sessions.tests.js | 2 -- .../server/functions/getSettingDefaults.tests.ts | 2 -- .../server/functions/overrideGenerator.tests.ts | 2 -- app/settings/server/functions/settings.tests.ts | 1 - .../server/functions/validateSettings.tests.ts | 1 - app/settings/server/raw.tests.js | 1 - app/statistics/server/lib/UAParserCustom.tests.js | 2 -- .../server/lib/getMostImportantRole.tests.js | 2 -- app/ui-utils/tests/server.tests.js | 1 - app/ui/client/views/app/tests/helpers.tests.js | 1 - app/utils/lib/getURL.tests.js | 1 - server/lib/fileUtils.tests.ts | 1 - tests/cypress/integration/14-setting-permissions.js | 1 - tests/cypress/integration/16-discussion.js | 1 - 24 files changed, 10 insertions(+), 31 deletions(-) diff --git a/.eslintrc b/.eslintrc index 8833cddb4eec..212170f0e1c2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -144,6 +144,16 @@ "version": "detect" } } + }, + { + "files": [ + "**/*.tests.js", + "**/*.tests.ts", + "**/*.spec.ts" + ], + "env": { + "mocha": true + } } ] } diff --git a/app/apps/server/tests/messages.tests.js b/app/apps/server/tests/messages.tests.js index 1ba8549ef5a5..a81bbf7a69d2 100644 --- a/app/apps/server/tests/messages.tests.js +++ b/app/apps/server/tests/messages.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import mock from 'mock-require'; import chai from 'chai'; diff --git a/app/custom-oauth/server/transform_helpers.tests.js b/app/custom-oauth/server/transform_helpers.tests.js index ec1475780e68..5139edb2410e 100644 --- a/app/custom-oauth/server/transform_helpers.tests.js +++ b/app/custom-oauth/server/transform_helpers.tests.js @@ -1,5 +1,3 @@ -/* eslint-env mocha */ - import { expect } from 'chai'; import { diff --git a/app/highlight-words/tests/helper.tests.js b/app/highlight-words/tests/helper.tests.js index 7c3c4e13ba7d..71904c92a142 100644 --- a/app/highlight-words/tests/helper.tests.js +++ b/app/highlight-words/tests/helper.tests.js @@ -1,5 +1,3 @@ -/* eslint-env mocha */ - import 'babel-polyfill'; import { expect } from 'chai'; diff --git a/app/lib/tests/server.tests.js b/app/lib/tests/server.tests.js index c7b684eb4a9e..8483fbad2d03 100644 --- a/app/lib/tests/server.tests.js +++ b/app/lib/tests/server.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import { expect } from 'chai'; diff --git a/app/mailer/tests/api.spec.ts b/app/mailer/tests/api.spec.ts index c51cf7bf3bca..5bb1397c62e7 100644 --- a/app/mailer/tests/api.spec.ts +++ b/app/mailer/tests/api.spec.ts @@ -1,5 +1,3 @@ -/* eslint-env mocha */ - import { expect } from 'chai'; import { replaceVariables } from '../server/replaceVariables'; diff --git a/app/markdown/tests/client.tests.js b/app/markdown/tests/client.tests.js index b33c74806062..8fbba658893c 100644 --- a/app/markdown/tests/client.tests.js +++ b/app/markdown/tests/client.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import './client.mocks.js'; diff --git a/app/mentions/tests/client.tests.js b/app/mentions/tests/client.tests.js index dd65a323dc79..ac913f1b2f2d 100644 --- a/app/mentions/tests/client.tests.js +++ b/app/mentions/tests/client.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import { expect } from 'chai'; diff --git a/app/mentions/tests/server.tests.js b/app/mentions/tests/server.tests.js index 4ed2a5d80bcb..594feee86d29 100644 --- a/app/mentions/tests/server.tests.js +++ b/app/mentions/tests/server.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import { expect } from 'chai'; diff --git a/app/meteor-accounts-saml/tests/server.tests.ts b/app/meteor-accounts-saml/tests/server.tests.ts index 25a512fffe83..8e9cb7b24d57 100644 --- a/app/meteor-accounts-saml/tests/server.tests.ts +++ b/app/meteor-accounts-saml/tests/server.tests.ts @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import chai from 'chai'; diff --git a/app/models/server/raw/Sessions.tests.js b/app/models/server/raw/Sessions.tests.js index 195dd3c8af7b..8b284009c5a4 100644 --- a/app/models/server/raw/Sessions.tests.js +++ b/app/models/server/raw/Sessions.tests.js @@ -1,5 +1,3 @@ -/* eslint-env mocha */ - import { expect } from 'chai'; import { MongoMemoryServer } from 'mongodb-memory-server'; diff --git a/app/settings/server/functions/getSettingDefaults.tests.ts b/app/settings/server/functions/getSettingDefaults.tests.ts index 35d5f3f5c029..63581c43ce69 100644 --- a/app/settings/server/functions/getSettingDefaults.tests.ts +++ b/app/settings/server/functions/getSettingDefaults.tests.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/camelcase */ -/* eslint-env mocha */ import { expect } from 'chai'; import { getSettingDefaults } from './getSettingDefaults'; diff --git a/app/settings/server/functions/overrideGenerator.tests.ts b/app/settings/server/functions/overrideGenerator.tests.ts index a1881527ed84..776cf8ce2cf9 100644 --- a/app/settings/server/functions/overrideGenerator.tests.ts +++ b/app/settings/server/functions/overrideGenerator.tests.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/camelcase */ -/* eslint-env mocha */ import { expect } from 'chai'; import { getSettingDefaults } from './getSettingDefaults'; diff --git a/app/settings/server/functions/settings.tests.ts b/app/settings/server/functions/settings.tests.ts index 30dc4f07aa79..b8a9b611fd3f 100644 --- a/app/settings/server/functions/settings.tests.ts +++ b/app/settings/server/functions/settings.tests.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/camelcase */ -/* eslint-env mocha */ import chai, { expect } from 'chai'; import { Settings } from './settings.mocks'; diff --git a/app/settings/server/functions/validateSettings.tests.ts b/app/settings/server/functions/validateSettings.tests.ts index 8891afcf6947..ba7d28f793f8 100644 --- a/app/settings/server/functions/validateSettings.tests.ts +++ b/app/settings/server/functions/validateSettings.tests.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/camelcase */ -/* eslint-env mocha */ import { expect } from 'chai'; import { validateSetting } from './validateSetting'; diff --git a/app/settings/server/raw.tests.js b/app/settings/server/raw.tests.js index 6fff45c19f80..190400c4f3ac 100644 --- a/app/settings/server/raw.tests.js +++ b/app/settings/server/raw.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import chai, { expect } from 'chai'; import rewire from 'rewire'; diff --git a/app/statistics/server/lib/UAParserCustom.tests.js b/app/statistics/server/lib/UAParserCustom.tests.js index 0d7b2da16f3d..18338bc05104 100644 --- a/app/statistics/server/lib/UAParserCustom.tests.js +++ b/app/statistics/server/lib/UAParserCustom.tests.js @@ -1,5 +1,3 @@ -/* eslint-env mocha */ - import { expect } from 'chai'; import { UAParserMobile, UAParserDesktop } from './UAParserCustom'; diff --git a/app/statistics/server/lib/getMostImportantRole.tests.js b/app/statistics/server/lib/getMostImportantRole.tests.js index 464f14b0e574..5d3c7a6efa84 100644 --- a/app/statistics/server/lib/getMostImportantRole.tests.js +++ b/app/statistics/server/lib/getMostImportantRole.tests.js @@ -1,5 +1,3 @@ -/* eslint-env mocha */ - import { expect } from 'chai'; import { getMostImportantRole } from './getMostImportantRole'; diff --git a/app/ui-utils/tests/server.tests.js b/app/ui-utils/tests/server.tests.js index 1df993a1f35a..6f22dfdf221a 100644 --- a/app/ui-utils/tests/server.tests.js +++ b/app/ui-utils/tests/server.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import { expect } from 'chai'; diff --git a/app/ui/client/views/app/tests/helpers.tests.js b/app/ui/client/views/app/tests/helpers.tests.js index 15672f4293cb..dc7692897cfd 100644 --- a/app/ui/client/views/app/tests/helpers.tests.js +++ b/app/ui/client/views/app/tests/helpers.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import { expect } from 'chai'; diff --git a/app/utils/lib/getURL.tests.js b/app/utils/lib/getURL.tests.js index 6e0396f27f40..1e6d77015e21 100644 --- a/app/utils/lib/getURL.tests.js +++ b/app/utils/lib/getURL.tests.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import 'babel-polyfill'; import { expect } from 'chai'; import s from 'underscore.string'; diff --git a/server/lib/fileUtils.tests.ts b/server/lib/fileUtils.tests.ts index 6c5b5853acbb..5e82ad360864 100644 --- a/server/lib/fileUtils.tests.ts +++ b/server/lib/fileUtils.tests.ts @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import { expect } from 'chai'; import { fileName, joinPath } from './fileUtils'; diff --git a/tests/cypress/integration/14-setting-permissions.js b/tests/cypress/integration/14-setting-permissions.js index a8270a0a00e4..4bcf2ac1d423 100644 --- a/tests/cypress/integration/14-setting-permissions.js +++ b/tests/cypress/integration/14-setting-permissions.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ import { assert } from 'chai'; import { adminUsername, adminEmail, adminPassword, username, email, password } from '../../data/user.js'; diff --git a/tests/cypress/integration/16-discussion.js b/tests/cypress/integration/16-discussion.js index 832a7c06fdd7..238d22dffac6 100644 --- a/tests/cypress/integration/16-discussion.js +++ b/tests/cypress/integration/16-discussion.js @@ -1,4 +1,3 @@ -/* eslint-env mocha */ /* eslint-disable func-names, prefer-arrow-callback, no-var, space-before-function-paren, quotes, prefer-template, no-undef, no-unused-vars*/ From 8966da1bfc935a06d1579b3f23c9f890eca04741 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 17:04:46 -0300 Subject: [PATCH 05/23] Replace import babel-polyfill with requiring runtime-generator --- .mocharc.js | 3 ++- app/apps/server/tests/messages.tests.js | 1 - app/highlight-words/tests/helper.tests.js | 2 -- app/lib/tests/server.tests.js | 2 -- app/markdown/tests/client.tests.js | 1 - app/mentions/tests/client.tests.js | 1 - app/mentions/tests/server.tests.js | 1 - app/meteor-accounts-saml/tests/server.tests.ts | 2 -- app/ui-utils/tests/server.tests.js | 1 - app/ui/client/views/app/tests/helpers.tests.js | 1 - app/utils/lib/getURL.tests.js | 1 - 11 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.mocharc.js b/.mocharc.js index 5f2dc164bca3..1fa6d816a663 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -2,8 +2,9 @@ module.exports = { require: [ - 'ts-node/register', '@babel/register', + 'regenerator-runtime/runtime', + 'ts-node/register', './tests/setup/chaiPlugins.ts', ], reporter: 'spec', diff --git a/app/apps/server/tests/messages.tests.js b/app/apps/server/tests/messages.tests.js index a81bbf7a69d2..64f09b1a918e 100644 --- a/app/apps/server/tests/messages.tests.js +++ b/app/apps/server/tests/messages.tests.js @@ -1,4 +1,3 @@ -import 'babel-polyfill'; import mock from 'mock-require'; import chai from 'chai'; diff --git a/app/highlight-words/tests/helper.tests.js b/app/highlight-words/tests/helper.tests.js index 71904c92a142..2b4e895d0e65 100644 --- a/app/highlight-words/tests/helper.tests.js +++ b/app/highlight-words/tests/helper.tests.js @@ -1,5 +1,3 @@ -import 'babel-polyfill'; - import { expect } from 'chai'; import { highlightWords, getRegexHighlight, getRegexHighlightUrl } from '../client/helper'; diff --git a/app/lib/tests/server.tests.js b/app/lib/tests/server.tests.js index 8483fbad2d03..a606cff94901 100644 --- a/app/lib/tests/server.tests.js +++ b/app/lib/tests/server.tests.js @@ -1,5 +1,3 @@ -import 'babel-polyfill'; - import { expect } from 'chai'; import './server.mocks.js'; diff --git a/app/markdown/tests/client.tests.js b/app/markdown/tests/client.tests.js index 8fbba658893c..8d741f696ddb 100644 --- a/app/markdown/tests/client.tests.js +++ b/app/markdown/tests/client.tests.js @@ -1,4 +1,3 @@ -import 'babel-polyfill'; import './client.mocks.js'; import { expect } from 'chai'; diff --git a/app/mentions/tests/client.tests.js b/app/mentions/tests/client.tests.js index ac913f1b2f2d..e90249dcf23c 100644 --- a/app/mentions/tests/client.tests.js +++ b/app/mentions/tests/client.tests.js @@ -1,4 +1,3 @@ -import 'babel-polyfill'; import { expect } from 'chai'; import { MentionsParser } from '../lib/MentionsParser'; diff --git a/app/mentions/tests/server.tests.js b/app/mentions/tests/server.tests.js index 594feee86d29..a1a77bac3058 100644 --- a/app/mentions/tests/server.tests.js +++ b/app/mentions/tests/server.tests.js @@ -1,4 +1,3 @@ -import 'babel-polyfill'; import { expect } from 'chai'; import MentionsServer from '../server/Mentions'; diff --git a/app/meteor-accounts-saml/tests/server.tests.ts b/app/meteor-accounts-saml/tests/server.tests.ts index 8e9cb7b24d57..0810c62fafd9 100644 --- a/app/meteor-accounts-saml/tests/server.tests.ts +++ b/app/meteor-accounts-saml/tests/server.tests.ts @@ -1,5 +1,3 @@ -import 'babel-polyfill'; - import chai from 'chai'; import '../../lib/tests/server.mocks.js'; diff --git a/app/ui-utils/tests/server.tests.js b/app/ui-utils/tests/server.tests.js index 6f22dfdf221a..5f2dfee9e891 100644 --- a/app/ui-utils/tests/server.tests.js +++ b/app/ui-utils/tests/server.tests.js @@ -1,4 +1,3 @@ -import 'babel-polyfill'; import { expect } from 'chai'; import './server.mocks.js'; diff --git a/app/ui/client/views/app/tests/helpers.tests.js b/app/ui/client/views/app/tests/helpers.tests.js index dc7692897cfd..77487ec1ea4c 100644 --- a/app/ui/client/views/app/tests/helpers.tests.js +++ b/app/ui/client/views/app/tests/helpers.tests.js @@ -1,4 +1,3 @@ -import 'babel-polyfill'; import { expect } from 'chai'; import { timeAgo } from '../helpers'; diff --git a/app/utils/lib/getURL.tests.js b/app/utils/lib/getURL.tests.js index 1e6d77015e21..1cccb926d941 100644 --- a/app/utils/lib/getURL.tests.js +++ b/app/utils/lib/getURL.tests.js @@ -1,4 +1,3 @@ -import 'babel-polyfill'; import { expect } from 'chai'; import s from 'underscore.string'; From 7302bc6ac309eaf594427cbae1f620f750ec6a63 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 17:11:39 -0300 Subject: [PATCH 06/23] Named-import chai functions --- app/apps/server/tests/messages.tests.js | 4 +- .../tests/server.tests.ts | 4 +- .../server/functions/settings.tests.ts | 34 +++++++------- app/settings/server/raw.tests.js | 6 +-- client/lib/download.spec.ts | 18 ++++---- client/lib/minimongo/bson.spec.ts | 40 ++++++++-------- client/lib/minimongo/comparisons.spec.ts | 46 +++++++++---------- client/lib/minimongo/lookups.spec.ts | 16 ++++--- 8 files changed, 83 insertions(+), 85 deletions(-) diff --git a/app/apps/server/tests/messages.tests.js b/app/apps/server/tests/messages.tests.js index 64f09b1a918e..9e4919731e8c 100644 --- a/app/apps/server/tests/messages.tests.js +++ b/app/apps/server/tests/messages.tests.js @@ -1,5 +1,5 @@ import mock from 'mock-require'; -import chai from 'chai'; +import { expect } from 'chai'; import { AppServerOrchestratorMock } from './mocks/orchestrator.mock'; import { appMessageMock, appMessageInvalidRoomMock } from './mocks/data/messages.data'; @@ -7,8 +7,6 @@ import { MessagesMock } from './mocks/models/Messages.mock'; import { RoomsMock } from './mocks/models/Rooms.mock'; import { UsersMock } from './mocks/models/Users.mock'; -const { expect } = chai; - mock('../../../models', './mocks/models'); mock('meteor/random', { id: () => 1, diff --git a/app/meteor-accounts-saml/tests/server.tests.ts b/app/meteor-accounts-saml/tests/server.tests.ts index 0810c62fafd9..2b64134e22a9 100644 --- a/app/meteor-accounts-saml/tests/server.tests.ts +++ b/app/meteor-accounts-saml/tests/server.tests.ts @@ -1,4 +1,4 @@ -import chai from 'chai'; +import { expect } from 'chai'; import '../../lib/tests/server.mocks.js'; import { AuthorizeRequest } from '../server/lib/generators/AuthorizeRequest'; @@ -36,8 +36,6 @@ import { privateKey, } from './data'; -const { expect } = chai; - describe('SAML', () => { describe('[AuthorizeRequest]', () => { describe('AuthorizeRequest.generate', () => { diff --git a/app/settings/server/functions/settings.tests.ts b/app/settings/server/functions/settings.tests.ts index b8a9b611fd3f..55692c7dc6d5 100644 --- a/app/settings/server/functions/settings.tests.ts +++ b/app/settings/server/functions/settings.tests.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import chai, { expect } from 'chai'; +import { expect, spy } from 'chai'; import { Settings } from './settings.mocks'; import { SettingsRegistry } from '../SettingsRegistry'; @@ -302,8 +302,8 @@ describe('Settings', () => { settings.initilized(); const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); - const spy = chai.spy(); - const spy2 = chai.spy(); + const spiedCallback1 = spy(); + const spiedCallback2 = spy(); settingsRegistry.addGroup('group', function() { this.section('section', function() { @@ -313,27 +313,27 @@ describe('Settings', () => { }); }); - settings.watch('setting_callback', spy, { debounce: 10 }); - settings.watchByRegex(/setting_callback/, spy2, { debounce: 10 }); + settings.watch('setting_callback', spiedCallback1, { debounce: 10 }); + settings.watchByRegex(/setting_callback/, spiedCallback2, { debounce: 10 }); setTimeout(() => { - expect(spy).to.have.been.called.exactly(1); - expect(spy2).to.have.been.called.exactly(1); - expect(spy).to.have.been.called.always.with('value1'); - expect(spy2).to.have.been.called.always.with('setting_callback', 'value1'); + expect(spiedCallback1).to.have.been.called.exactly(1); + expect(spiedCallback2).to.have.been.called.exactly(1); + expect(spiedCallback1).to.have.been.called.always.with('value1'); + expect(spiedCallback2).to.have.been.called.always.with('setting_callback', 'value1'); done(); }, settings.getConfig({ debounce: 10 }).debounce); }); it('should call `settings.watch` callback on setting changed registering before initialized', (done) => { - const spy = chai.spy(); - const spy2 = chai.spy(); + const spiedCallback1 = spy(); + const spiedCallback2 = spy(); const settings = new CachedSettings(); Settings.settings = settings; const settingsRegistry = new SettingsRegistry({ store: settings, model: Settings as any }); - settings.watch('setting_callback', spy, { debounce: 1 }); - settings.watchByRegex(/setting_callback/ig, spy2, { debounce: 1 }); + settings.watch('setting_callback', spiedCallback1, { debounce: 1 }); + settings.watchByRegex(/setting_callback/ig, spiedCallback2, { debounce: 1 }); settings.initilized(); settingsRegistry.addGroup('group', function() { @@ -346,10 +346,10 @@ describe('Settings', () => { setTimeout(() => { Settings.updateValueById('setting_callback', 'value3'); setTimeout(() => { - expect(spy).to.have.been.called.exactly(2); - expect(spy2).to.have.been.called.exactly(2); - expect(spy).to.have.been.called.with('value2'); - expect(spy).to.have.been.called.with('value3'); + expect(spiedCallback1).to.have.been.called.exactly(2); + expect(spiedCallback2).to.have.been.called.exactly(2); + expect(spiedCallback1).to.have.been.called.with('value2'); + expect(spiedCallback1).to.have.been.called.with('value3'); done(); }, settings.getConfig({ debounce: 10 }).debounce); }, settings.getConfig({ debounce: 10 }).debounce); diff --git a/app/settings/server/raw.tests.js b/app/settings/server/raw.tests.js index 190400c4f3ac..ecd1aeada491 100644 --- a/app/settings/server/raw.tests.js +++ b/app/settings/server/raw.tests.js @@ -1,4 +1,4 @@ -import chai, { expect } from 'chai'; +import { expect, spy } from 'chai'; import rewire from 'rewire'; describe('Raw Settings', () => { @@ -6,13 +6,13 @@ describe('Raw Settings', () => { const cache = new Map(); before('rewire deps', () => { - const spy = chai.spy(async (id) => { + const spied = spy(async (id) => { if (id === '1') { return 'some-setting-value'; } return null; }); rawModule = rewire('./raw'); - rawModule.__set__('setFromDB', spy); + rawModule.__set__('setFromDB', spied); rawModule.__set__('cache', cache); }); diff --git a/client/lib/download.spec.ts b/client/lib/download.spec.ts index 29748fae10d3..638c5b43bce8 100644 --- a/client/lib/download.spec.ts +++ b/client/lib/download.spec.ts @@ -1,5 +1,5 @@ import 'jsdom-global/register'; -import chai from 'chai'; +import { expect, spy } from 'chai'; import { describe, it } from 'mocha'; import { withBlobUrls } from '../../tests/utils/client/withBlobUrls'; @@ -7,13 +7,13 @@ import { download, downloadAs, downloadCsvAs, downloadJsonAs } from './download' describe('download', () => { it('should work', () => { - const listener = chai.spy(); + const listener = spy(); document.addEventListener('click', listener, false); download('about:blank', 'blank'); document.removeEventListener('click', listener, false); - chai.expect(listener).to.have.been.called(); + expect(listener).to.have.been.called(); }); }); @@ -21,13 +21,13 @@ describe('downloadAs', () => { withBlobUrls(); it('should work', () => { - const listener = chai.spy(); + const listener = spy(); document.addEventListener('click', listener, false); downloadAs({ data: [] }, 'blank'); document.removeEventListener('click', listener, false); - chai.expect(listener).to.have.been.called(); + expect(listener).to.have.been.called(); }); }); @@ -35,13 +35,13 @@ describe('downloadJsonAs', () => { withBlobUrls(); it('should work', () => { - const listener = chai.spy(); + const listener = spy(); document.addEventListener('click', listener, false); downloadJsonAs({}, 'blank'); document.removeEventListener('click', listener, false); - chai.expect(listener).to.have.been.called(); + expect(listener).to.have.been.called(); }); }); @@ -49,7 +49,7 @@ describe('downloadCsvAs', () => { withBlobUrls(); it('should work', () => { - const listener = chai.spy(); + const listener = spy(); document.addEventListener('click', listener, false); downloadCsvAs( @@ -61,6 +61,6 @@ describe('downloadCsvAs', () => { ); document.removeEventListener('click', listener, false); - chai.expect(listener).to.have.been.called(); + expect(listener).to.have.been.called(); }); }); diff --git a/client/lib/minimongo/bson.spec.ts b/client/lib/minimongo/bson.spec.ts index 1f2c5048f424..f71a38f95141 100644 --- a/client/lib/minimongo/bson.spec.ts +++ b/client/lib/minimongo/bson.spec.ts @@ -1,4 +1,4 @@ -import chai from 'chai'; +import { expect } from 'chai'; import { describe, it } from 'mocha'; import { getBSONType, compareBSONValues } from './bson'; @@ -6,32 +6,32 @@ import { BSONType } from './types'; describe('getBSONType', () => { it('should work', () => { - chai.expect(getBSONType(1)).to.be.equals(BSONType.Double); - chai.expect(getBSONType('xyz')).to.be.equals(BSONType.String); - chai.expect(getBSONType({})).to.be.equals(BSONType.Object); - chai.expect(getBSONType([])).to.be.equals(BSONType.Array); - chai.expect(getBSONType(new Uint8Array())).to.be.equals(BSONType.BinData); - chai.expect(getBSONType(undefined)).to.be.equals(BSONType.Object); - chai.expect(getBSONType(null)).to.be.equals(BSONType.Null); - chai.expect(getBSONType(false)).to.be.equals(BSONType.Boolean); - chai.expect(getBSONType(/.*/)).to.be.equals(BSONType.Regex); - chai.expect(getBSONType(() => true)).to.be.equals(BSONType.JavaScript); - chai.expect(getBSONType(new Date(0))).to.be.equals(BSONType.Date); + expect(getBSONType(1)).to.be.equals(BSONType.Double); + expect(getBSONType('xyz')).to.be.equals(BSONType.String); + expect(getBSONType({})).to.be.equals(BSONType.Object); + expect(getBSONType([])).to.be.equals(BSONType.Array); + expect(getBSONType(new Uint8Array())).to.be.equals(BSONType.BinData); + expect(getBSONType(undefined)).to.be.equals(BSONType.Object); + expect(getBSONType(null)).to.be.equals(BSONType.Null); + expect(getBSONType(false)).to.be.equals(BSONType.Boolean); + expect(getBSONType(/.*/)).to.be.equals(BSONType.Regex); + expect(getBSONType(() => true)).to.be.equals(BSONType.JavaScript); + expect(getBSONType(new Date(0))).to.be.equals(BSONType.Date); }); }); describe('compareBSONValues', () => { it('should work for the same types', () => { - chai.expect(compareBSONValues(2, 3)).to.be.equals(-1); - chai.expect(compareBSONValues('xyz', 'abc')).to.be.equals(1); - chai.expect(compareBSONValues({}, {})).to.be.equals(0); - chai.expect(compareBSONValues(true, false)).to.be.equals(1); - chai.expect(compareBSONValues(new Date(0), new Date(1))).to.be.equals(-1); + expect(compareBSONValues(2, 3)).to.be.equals(-1); + expect(compareBSONValues('xyz', 'abc')).to.be.equals(1); + expect(compareBSONValues({}, {})).to.be.equals(0); + expect(compareBSONValues(true, false)).to.be.equals(1); + expect(compareBSONValues(new Date(0), new Date(1))).to.be.equals(-1); }); it('should work for different types', () => { - chai.expect(compareBSONValues(2, null)).to.be.equals(1); - chai.expect(compareBSONValues('xyz', {})).to.be.equals(-1); - chai.expect(compareBSONValues(false, 3)).to.be.equals(1); + expect(compareBSONValues(2, null)).to.be.equals(1); + expect(compareBSONValues('xyz', {})).to.be.equals(-1); + expect(compareBSONValues(false, 3)).to.be.equals(1); }); }); diff --git a/client/lib/minimongo/comparisons.spec.ts b/client/lib/minimongo/comparisons.spec.ts index eb32433d9a81..3048223f51ac 100644 --- a/client/lib/minimongo/comparisons.spec.ts +++ b/client/lib/minimongo/comparisons.spec.ts @@ -1,4 +1,4 @@ -import chai from 'chai'; +import { expect } from 'chai'; import { describe, it } from 'mocha'; import { equals, isObject, flatSome, some, isEmptyArray } from './comparisons'; @@ -6,57 +6,57 @@ import { equals, isObject, flatSome, some, isEmptyArray } from './comparisons'; describe('Comparisons service', () => { describe('equals', () => { it('should return true if two numbers are equal', () => { - chai.expect(equals(1, 1)).to.be.equal(true); + expect(equals(1, 1)).to.be.equal(true); }); it('should return false if arguments are null or undefined', () => { - chai.expect(equals(undefined, null)).to.be.equal(false); - chai.expect(equals(null, undefined)).to.be.equal(false); + expect(equals(undefined, null)).to.be.equal(false); + expect(equals(null, undefined)).to.be.equal(false); }); it('should return false if arguments arent objects and they are not the same', () => { - chai.expect(equals('not', 'thesame')).to.be.equal(false); + expect(equals('not', 'thesame')).to.be.equal(false); }); it('should return true if date objects provided have the same value', () => { const currentDate = new Date(); - chai.expect(equals(currentDate, currentDate)).to.be.equal(true); + expect(equals(currentDate, currentDate)).to.be.equal(true); }); it('should return true if 2 equal UInt8Array are provided', () => { const arr1 = new Uint8Array([1, 2]); const arr2 = new Uint8Array([1, 2]); - chai.expect(equals(arr1, arr2)).to.be.equal(true); + expect(equals(arr1, arr2)).to.be.equal(true); }); it('should return true if 2 equal arrays are provided', () => { const arr1 = [1, 2, 4]; const arr2 = [1, 2, 4]; - chai.expect(equals(arr1, arr2)).to.be.equal(true); + expect(equals(arr1, arr2)).to.be.equal(true); }); it('should return false if 2 arrays with different length are provided', () => { const arr1 = [1, 4, 5]; const arr2 = [1, 4, 5, 7]; - chai.expect(equals(arr1, arr2)).to.be.equal(false); + expect(equals(arr1, arr2)).to.be.equal(false); }); it('should return true if the objects provided are "equal"', () => { const obj = { a: 1 }; const obj2 = obj; - chai.expect(equals(obj, obj2)).to.be.equal(true); + expect(equals(obj, obj2)).to.be.equal(true); }); it('should return true if both objects have the same keys', () => { const obj = { a: 1 }; const obj2 = { a: 1 }; - chai.expect(equals(obj, obj2)).to.be.equal(true); + expect(equals(obj, obj2)).to.be.equal(true); }); }); @@ -65,14 +65,14 @@ describe('Comparisons service', () => { const obj = {}; const func = (a: any): any => a; - chai.expect(isObject(obj)).to.be.equal(true); - chai.expect(isObject(func)).to.be.equal(true); + expect(isObject(obj)).to.be.equal(true); + expect(isObject(func)).to.be.equal(true); }); it('should return false for other data types', () => { - chai.expect(isObject(1)).to.be.equal(false); - chai.expect(isObject(true)).to.be.equal(false); - chai.expect(isObject('212')).to.be.equal(false); + expect(isObject(1)).to.be.equal(false); + expect(isObject(true)).to.be.equal(false); + expect(isObject('212')).to.be.equal(false); }); }); @@ -81,14 +81,14 @@ describe('Comparisons service', () => { const arr = [1, 2, 4, 6, 9]; const isEven = (v: number): boolean => v % 2 === 0; - chai.expect(flatSome(arr, isEven)).to.be.equal(true); + expect(flatSome(arr, isEven)).to.be.equal(true); }); it('should run the function on the value when its not an array', () => { const val = 1; const isEven = (v: number): boolean => v % 2 === 0; - chai.expect(flatSome(val, isEven)).to.be.equal(false); + expect(flatSome(val, isEven)).to.be.equal(false); }); }); @@ -102,7 +102,7 @@ describe('Comparisons service', () => { return v % 2 === 0; }; - chai.expect(some(arr, isEven)).to.be.equal(true); + expect(some(arr, isEven)).to.be.equal(true); }); it('should run the function on the value when its not an array', () => { @@ -114,21 +114,21 @@ describe('Comparisons service', () => { return v % 2 === 0; }; - chai.expect(some(val, isEven)).to.be.equal(false); + expect(some(val, isEven)).to.be.equal(false); }); }); describe('isEmptyArray', () => { it('should return true if array is empty', () => { - chai.expect(isEmptyArray([])).to.be.equal(true); + expect(isEmptyArray([])).to.be.equal(true); }); it('should return false if value is not an array', () => { - chai.expect(isEmptyArray(1)).to.be.equal(false); + expect(isEmptyArray(1)).to.be.equal(false); }); it('should return false if array is not empty', () => { - chai.expect(isEmptyArray([1, 2])).to.be.equal(false); + expect(isEmptyArray([1, 2])).to.be.equal(false); }); }); }); diff --git a/client/lib/minimongo/lookups.spec.ts b/client/lib/minimongo/lookups.spec.ts index 1056a3d7f5c8..3bae10346b62 100644 --- a/client/lib/minimongo/lookups.spec.ts +++ b/client/lib/minimongo/lookups.spec.ts @@ -1,15 +1,17 @@ -import chai from 'chai'; +import { expect } from 'chai'; import { describe, it } from 'mocha'; import { createLookupFunction } from './lookups'; describe('createLookupFunction', () => { it('should work', () => { - chai.expect(createLookupFunction('a.x')({ a: { x: 1 } })).to.be.deep.equals([1]); - chai.expect(createLookupFunction('a.x')({ a: { x: [1] } })).to.be.deep.equals([[1]]); - chai.expect(createLookupFunction('a.x')({ a: 5 })).to.be.deep.equals([undefined]); - chai - .expect(createLookupFunction('a.x')({ a: [{ x: 1 }, { x: [2] }, { y: 3 }] })) - .to.be.deep.equals([1, [2], undefined]); + expect(createLookupFunction('a.x')({ a: { x: 1 } })).to.be.deep.equals([1]); + expect(createLookupFunction('a.x')({ a: { x: [1] } })).to.be.deep.equals([[1]]); + expect(createLookupFunction('a.x')({ a: 5 })).to.be.deep.equals([undefined]); + expect(createLookupFunction('a.x')({ a: [{ x: 1 }, { x: [2] }, { y: 3 }] })).to.be.deep.equals([ + 1, + [2], + undefined, + ]); }); }); From dd3a2b0a169b4c71eaca218720caef0c37912bc9 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 17:36:26 -0300 Subject: [PATCH 07/23] Move Web API mocks --- client/lib/download.spec.ts | 10 ++-------- tests/mocks/web/register.ts | 14 ++++++++++++++ tests/utils/client/withBlobUrls.ts | 29 ----------------------------- 3 files changed, 16 insertions(+), 37 deletions(-) create mode 100644 tests/mocks/web/register.ts delete mode 100644 tests/utils/client/withBlobUrls.ts diff --git a/client/lib/download.spec.ts b/client/lib/download.spec.ts index 638c5b43bce8..9b3e96142764 100644 --- a/client/lib/download.spec.ts +++ b/client/lib/download.spec.ts @@ -1,8 +1,8 @@ -import 'jsdom-global/register'; +import '../../tests/mocks/web/register'; + import { expect, spy } from 'chai'; import { describe, it } from 'mocha'; -import { withBlobUrls } from '../../tests/utils/client/withBlobUrls'; import { download, downloadAs, downloadCsvAs, downloadJsonAs } from './download'; describe('download', () => { @@ -18,8 +18,6 @@ describe('download', () => { }); describe('downloadAs', () => { - withBlobUrls(); - it('should work', () => { const listener = spy(); document.addEventListener('click', listener, false); @@ -32,8 +30,6 @@ describe('downloadAs', () => { }); describe('downloadJsonAs', () => { - withBlobUrls(); - it('should work', () => { const listener = spy(); document.addEventListener('click', listener, false); @@ -46,8 +42,6 @@ describe('downloadJsonAs', () => { }); describe('downloadCsvAs', () => { - withBlobUrls(); - it('should work', () => { const listener = spy(); document.addEventListener('click', listener, false); diff --git a/tests/mocks/web/register.ts b/tests/mocks/web/register.ts new file mode 100644 index 000000000000..010c55680c44 --- /dev/null +++ b/tests/mocks/web/register.ts @@ -0,0 +1,14 @@ +import 'jsdom-global/register'; + +const blobs = new Map(); + +window.URL.createObjectURL = (blob: Blob): string => { + const uuid = Math.random().toString(36).slice(2); + const url = `blob://${ uuid }`; + blobs.set(url, blob); + return url; +}; + +window.URL.revokeObjectURL = (url: string): void => { + blobs.delete(url); +}; diff --git a/tests/utils/client/withBlobUrls.ts b/tests/utils/client/withBlobUrls.ts deleted file mode 100644 index c375672d8396..000000000000 --- a/tests/utils/client/withBlobUrls.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { before, after } from 'mocha'; - -export const withBlobUrls = (): void => { - let createObjectURL: typeof URL.createObjectURL; - let revokeObjectURL: typeof URL.revokeObjectURL; - - before(() => { - const blobs = new Map(); - - createObjectURL = window.URL.createObjectURL; - revokeObjectURL = window.URL.revokeObjectURL; - - window.URL.createObjectURL = (blob: Blob): string => { - const uuid = Math.random().toString(36).slice(2); - const url = `blob://${ uuid }`; - blobs.set(url, blob); - return url; - }; - - window.URL.revokeObjectURL = (url: string): void => { - blobs.delete(url); - }; - }); - - after(() => { - window.URL.createObjectURL = createObjectURL; - window.URL.revokeObjectURL = revokeObjectURL; - }); -}; From c9c0286712883bd65b737c02ac1ad8c964ee8613 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 19:06:59 -0300 Subject: [PATCH 08/23] Unify configurations --- mocha_end_to_end.opts.js => .mocharc.api.js | 8 +- .mocharc.base.json | 11 + .mocharc.js | 10 +- package-lock.json | 998 -------------------- package.json | 4 +- tests/cypress/integration/12-settings.js | 4 +- tests/end-to-end/api/00-miscellaneous.js | 4 +- 7 files changed, 18 insertions(+), 1021 deletions(-) rename mocha_end_to_end.opts.js => .mocharc.api.js (60%) create mode 100644 .mocharc.base.json diff --git a/mocha_end_to_end.opts.js b/.mocharc.api.js similarity index 60% rename from mocha_end_to_end.opts.js rename to .mocharc.api.js index 36850cb81209..6369f654297a 100644 --- a/mocha_end_to_end.opts.js +++ b/.mocharc.api.js @@ -1,13 +1,7 @@ 'use strict'; module.exports = { - require: [ - 'babel-mocha-es6-compiler', - 'babel-polyfill', - ], - reporter: 'spec', - ui: 'bdd', - extension: 'js,ts', + ...require('./.mocharc.base.json'), // see https://github.com/mochajs/mocha/issues/3916 timeout: 10000, bail: true, file: 'tests/end-to-end/teardown.js', diff --git a/.mocharc.base.json b/.mocharc.base.json new file mode 100644 index 000000000000..bcc3f72717c9 --- /dev/null +++ b/.mocharc.base.json @@ -0,0 +1,11 @@ +{ + "require": [ + "@babel/register", + "regenerator-runtime/runtime", + "ts-node/register", + "./tests/setup/chaiPlugins.ts" + ], + "reporter": "spec", + "ui": "bdd", + "extension": "js,ts" +} diff --git a/.mocharc.js b/.mocharc.js index 1fa6d816a663..2c23ab486f7e 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -1,15 +1,7 @@ 'use strict'; module.exports = { - require: [ - '@babel/register', - 'regenerator-runtime/runtime', - 'ts-node/register', - './tests/setup/chaiPlugins.ts', - ], - reporter: 'spec', - ui: 'bdd', - extension: ['js', 'ts'], + ...require('./.mocharc.base.json'), // see https://github.com/mochajs/mocha/issues/3916 spec: [ 'app/**/*.tests.js', 'app/**/*.tests.ts', diff --git a/package-lock.json b/package-lock.json index ebb4490da964..2921c090d3b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12554,201 +12554,6 @@ } } }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - } - } - }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-builder-react-jsx": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", - "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "esutils": "^2.0.2" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-explode-class": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", - "dev": true, - "requires": { - "babel-helper-bindify-decorators": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, "babel-loader": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", @@ -12888,77 +12693,6 @@ "babel-runtime": "^6.22.0" } }, - "babel-mocha-es6-compiler": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/babel-mocha-es6-compiler/-/babel-mocha-es6-compiler-0.1.0.tgz", - "integrity": "sha1-QMnkBoCvRhWP7usntJQUtrgOxDg=", - "dev": true, - "requires": { - "babel-core": "~6.9.0", - "babel-plugin-add-module-exports": "~0.2.1", - "babel-preset-es2015": "~6.3.13", - "babel-preset-react": "~6.3.13", - "babel-preset-stage-0": "~6.3.13" - }, - "dependencies": { - "babel-core": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.9.1.tgz", - "integrity": "sha1-SNRx7r9N5GngqUL+RW3MlLGL6A0=", - "dev": true, - "requires": { - "babel-code-frame": "^6.8.0", - "babel-generator": "^6.9.0", - "babel-helpers": "^6.8.0", - "babel-messages": "^6.8.0", - "babel-register": "^6.9.0", - "babel-runtime": "^6.9.1", - "babel-template": "^6.9.0", - "babel-traverse": "^6.9.0", - "babel-types": "^6.9.1", - "babylon": "^6.7.0", - "convert-source-map": "^1.1.0", - "debug": "^2.1.1", - "json5": "^0.4.0", - "lodash": "^4.2.0", - "minimatch": "^2.0.3", - "path-exists": "^1.0.0", - "path-is-absolute": "^1.0.0", - "private": "^0.1.6", - "shebang-regex": "^1.0.0", - "slash": "^1.0.0", - "source-map": "^0.5.0" - } - }, - "json5": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz", - "integrity": "sha1-BUNS5MTIDIbAkjh31EneF2pzLI0=", - "dev": true - }, - "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", - "dev": true, - "requires": { - "brace-expansion": "^1.0.0" - } - }, - "path-exists": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz", - "integrity": "sha1-1aiZjrce83p0w06w2eum6HjuoIE=", - "dev": true - } - } - }, - "babel-plugin-add-module-exports": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", - "integrity": "sha1-mumh9KjcZ/DN7E9K7aHkOl/2XiU=", - "dev": true - }, "babel-plugin-add-react-displayname": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz", @@ -12989,15 +12723,6 @@ "integrity": "sha1-z1RS6Bx7gD+3lZ8QRayI4uwo/3Y=", "dev": true }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -13156,613 +12881,12 @@ } } }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", - "dev": true - }, - "babel-plugin-syntax-class-constructor-call": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", - "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", - "dev": true - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", - "dev": true - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", - "dev": true - }, - "babel-plugin-syntax-do-expressions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", - "integrity": "sha1-V0d1YTmqJtOQ0JQQsDdEugfkeW0=", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-export-extensions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", - "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", - "dev": true - }, - "babel-plugin-syntax-flow": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", - "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", - "dev": true - }, - "babel-plugin-syntax-function-bind": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", - "integrity": "sha1-SMSV8Xe98xqYHnMvVa3AvdJgH0Y=", - "dev": true - }, "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", "dev": true }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-generators": "^6.5.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-class-constructor-call": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", - "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", - "dev": true, - "requires": { - "babel-plugin-syntax-class-constructor-call": "^6.18.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", - "dev": true, - "requires": { - "babel-helper-explode-class": "^6.24.1", - "babel-plugin-syntax-decorators": "^6.13.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-do-expressions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz", - "integrity": "sha1-KMyvkoEtlJws0SgfaQyP3EaK6bs=", - "dev": true, - "requires": { - "babel-plugin-syntax-do-expressions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-export-extensions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", - "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", - "dev": true, - "requires": { - "babel-plugin-syntax-export-extensions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-flow-strip-types": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", - "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", - "dev": true, - "requires": { - "babel-plugin-syntax-flow": "^6.18.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-function-bind": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz", - "integrity": "sha1-xvuOlqwpajELjPjqQBRiQH3fapc=", - "dev": true, - "requires": { - "babel-plugin-syntax-function-bind": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.26.0" - } - }, - "babel-plugin-transform-react-display-name": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", - "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-react-jsx": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", - "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", - "dev": true, - "requires": { - "babel-helper-builder-react-jsx": "^6.24.1", - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-react-jsx-source": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", - "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", - "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" - }, - "dependencies": { - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - } - } - }, - "babel-preset-es2015": { - "version": "6.3.13", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.3.13.tgz", - "integrity": "sha1-l9zn7ykuGMubK3VF2AxZPCjZUX8=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.3.13", - "babel-plugin-transform-es2015-arrow-functions": "^6.3.13", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.3.13", - "babel-plugin-transform-es2015-block-scoping": "^6.3.13", - "babel-plugin-transform-es2015-classes": "^6.3.13", - "babel-plugin-transform-es2015-computed-properties": "^6.3.13", - "babel-plugin-transform-es2015-destructuring": "^6.3.13", - "babel-plugin-transform-es2015-for-of": "^6.3.13", - "babel-plugin-transform-es2015-function-name": "^6.3.13", - "babel-plugin-transform-es2015-literals": "^6.3.13", - "babel-plugin-transform-es2015-modules-commonjs": "^6.3.13", - "babel-plugin-transform-es2015-object-super": "^6.3.13", - "babel-plugin-transform-es2015-parameters": "^6.3.13", - "babel-plugin-transform-es2015-shorthand-properties": "^6.3.13", - "babel-plugin-transform-es2015-spread": "^6.3.13", - "babel-plugin-transform-es2015-sticky-regex": "^6.3.13", - "babel-plugin-transform-es2015-template-literals": "^6.3.13", - "babel-plugin-transform-es2015-typeof-symbol": "^6.3.13", - "babel-plugin-transform-es2015-unicode-regex": "^6.3.13", - "babel-plugin-transform-regenerator": "^6.3.13" - } - }, - "babel-preset-react": { - "version": "6.3.13", - "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.3.13.tgz", - "integrity": "sha1-E9VeBqZfqqoHw5v2Op2DbgMhFvo=", - "dev": true, - "requires": { - "babel-plugin-syntax-flow": "^6.3.13", - "babel-plugin-syntax-jsx": "^6.3.13", - "babel-plugin-transform-flow-strip-types": "^6.3.13", - "babel-plugin-transform-react-display-name": "^6.3.13", - "babel-plugin-transform-react-jsx": "^6.3.13", - "babel-plugin-transform-react-jsx-source": "^6.3.13" - } - }, - "babel-preset-stage-0": { - "version": "6.3.13", - "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.3.13.tgz", - "integrity": "sha1-eKN8VvCzmI8qeZMtywzrj/N3sNE=", - "dev": true, - "requires": { - "babel-plugin-transform-do-expressions": "^6.3.13", - "babel-plugin-transform-function-bind": "^6.3.13", - "babel-preset-stage-1": "^6.3.13" - } - }, - "babel-preset-stage-1": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", - "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", - "dev": true, - "requires": { - "babel-plugin-transform-class-constructor-call": "^6.24.1", - "babel-plugin-transform-export-extensions": "^6.22.0", - "babel-preset-stage-2": "^6.24.1" - } - }, - "babel-preset-stage-2": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", - "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", - "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators": "^6.24.1", - "babel-preset-stage-3": "^6.24.1" - } - }, - "babel-preset-stage-3": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", - "dev": true, - "requires": { - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-generator-functions": "^6.24.1", - "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-plugin-transform-exponentiation-operator": "^6.24.1", - "babel-plugin-transform-object-rest-spread": "^6.22.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - }, - "dependencies": { - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "dev": true - } - } - }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -13787,19 +12911,6 @@ } } }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, "babel-traverse": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", @@ -17152,15 +16263,6 @@ "repeat-string": "^1.5.4" } }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -22111,16 +21213,6 @@ "react-is": "^16.7.0" } }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -23130,15 +22222,6 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -28450,12 +27533,6 @@ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -31893,17 +30970,6 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -31930,40 +30996,6 @@ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -32325,15 +31357,6 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -33065,12 +32088,6 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, "slice-ansi": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", @@ -33285,15 +32302,6 @@ "urix": "^0.1.0" } }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -35692,12 +34700,6 @@ "escape-string-regexp": "^1.0.2" } }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, "trim-trailing-lines": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", diff --git a/package.json b/package.json index b5fc30f02243..e624d7411d08 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "coverage": "nyc -r html mocha --config ./.mocharc.js", "testci": "node .scripts/start.js", "testui": "cypress run --project tests", - "testapi": "mocha --config ./mocha_end_to_end.opts.js", + "testapi": "mocha --config ./.mocharc.api.js", "testunit": "mocha --config ./.mocharc.js", "testunit-watch": "mocha --watch --config ./.mocharc.js", "test": "npm run testapi && npm run testui", @@ -112,9 +112,7 @@ "autoprefixer": "^9.8.6", "babel-eslint": "^10.1.0", "babel-loader": "^8.2.2", - "babel-mocha-es6-compiler": "^0.1.0", "babel-plugin-array-includes": "^2.0.3", - "babel-polyfill": "^6.26.0", "chai": "^4.3.4", "chai-datetime": "^1.8.0", "chai-spies": "^1.0.0", diff --git a/tests/cypress/integration/12-settings.js b/tests/cypress/integration/12-settings.js index be803c1f5c30..c413e0c3b348 100644 --- a/tests/cypress/integration/12-settings.js +++ b/tests/cypress/integration/12-settings.js @@ -46,8 +46,8 @@ describe('[Api Settings Change]', () => { }); it('/login', () => { - expect(credentials).to.have.property('X-Auth-Token').with.length.at.least(1); - expect(credentials).to.have.property('X-User-Id').with.length.at.least(1); + expect(credentials).to.have.property('X-Auth-Token').with.lengthOf.at.least(1); + expect(credentials).to.have.property('X-User-Id').with.lengthOf.at.least(1); }); describe('message edit:', () => { diff --git a/tests/end-to-end/api/00-miscellaneous.js b/tests/end-to-end/api/00-miscellaneous.js index 6dc6b084ae44..e8d04bda5480 100644 --- a/tests/end-to-end/api/00-miscellaneous.js +++ b/tests/end-to-end/api/00-miscellaneous.js @@ -43,8 +43,8 @@ describe('miscellaneous', function() { }); it('/login', () => { - expect(credentials).to.have.property('X-Auth-Token').with.length.at.least(1); - expect(credentials).to.have.property('X-User-Id').with.length.at.least(1); + expect(credentials).to.have.property('X-Auth-Token').with.lengthOf.at.least(1); + expect(credentials).to.have.property('X-User-Id').with.lengthOf.at.least(1); }); it('/login (wrapper username)', (done) => { From 5becbf21f68a3d7aaceec8c279e4ee3132eb53a1 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 12 Nov 2021 19:17:21 -0300 Subject: [PATCH 09/23] Upgrade testing deps --- package-lock.json | 200 +++++++++++++++------------------------------- package.json | 9 ++- 2 files changed, 71 insertions(+), 138 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2921c090d3b5..88fb708404bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9477,9 +9477,9 @@ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" }, "@types/chai": { - "version": "4.2.19", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.19.tgz", - "integrity": "sha512-jRJgpRBuY+7izT7/WNXP/LsMO9YonsstuL+xuvycDyESpoDoIAsMd7suwpB4h9oEWB+ZlPTqJJ8EHomzNhwTPQ==" + "version": "4.2.22", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.22.tgz", + "integrity": "sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ==" }, "@types/chai-datetime": { "version": "0.0.37", @@ -9534,6 +9534,12 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "@types/dompurify": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.2.2.tgz", @@ -9856,9 +9862,9 @@ } }, "@types/mocha": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", - "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==" + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==" }, "@types/mock-require": { "version": "2.0.0", @@ -10176,6 +10182,25 @@ "integrity": "sha512-+mdBIb+pxJ9SLwtjc2DgolMm8U7CG6qBdCevkjSsFB7ehJ0EExFd2ltKQ6m9CoKitqXwe6Tx5h+fAcklGQD0Bw==", "dev": true }, + "@types/superagent": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.13.tgz", + "integrity": "sha512-YIGelp3ZyMiH0/A09PMAORO0EBGlF5xIKfDpK74wdYvWUs2o96b5CItJcWPdH409b7SAXIIG6p8NdU/4U2Maww==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.11.tgz", + "integrity": "sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q==", + "dev": true, + "requires": { + "@types/superagent": "*" + } + }, "@types/tapable": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz", @@ -15021,9 +15046,9 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", "dev": true }, "copy-concurrently": { @@ -18916,9 +18941,9 @@ "dev": true }, "formidable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", - "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", "dev": true }, "forwarded": { @@ -25869,16 +25894,16 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-GRGG/q9bIaUkHJB9NL+KZNjDhMBHB30zW3bZW9qOiYr+QChyLjPzswaxFWkI1q6lGlSL28EQYzAi2vKWNkPx+g==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.1", - "debug": "4.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", @@ -25889,13 +25914,12 @@ "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.23", - "serialize-javascript": "5.0.1", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.4", + "workerpool": "6.1.5", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -25922,25 +25946,10 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -25958,22 +25967,6 @@ } } }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -25984,9 +25977,9 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -26006,15 +25999,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -26025,13 +26009,6 @@ "path-exists": "^4.0.0" } }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -26046,45 +26023,12 @@ "path-is-absolute": "^1.0.0" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -26119,6 +26063,12 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -26152,19 +26102,10 @@ "safe-buffer": "^5.1.0" } }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -26185,15 +26126,6 @@ "has-flag": "^4.0.0" } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -33868,9 +33800,9 @@ } }, "mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true }, "ms": { @@ -36667,9 +36599,9 @@ } }, "workerpool": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.4.tgz", - "integrity": "sha512-jGWPzsUqzkow8HoAvqaPWTUPCrlPJaJ5tY8Iz7n1uCz3tTp6s3CDG0FF1NsX42WNlkRSW6Mr+CDZGnNoSsKa7g==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index e624d7411d08..e3553636f69a 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@types/bad-words": "^3.0.1", "@types/bcrypt": "^5.0.0", "@types/body-parser": "^1.19.0", - "@types/chai": "^4.2.19", + "@types/chai": "^4.2.22", "@types/chai-datetime": "0.0.37", "@types/chai-spies": "^1.0.3", "@types/clipboard": "^2.0.1", @@ -86,7 +86,7 @@ "@types/marked": "^1.2.2", "@types/meteor": "1.4.74", "@types/mkdirp": "^1.0.1", - "@types/mocha": "^8.2.2", + "@types/mocha": "^8.2.3", "@types/mock-require": "^2.0.0", "@types/moment-timezone": "^0.5.30", "@types/mongodb": "^3.6.19", @@ -101,6 +101,7 @@ "@types/semver": "^7.3.6", "@types/sharp": "^0.28.3", "@types/string-strip-html": "^5.0.0", + "@types/supertest": "^2.0.11", "@types/toastr": "^2.1.38", "@types/underscore.string": "0.0.38", "@types/use-subscription": "^1.0.0", @@ -128,8 +129,8 @@ "fast-glob": "^3.2.5", "husky": "^7.0.1", "i18next": "^20.3.2", - "jsdom-global": "3.0.2", - "mocha": "^9.0.0", + "jsdom-global": "^3.0.2", + "mocha": "^9.1.3", "mock-require": "^3.0.3", "pino-pretty": "^7.1.0", "postcss": "^8.3.5", From 87f389b44901adfe5960adf21523ce7422c86829 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Sat, 13 Nov 2021 03:09:06 -0300 Subject: [PATCH 10/23] Adjust URL.createObjectURL mock --- tests/mocks/web/register.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/mocks/web/register.ts b/tests/mocks/web/register.ts index 010c55680c44..cd191c94e40c 100644 --- a/tests/mocks/web/register.ts +++ b/tests/mocks/web/register.ts @@ -1,14 +1,22 @@ import 'jsdom-global/register'; +import uuid from 'uuid'; -const blobs = new Map(); +const urlByBlob = new WeakMap(); +const blobByUrl = new Map(); window.URL.createObjectURL = (blob: Blob): string => { - const uuid = Math.random().toString(36).slice(2); - const url = `blob://${ uuid }`; - blobs.set(url, blob); + const url = urlByBlob.get(blob) ?? `blob://${ uuid.v4() }`; + urlByBlob.set(blob, url); + blobByUrl.set(url, blob); return url; }; window.URL.revokeObjectURL = (url: string): void => { - blobs.delete(url); + const blob = blobByUrl.get(url); + if (!blob) { + return; + } + + urlByBlob.delete(blob); + blobByUrl.delete(url); }; From 5933ce8a576cd405f0f89436c35393506c1b0ded Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Sat, 13 Nov 2021 14:22:53 -0300 Subject: [PATCH 11/23] Force exit at the end to circumvent unresolved mongodb promises --- .mocharc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.mocharc.js b/.mocharc.js index 2c23ab486f7e..d60571a5401e 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -9,4 +9,5 @@ module.exports = { 'server/**/*.tests.ts', 'client/**/*.spec.ts', ], + exit: true, }; From 3302204305994963d6640cef7f17b74ba954bfa3 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Sun, 14 Nov 2021 14:57:28 -0300 Subject: [PATCH 12/23] Split configuration for client tests --- .babelrc | 3 +- .eslintignore | 1 + .github/workflows/build_and_test.yml | 3 + .husky/pre-push | 3 +- .mocharc.base.json | 3 +- .mocharc.client.js | 16 ++ .mocharc.js | 3 +- client/.eslintrc.js | 6 + client/lib/download.spec.ts | 2 - package-lock.json | 277 ++++++++++++++++++++++++ package.json | 5 + tests/mocks/{web => client}/register.ts | 9 +- tests/setup/chaiPlugins.ts | 2 + 13 files changed, 325 insertions(+), 8 deletions(-) create mode 100644 .mocharc.client.js rename tests/mocks/{web => client}/register.ts (72%) diff --git a/.babelrc b/.babelrc index 867a790a279a..b9359fe771b4 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,6 @@ { "presets": [ - "@babel/preset-env" + "@babel/preset-env", + "@babel/preset-react" ] } diff --git a/.eslintignore b/.eslintignore index 24f6298dbc9d..f37309f64ef2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,5 +18,6 @@ imports/client/**/* !/.storybook/ ee/server/services/dist/** !/.mocharc.js +!/.mocharc.*.js !/client/.eslintrc.js !/ee/client/.eslintrc.js diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 9f7b2443454c..8f4e85f39d3e 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -245,6 +245,9 @@ jobs: - name: Unit Test run: npm run testunit + - name: Unit Test (client) + run: npm run testunit-client + - name: E2E Test env: TEST_MODE: "true" diff --git a/.husky/pre-push b/.husky/pre-push index 8f8e7a09a9aa..3c9fedc8460a 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,2 +1,3 @@ meteor npm run lint && \ -meteor npm run testunit +meteor npm run testunit && \ +meteor npm run testunit-client diff --git a/.mocharc.base.json b/.mocharc.base.json index bcc3f72717c9..07c13357327f 100644 --- a/.mocharc.base.json +++ b/.mocharc.base.json @@ -7,5 +7,6 @@ ], "reporter": "spec", "ui": "bdd", - "extension": "js,ts" + "extension": "js,ts,tsx", + "watch-extensions": "js,ts,tsx" } diff --git a/.mocharc.client.js b/.mocharc.client.js new file mode 100644 index 000000000000..a4d22c6af03f --- /dev/null +++ b/.mocharc.client.js @@ -0,0 +1,16 @@ +'use strict'; + +const base = require('./.mocharc.base.json'); + +module.exports = { + ...base, // see https://github.com/mochajs/mocha/issues/3916 + require: [ + ...base.require, + './tests/mocks/client/register.ts', + ], + spec: [ + 'client/**/*.spec.ts', + 'client/**/*.spec.tsx', + ], + exit: true, +}; diff --git a/.mocharc.js b/.mocharc.js index d60571a5401e..6b3ccdf7e60a 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -3,11 +3,10 @@ module.exports = { ...require('./.mocharc.base.json'), // see https://github.com/mochajs/mocha/issues/3916 spec: [ + 'app/**/*.spec.ts', 'app/**/*.tests.js', 'app/**/*.tests.ts', - 'app/**/*.spec.ts', 'server/**/*.tests.ts', - 'client/**/*.spec.ts', ], exit: true, }; diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 4d6bf74449b7..9290d65a9023 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -133,5 +133,11 @@ module.exports = { 'react/no-multi-comp': 'off', }, }, + { + files: ['**/*.tests.js', '**/*.tests.ts', '**/*.spec.ts', '**/*.spec.tsx'], + env: { + mocha: true, + }, + }, ], }; diff --git a/client/lib/download.spec.ts b/client/lib/download.spec.ts index 9b3e96142764..042495a4ed7e 100644 --- a/client/lib/download.spec.ts +++ b/client/lib/download.spec.ts @@ -1,5 +1,3 @@ -import '../../tests/mocks/web/register'; - import { expect, spy } from 'chai'; import { describe, it } from 'mocha'; diff --git a/package-lock.json b/package-lock.json index 88fb708404bb..0b552bb689d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9375,6 +9375,136 @@ "unist-util-find-all-after": "^3.0.2" } }, + "@testing-library/dom": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.1.tgz", + "integrity": "sha512-3KQDyx9r0RKYailW2MiYrSSKEfH0GTkI51UGEvJenvcoDoeRYs0PZpi2SXqtnMClQvCqdtTTpOfFETDTVADpAg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@testing-library/react": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.2.tgz", + "integrity": "sha512-ihQiEOklNyHIpo2Y8FREkyD1QAea054U0MVbwH1m8N9TxeFz+KoJ9LkqoKqJlzx2JDm56DVwaJ1r36JYxZM05g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0" + } + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -9423,6 +9553,12 @@ "@types/node": "*" } }, + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, "@types/bad-words": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/bad-words/-/bad-words-3.0.1.tgz", @@ -9490,6 +9626,15 @@ "@types/chai": "*" } }, + "@types/chai-dom": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@types/chai-dom/-/chai-dom-0.0.11.tgz", + "integrity": "sha512-qLGx6pHrcCfjkpfuh8aBj7xBm8wo2F9aTgv9p+/X0zBAsR3c1wxJguDFnV5cXxUQJKY9BdwE6PVy7W4PT5IAlA==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, "@types/chai-spies": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.3.tgz", @@ -9726,6 +9871,15 @@ "@types/tough-cookie": "*" } }, + "@types/jsdom-global": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/jsdom-global/-/jsdom-global-3.0.2.tgz", + "integrity": "sha512-CFIPEDpO5vQWdQrVXrdSR2j5giiDuyb0hzZD04OJqMfizt7sh6WoqaLBdnP4w74yHDDcQkc0k5TzrXffhua3Jg==", + "dev": true, + "requires": { + "@types/jsdom": "*" + } + }, "@types/json-schema": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", @@ -11519,6 +11673,12 @@ } } }, + "aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -14167,6 +14327,12 @@ "chai": ">1.9.0" } }, + "chai-dom": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/chai-dom/-/chai-dom-1.10.0.tgz", + "integrity": "sha512-/FE0NvEGMXx1x1YQlc8ihLrEhH8JawflchuGe6ypIAX/4Zwmkr4cC3mfR9pDytbxsE/2LSm719TeU7VF/TCmtg==", + "dev": true + }, "chai-spies": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.0.0.tgz", @@ -16346,6 +16512,12 @@ "esutils": "^2.0.2" } }, + "dom-accessibility-api": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz", + "integrity": "sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g==", + "dev": true + }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -24326,6 +24498,12 @@ } } }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "dev": true + }, "mailparser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.2.0.tgz", @@ -29446,6 +29624,105 @@ "renderkid": "^2.0.4" } }, + "pretty-format": { + "version": "27.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.3.1.tgz", + "integrity": "sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA==", + "dev": true, + "requires": { + "@jest/types": "^27.2.5", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.2.5.tgz", + "integrity": "sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", diff --git a/package.json b/package.json index e3553636f69a..e14bffc10917 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "testui": "cypress run --project tests", "testapi": "mocha --config ./.mocharc.api.js", "testunit": "mocha --config ./.mocharc.js", + "testunit-client": "mocha --config ./.mocharc.client.js", "testunit-watch": "mocha --watch --config ./.mocharc.js", "test": "npm run testapi && npm run testui", "translation-diff": "node .scripts/translationDiff.js", @@ -64,6 +65,7 @@ "@storybook/addon-postcss": "^2.0.0", "@storybook/addons": "^6.3.9", "@storybook/react": "^6.3.9", + "@testing-library/react": "^12.1.2", "@types/adm-zip": "^0.4.34", "@types/agenda": "^2.0.9", "@types/bad-words": "^3.0.1", @@ -71,6 +73,7 @@ "@types/body-parser": "^1.19.0", "@types/chai": "^4.2.22", "@types/chai-datetime": "0.0.37", + "@types/chai-dom": "0.0.11", "@types/chai-spies": "^1.0.3", "@types/clipboard": "^2.0.1", "@types/dompurify": "^2.2.2", @@ -79,6 +82,7 @@ "@types/fibers": "^3.1.0", "@types/imap": "^0.8.35", "@types/jsdom": "^16.2.12", + "@types/jsdom-global": "^3.0.2", "@types/ldapjs": "^2.2.1", "@types/less": "^3.0.2", "@types/lodash.get": "^4.4.6", @@ -116,6 +120,7 @@ "babel-plugin-array-includes": "^2.0.3", "chai": "^4.3.4", "chai-datetime": "^1.8.0", + "chai-dom": "^1.10.0", "chai-spies": "^1.0.0", "cross-env": "^7.0.3", "cypress": "^4.12.1", diff --git a/tests/mocks/web/register.ts b/tests/mocks/client/register.ts similarity index 72% rename from tests/mocks/web/register.ts rename to tests/mocks/client/register.ts index cd191c94e40c..8b950df55497 100644 --- a/tests/mocks/web/register.ts +++ b/tests/mocks/client/register.ts @@ -1,6 +1,13 @@ -import 'jsdom-global/register'; +import globalJsdom from 'jsdom-global'; import uuid from 'uuid'; +globalJsdom( + '', + { + url: 'http://localhost:3000', + }, +); + const urlByBlob = new WeakMap(); const blobByUrl = new Map(); diff --git a/tests/setup/chaiPlugins.ts b/tests/setup/chaiPlugins.ts index 86077951000b..a9f0a1dc5b86 100644 --- a/tests/setup/chaiPlugins.ts +++ b/tests/setup/chaiPlugins.ts @@ -1,6 +1,8 @@ import chai from 'chai'; import chaiSpies from 'chai-spies'; import chaiDatetime from 'chai-datetime'; +import chaiDom from 'chai-dom'; chai.use(chaiSpies); chai.use(chaiDatetime); +chai.use(chaiDom); From 64c976562638639c8fab288c33adc044fd746644 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Sun, 14 Nov 2021 14:57:44 -0300 Subject: [PATCH 13/23] Add integration tests for NotFoundPage --- client/views/notFound/NotFoundPage.js | 9 ++- client/views/notFound/NotFoundPage.spec.tsx | 70 +++++++++++++++++++++ tests/mocks/client/RouterContextMock.tsx | 44 +++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 client/views/notFound/NotFoundPage.spec.tsx create mode 100644 tests/mocks/client/RouterContextMock.tsx diff --git a/client/views/notFound/NotFoundPage.js b/client/views/notFound/NotFoundPage.js index 9a564a753053..0aaf3bf64d1a 100644 --- a/client/views/notFound/NotFoundPage.js +++ b/client/views/notFound/NotFoundPage.js @@ -38,11 +38,16 @@ function NotFoundPage() { 404 - + {t('Oops_page_not_found')} - + {t('Sorry_page_you_requested_does_not_exist_or_was_deleted')} diff --git a/client/views/notFound/NotFoundPage.spec.tsx b/client/views/notFound/NotFoundPage.spec.tsx new file mode 100644 index 000000000000..cd8aef9539be --- /dev/null +++ b/client/views/notFound/NotFoundPage.spec.tsx @@ -0,0 +1,70 @@ +import { render, fireEvent, waitFor } from '@testing-library/react'; +import { expect, spy } from 'chai'; +import React from 'react'; + +import RouterContextMock from '../../../tests/mocks/client/RouterContextMock'; +import NotFoundPage from './NotFoundPage'; + +describe('views/notFound/NotFoundPage', () => { + it('should look good', async () => { + const { getByRole } = render(); + + expect(getByRole('heading', { level: 1, name: 'Oops_page_not_found' })).to.be.visible; + expect( + getByRole('status', { + name: 'Sorry_page_you_requested_does_not_exist_or_was_deleted', + }), + ).to.be.visible; + }); + + context('"Return to previous page" button', () => { + it('is visible', () => { + const { getByRole } = render(); + expect(getByRole('button', { name: 'Return_to_previous_page' })).to.be.visible; + }); + + context('when clicked', () => { + const listener = spy(); + + before(() => { + window.history.pushState('404-page', '', 'http://localhost:3000/404'); + window.addEventListener('popstate', listener); + }); + + after(() => { + window.removeEventListener('popstate', listener); + }); + + it('should go back on history', async () => { + const { getByRole } = render(); + const button = getByRole('button', { name: 'Return_to_previous_page' }); + + fireEvent.click(button); + await waitFor(() => expect(listener).to.have.been.called(), { timeout: 2000 }); + expect(window.history.state).to.not.be.eq('404-page'); + }); + }); + }); + + context('"Return to home" button', () => { + it('is visible', () => { + const { getByRole } = render(); + expect(getByRole('button', { name: 'Return_to_home' })).to.be.visible; + }); + + context('when clicked', () => { + it('should go back on history', async () => { + const pushRoute = spy(); + const { getByRole } = render( + + + , + ); + const button = getByRole('button', { name: 'Return_to_home' }); + + fireEvent.click(button); + await waitFor(() => expect(pushRoute).to.have.been.called.with('home')); + }); + }); + }); +}); diff --git a/tests/mocks/client/RouterContextMock.tsx b/tests/mocks/client/RouterContextMock.tsx new file mode 100644 index 000000000000..71b765bde494 --- /dev/null +++ b/tests/mocks/client/RouterContextMock.tsx @@ -0,0 +1,44 @@ +import React, { ContextType, ReactElement, ReactNode, useMemo } from 'react'; +import { Subscription } from 'use-subscription'; + +import { RouterContext } from '../../../client/contexts/RouterContext'; + +type RouterContextMockProps = { + children?: ReactNode; + pushRoute?: (name: string, parameters?: Record, queryStringParameters?: Record) => void; + replaceRoute?: (name: string, parameters?: Record, queryStringParameters?: Record) => void; +}; + +const RouterContextMock = ({ children, pushRoute, replaceRoute }: RouterContextMockProps): ReactElement => { + const value = useMemo>(() => ({ + queryRoutePath: (): Subscription => ({ + getCurrentValue: (): undefined => undefined, + subscribe: () => (): void => undefined, + }), + queryRouteUrl: (): Subscription => ({ + getCurrentValue: (): undefined => undefined, + subscribe: () => (): void => undefined, + }), + pushRoute: pushRoute ?? ((): void => undefined), + replaceRoute: replaceRoute ?? ((): void => undefined), + queryRouteParameter: (): Subscription => ({ + getCurrentValue: (): undefined => undefined, + subscribe: () => (): void => undefined, + }), + queryQueryStringParameter: (): Subscription => ({ + getCurrentValue: (): undefined => undefined, + subscribe: () => (): void => undefined, + }), + queryCurrentRoute: (): Subscription<[undefined, {}, {}, undefined]> => ({ + getCurrentValue: (): [undefined, {}, {}, undefined] => [undefined, {}, {}, undefined], + subscribe: () => (): void => undefined, + }), + }), [pushRoute, replaceRoute]); + + return ; +}; + +export default RouterContextMock; From ef8c615aae347f21ca5efd40d55979895991ae02 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Sun, 14 Nov 2021 22:35:38 -0300 Subject: [PATCH 14/23] Fix cleanup invocation on Mocha --- .mocharc.client.js | 1 + tests/setup/cleanupTestingLibrary.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/setup/cleanupTestingLibrary.ts diff --git a/.mocharc.client.js b/.mocharc.client.js index a4d22c6af03f..62f0123d7eab 100644 --- a/.mocharc.client.js +++ b/.mocharc.client.js @@ -7,6 +7,7 @@ module.exports = { require: [ ...base.require, './tests/mocks/client/register.ts', + './tests/setup/cleanupTestingLibrary.ts', ], spec: [ 'client/**/*.spec.ts', diff --git a/tests/setup/cleanupTestingLibrary.ts b/tests/setup/cleanupTestingLibrary.ts new file mode 100644 index 000000000000..663890796ac8 --- /dev/null +++ b/tests/setup/cleanupTestingLibrary.ts @@ -0,0 +1,17 @@ +import { cleanup } from '@testing-library/react'; + +/** + * Usually the testing library attachs its `cleanup` function by itself when an `afterEach` function is present at the + * global scope. It provides a simple mechanism for, e.g., unmounting React components after tests to avoid leaking + * memory and breaking the idempotence of subsequent tests. Despite working fine at a single run, when Mocha is run in + * _watch mode_ all hooks previously attached are discarded and reloaded from **tests files only**, and its supposed to + * work that way. + * + * See https://testing-library.com/docs/react-testing-library/setup#auto-cleanup-in-mochas-watch-mode + */ + +export const mochaHooks = { + afterEach(): void { + cleanup(); + }, +}; From f55e3fcac3954c5b6a7b3bb85c4ae49e665425d0 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Sun, 14 Nov 2021 23:24:09 -0300 Subject: [PATCH 15/23] Transpile tests without type checking --- .mocharc.base.json | 12 ++++++++---- .mocharc.client.js | 16 ++++++++++++++-- .mocharc.js | 16 ++++++++++++++-- tests/mocks/client/blobUrls.ts | 23 +++++++++++++++++++++++ tests/mocks/client/jsdom.ts | 10 ++++++++++ tests/mocks/client/register.ts | 29 ----------------------------- tests/setup/registerWebApiMocks.ts | 5 +++++ 7 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 tests/mocks/client/blobUrls.ts create mode 100644 tests/mocks/client/jsdom.ts delete mode 100644 tests/mocks/client/register.ts create mode 100644 tests/setup/registerWebApiMocks.ts diff --git a/.mocharc.base.json b/.mocharc.base.json index 07c13357327f..ac8a2bcce8b7 100644 --- a/.mocharc.base.json +++ b/.mocharc.base.json @@ -1,12 +1,16 @@ { + "ui": "bdd", + "reporter": "spec", + "extension": ["js", "ts", "tsx"], "require": [ "@babel/register", "regenerator-runtime/runtime", "ts-node/register", "./tests/setup/chaiPlugins.ts" ], - "reporter": "spec", - "ui": "bdd", - "extension": "js,ts,tsx", - "watch-extensions": "js,ts,tsx" + "watch-files": [ + "./**/*.js", + "./**/*.ts", + "./**/*.tsx" + ] } diff --git a/.mocharc.client.js b/.mocharc.client.js index 62f0123d7eab..c8528bc361f6 100644 --- a/.mocharc.client.js +++ b/.mocharc.client.js @@ -2,16 +2,28 @@ const base = require('./.mocharc.base.json'); +/** + * Mocha will run `ts-node` without doing type checking to speed-up the tests. It should be fine as `npm run typecheck` + * covers test files too. + */ + +Object.assign(process.env, { + TS_NODE_FILES: true, + TS_NODE_TRANSPILE_ONLY: true, +}, process.env); + module.exports = { ...base, // see https://github.com/mochajs/mocha/issues/3916 require: [ ...base.require, - './tests/mocks/client/register.ts', + './tests/setup/registerWebApiMocks.ts', './tests/setup/cleanupTestingLibrary.ts', ], + exit: false, + parallel: true, + slow: 200, spec: [ 'client/**/*.spec.ts', 'client/**/*.spec.tsx', ], - exit: true, }; diff --git a/.mocharc.js b/.mocharc.js index 6b3ccdf7e60a..20c865753d2c 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -1,12 +1,24 @@ 'use strict'; +const base = require('./.mocharc.base.json'); + +/** + * Mocha will run `ts-node` without doing type checking to speed-up the tests. It should be fine as `npm run typecheck` + * covers test files too. + */ + +Object.assign(process.env, { + TS_NODE_FILES: true, + TS_NODE_TRANSPILE_ONLY: true, +}, process.env); + module.exports = { - ...require('./.mocharc.base.json'), // see https://github.com/mochajs/mocha/issues/3916 + ...base, // see https://github.com/mochajs/mocha/issues/3916 + exit: true, spec: [ 'app/**/*.spec.ts', 'app/**/*.tests.js', 'app/**/*.tests.ts', 'server/**/*.tests.ts', ], - exit: true, }; diff --git a/tests/mocks/client/blobUrls.ts b/tests/mocks/client/blobUrls.ts new file mode 100644 index 000000000000..5e01a99dc927 --- /dev/null +++ b/tests/mocks/client/blobUrls.ts @@ -0,0 +1,23 @@ +import uuid from 'uuid'; + +export const enableBlobUrlsMock = (): void => { + const urlByBlob = new WeakMap(); + const blobByUrl = new Map(); + + window.URL.createObjectURL = (blob: Blob): string => { + const url = urlByBlob.get(blob) ?? `blob://${ uuid.v4() }`; + urlByBlob.set(blob, url); + blobByUrl.set(url, blob); + return url; + }; + + window.URL.revokeObjectURL = (url: string): void => { + const blob = blobByUrl.get(url); + if (!blob) { + return; + } + + urlByBlob.delete(blob); + blobByUrl.delete(url); + }; +}; diff --git a/tests/mocks/client/jsdom.ts b/tests/mocks/client/jsdom.ts new file mode 100644 index 000000000000..857dc69896ae --- /dev/null +++ b/tests/mocks/client/jsdom.ts @@ -0,0 +1,10 @@ +import globalJsdom from 'jsdom-global'; + +export const enableJsdom = (): void => { + globalJsdom( + '', + { + url: 'http://localhost:3000', + }, + ); +}; diff --git a/tests/mocks/client/register.ts b/tests/mocks/client/register.ts deleted file mode 100644 index 8b950df55497..000000000000 --- a/tests/mocks/client/register.ts +++ /dev/null @@ -1,29 +0,0 @@ -import globalJsdom from 'jsdom-global'; -import uuid from 'uuid'; - -globalJsdom( - '', - { - url: 'http://localhost:3000', - }, -); - -const urlByBlob = new WeakMap(); -const blobByUrl = new Map(); - -window.URL.createObjectURL = (blob: Blob): string => { - const url = urlByBlob.get(blob) ?? `blob://${ uuid.v4() }`; - urlByBlob.set(blob, url); - blobByUrl.set(url, blob); - return url; -}; - -window.URL.revokeObjectURL = (url: string): void => { - const blob = blobByUrl.get(url); - if (!blob) { - return; - } - - urlByBlob.delete(blob); - blobByUrl.delete(url); -}; diff --git a/tests/setup/registerWebApiMocks.ts b/tests/setup/registerWebApiMocks.ts new file mode 100644 index 000000000000..83bf826fa0d0 --- /dev/null +++ b/tests/setup/registerWebApiMocks.ts @@ -0,0 +1,5 @@ +import { enableBlobUrlsMock } from '../mocks/client/blobUrls'; +import { enableJsdom } from '../mocks/client/jsdom'; + +enableJsdom(); +enableBlobUrlsMock(); From cdf65f33f681c7e97c45551fa982f362f80d00a9 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Mon, 15 Nov 2021 01:10:03 -0300 Subject: [PATCH 16/23] Add some explanation on Mocha configuration files --- .mocharc.api.js | 4 ++++ .mocharc.client.js | 4 ++++ .mocharc.js | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/.mocharc.api.js b/.mocharc.api.js index 6369f654297a..b8a96c749c9f 100644 --- a/.mocharc.api.js +++ b/.mocharc.api.js @@ -1,5 +1,9 @@ 'use strict'; +/** + * Mocha configuration for REST API integration tests. + */ + module.exports = { ...require('./.mocharc.base.json'), // see https://github.com/mochajs/mocha/issues/3916 timeout: 10000, diff --git a/.mocharc.client.js b/.mocharc.client.js index c8528bc361f6..4ff9b354b2b8 100644 --- a/.mocharc.client.js +++ b/.mocharc.client.js @@ -1,5 +1,9 @@ 'use strict'; +/** + * Mocha configuration for client-side unit and integration tests. + */ + const base = require('./.mocharc.base.json'); /** diff --git a/.mocharc.js b/.mocharc.js index 20c865753d2c..a71a3020cf4b 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -1,5 +1,9 @@ 'use strict'; +/** + * Mocha configuration for general unit tests. + */ + const base = require('./.mocharc.base.json'); /** From 921e39d8fdf0bfeed7484f7b6b0ef5bfa8f8130c Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Mon, 15 Nov 2021 01:52:22 -0300 Subject: [PATCH 17/23] Add eslint-plugin-testing-library and implement suggestions --- .mocharc.client.js | 1 - client/.eslintrc.js | 9 +- client/views/notFound/NotFoundPage.spec.tsx | 51 +++-- package-lock.json | 238 ++++++++++++++++++++ package.json | 2 + 5 files changed, 278 insertions(+), 23 deletions(-) diff --git a/.mocharc.client.js b/.mocharc.client.js index 4ff9b354b2b8..e4279a9a6356 100644 --- a/.mocharc.client.js +++ b/.mocharc.client.js @@ -24,7 +24,6 @@ module.exports = { './tests/setup/cleanupTestingLibrary.ts', ], exit: false, - parallel: true, slow: 200, spec: [ 'client/**/*.spec.ts', diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 9290d65a9023..baae88c35553 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -2,7 +2,7 @@ module.exports = { root: true, extends: ['@rocket.chat/eslint-config', 'prettier'], parser: 'babel-eslint', - plugins: ['react', 'react-hooks', 'prettier'], + plugins: ['react', 'react-hooks', 'prettier', 'testing-library'], rules: { 'import/named': 'error', 'import/order': [ @@ -135,6 +135,13 @@ module.exports = { }, { files: ['**/*.tests.js', '**/*.tests.ts', '**/*.spec.ts', '**/*.spec.tsx'], + extends: ['plugin:testing-library/react'], + rules: { + 'testing-library/no-await-sync-events': 'warn', + 'testing-library/no-manual-cleanup': 'warn', + 'testing-library/prefer-explicit-assert': 'warn', + 'testing-library/prefer-user-event': 'warn', + }, env: { mocha: true, }, diff --git a/client/views/notFound/NotFoundPage.spec.tsx b/client/views/notFound/NotFoundPage.spec.tsx index cd8aef9539be..3f1389631580 100644 --- a/client/views/notFound/NotFoundPage.spec.tsx +++ b/client/views/notFound/NotFoundPage.spec.tsx @@ -1,4 +1,5 @@ -import { render, fireEvent, waitFor } from '@testing-library/react'; +import { render, waitFor, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { expect, spy } from 'chai'; import React from 'react'; @@ -7,22 +8,35 @@ import NotFoundPage from './NotFoundPage'; describe('views/notFound/NotFoundPage', () => { it('should look good', async () => { - const { getByRole } = render(); + render(); - expect(getByRole('heading', { level: 1, name: 'Oops_page_not_found' })).to.be.visible; + expect(screen.getByRole('heading', { level: 1, name: 'Oops_page_not_found' })).to.exist; expect( - getByRole('status', { + screen.getByRole('status', { name: 'Sorry_page_you_requested_does_not_exist_or_was_deleted', }), - ).to.be.visible; + ).to.exist; + expect( + screen.getByRole('button', { name: 'Return_to_previous_page' }), + ).to.exist.and.to.not.match(':disabled'); + expect(screen.getByRole('button', { name: 'Return_to_home' })).to.exist.and.to.not.match( + ':disabled', + ); }); - context('"Return to previous page" button', () => { - it('is visible', () => { - const { getByRole } = render(); - expect(getByRole('button', { name: 'Return_to_previous_page' })).to.be.visible; - }); + it('should have correct tab order', () => { + render(); + + expect(document.body).to.have.focus; + userEvent.tab(); + expect(screen.getByRole('button', { name: 'Return_to_previous_page' })).to.have.focus; + userEvent.tab(); + expect(screen.getByRole('button', { name: 'Return_to_home' })).to.have.focus; + userEvent.tab(); + expect(document.body).to.have.focus; + }); + context('"Return to previous page" button', () => { context('when clicked', () => { const listener = spy(); @@ -36,10 +50,10 @@ describe('views/notFound/NotFoundPage', () => { }); it('should go back on history', async () => { - const { getByRole } = render(); - const button = getByRole('button', { name: 'Return_to_previous_page' }); + render(); + const button = screen.getByRole('button', { name: 'Return_to_previous_page' }); - fireEvent.click(button); + userEvent.click(button); await waitFor(() => expect(listener).to.have.been.called(), { timeout: 2000 }); expect(window.history.state).to.not.be.eq('404-page'); }); @@ -47,22 +61,17 @@ describe('views/notFound/NotFoundPage', () => { }); context('"Return to home" button', () => { - it('is visible', () => { - const { getByRole } = render(); - expect(getByRole('button', { name: 'Return_to_home' })).to.be.visible; - }); - context('when clicked', () => { it('should go back on history', async () => { const pushRoute = spy(); - const { getByRole } = render( + render( , ); - const button = getByRole('button', { name: 'Return_to_home' }); + const button = screen.getByRole('button', { name: 'Return_to_home' }); - fireEvent.click(button); + userEvent.click(button); await waitFor(() => expect(pushRoute).to.have.been.called.with('home')); }); }); diff --git a/package-lock.json b/package-lock.json index 0b552bb689d6..99c21395cfe9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9505,6 +9505,15 @@ "@testing-library/dom": "^8.0.0" } }, + "@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5" + } + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -10716,6 +10725,40 @@ } } }, + "@typescript-eslint/scope-manager": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.1.tgz", + "integrity": "sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", + "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", + "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.1", + "eslint-visitor-keys": "^3.0.0" + } + }, + "eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true + } + } + }, "@typescript-eslint/types": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", @@ -17895,6 +17938,195 @@ "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==", "dev": true }, + "eslint-plugin-testing-library": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.0.0.tgz", + "integrity": "sha512-lojlPN8nsb7JTFYhJuLNwwI8kALRC0TBz5JRO1lvV7Ifzqu7IoddjDFRCxeM+0d2/zuEO7Sb5oc7ErDqhd4MBw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@typescript-eslint/experimental-utils": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.1.tgz", + "integrity": "sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.3.1", + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/typescript-estree": "5.3.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/types": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.1.tgz", + "integrity": "sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.1.tgz", + "integrity": "sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.1", + "@typescript-eslint/visitor-keys": "5.3.1", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.1.tgz", + "integrity": "sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.3.1", + "eslint-visitor-keys": "^3.0.0" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "eslint-scope": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", @@ -32297,6 +32529,12 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", diff --git a/package.json b/package.json index e14bffc10917..2bccf092d9e5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@storybook/addons": "^6.3.9", "@storybook/react": "^6.3.9", "@testing-library/react": "^12.1.2", + "@testing-library/user-event": "^13.5.0", "@types/adm-zip": "^0.4.34", "@types/agenda": "^2.0.9", "@types/bad-words": "^3.0.1", @@ -131,6 +132,7 @@ "eslint-plugin-prettier": "^3.4.0", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", + "eslint-plugin-testing-library": "^5.0.0", "fast-glob": "^3.2.5", "husky": "^7.0.1", "i18next": "^20.3.2", From 319c9c51656320ae1d12722f2fea0a681b1140cc Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 15 Nov 2021 04:08:07 -0600 Subject: [PATCH 18/23] Replace all occurrences of a placeholder on string instead of just first one (#23703) --- .../server/hooks/onMessageSentParsePlaceholder.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts b/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts index 7afe45463a93..c8a0006c7c07 100644 --- a/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts +++ b/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts @@ -29,6 +29,8 @@ const placeholderFields = { }, }; +const replaceAll = (text: string, old: string, replace: string): string => text.replace(new RegExp(old, 'g'), replace); + const handleBeforeSaveMessage = (message: IMessage, room: IOmnichannelRoom): any => { if (!message.msg || message.msg === '') { return message; @@ -50,7 +52,7 @@ const handleBeforeSaveMessage = (message: IMessage, room: IOmnichannelRoom): any const placeholderConfig = placeholderFields[field as keyof typeof placeholderFields]; const from = placeholderConfig.from === 'agent' ? agent : visitor; const data = get(from, placeholderConfig.dataKey, ''); - messageText = messageText.replace(templateKey, data); + messageText = replaceAll(messageText, templateKey, data); return messageText; }); From 99bc32dbbeec364a56c57afe52f2d22f59b29b54 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 15 Nov 2021 14:53:42 -0300 Subject: [PATCH 19/23] Chore: Api definitions (#23701) Co-authored-by: Tasso Evangelista --- app/api/server/api.d.ts | 116 ++++- app/api/server/api.js | 2 +- app/api/server/helpers/getPaginationItems.js | 2 +- app/api/server/v1/banners.ts | 23 +- app/api/server/v1/chat.js | 6 +- app/api/server/v1/dns.ts | 8 +- app/api/server/v1/instances.ts | 4 +- app/api/server/v1/ldap.ts | 12 +- app/api/server/v1/permissions.ts | 60 +-- app/api/server/v1/roles.ts | 171 +++---- app/api/server/v1/settings.ts | 101 ++-- app/api/server/v1/teams.ts | 233 ++++++---- .../server/functions/getUsersInRole.ts | 4 +- app/models/server/raw/BaseRaw.ts | 2 +- app/models/server/raw/Permissions.ts | 4 + app/models/server/raw/Roles.ts | 8 +- app/models/server/raw/Settings.ts | 2 +- .../contexts/ServerContext/ServerContext.ts | 2 +- client/contexts/ServerContext/endpoints.ts | 87 ---- .../contexts/ServerContext/endpoints/v1/dm.ts | 21 - .../ServerContext/endpoints/v1/teams.ts | 76 --- client/contexts/ServerContext/index.ts | 1 - client/hooks/useEndpointAction.ts | 2 +- client/hooks/useEndpointActionExperimental.ts | 2 +- client/hooks/useEndpointData.ts | 2 +- client/providers/ServerProvider.tsx | 5 +- .../channels/hooks/useTeamsChannelList.ts | 3 +- definition/IMessage/IMessage.ts | 10 +- definition/IRoom.ts | 1 + definition/IUser.ts | 2 +- .../apps.ts => definition/rest/apps/index.ts | 0 definition/rest/helpers/PaginatedRequest.ts | 4 + definition/rest/helpers/PaginatedResult.ts | 5 + definition/rest/index.ts | 97 ++++ definition/rest/v1/banners.ts | 26 ++ .../rest}/v1/channels.ts | 6 +- .../endpoints => definition/rest}/v1/chat.ts | 4 +- .../endpoints => definition/rest}/v1/cloud.ts | 0 .../rest}/v1/customUserStatus.ts | 0 definition/rest/v1/dm.ts | 21 + .../endpoints => definition/rest}/v1/dns.ts | 5 +- .../rest}/v1/emojiCustom.ts | 2 +- .../rest}/v1/groups.ts | 6 +- .../endpoints => definition/rest}/v1/im.ts | 14 +- definition/rest/v1/instances.ts | 16 + .../endpoints => definition/rest}/v1/ldap.ts | 6 +- .../rest}/v1/licenses.ts | 5 +- .../endpoints => definition/rest}/v1/misc.ts | 0 .../rest}/v1/omnichannel.ts | 18 +- definition/rest/v1/permissions.ts | 43 ++ definition/rest/v1/roles.ts | 186 ++++++++ .../endpoints => definition/rest}/v1/rooms.ts | 6 +- definition/rest/v1/settings.ts | 100 ++++ .../rest}/v1/statistics.ts | 2 +- .../v1/teams/TeamsAddMembersProps.test.ts | 71 +++ .../rest/v1/teams/TeamsAddMembersProps.ts | 80 ++++ .../teams/TeamsConvertToChannelProps.test.ts | 39 ++ .../v1/teams/TeamsConvertToChannelProps.ts | 54 +++ .../rest/v1/teams/TeamsDeleteProps.test.ts | 64 +++ definition/rest/v1/teams/TeamsDeleteProps.ts | 50 ++ .../rest/v1/teams/TeamsLeaveProps.test.ts | 64 +++ definition/rest/v1/teams/TeamsLeaveProps.ts | 51 ++ .../v1/teams/TeamsRemoveMemberProps.test.ts | 61 +++ .../rest/v1/teams/TeamsRemoveMemberProps.ts | 56 +++ .../v1/teams/TeamsRemoveRoomProps.test.ts | 27 ++ .../rest/v1/teams/TeamsRemoveRoomProps.ts | 40 ++ .../v1/teams/TeamsUpdateMemberProps.test.ts | 59 +++ .../rest/v1/teams/TeamsUpdateMemberProps.ts | 68 +++ .../rest/v1/teams/TeamsUpdateProps.test.ts | 161 +++++++ definition/rest/v1/teams/TeamsUpdateProps.ts | 75 +++ definition/rest/v1/teams/index.ts | 148 ++++++ .../endpoints => definition/rest}/v1/users.ts | 4 +- definition/utils.ts | 2 + ee/app/license/definitions/ILicense.ts | 11 + ee/app/license/definitions/ILicenseTag.ts | 4 + ee/app/license/server/license.ts | 17 +- .../server/api/business-hours.ts | 7 +- .../server/business-hour/Helper.ts | 2 +- .../endpoints/v1/engagementDashboard.ts | 10 - ee/definition/rest/index.ts | 4 + ee/definition/rest/v1/engagementDashboard.ts | 62 +++ .../rest/v1/omnichannel/businessHours.ts | 7 + ee/server/api/ldap.ts | 6 +- ee/server/api/licenses.ts | 3 +- ee/server/{index.js => index.ts} | 0 ee/server/lib/ldap/Manager.ts | 2 +- ee/server/services/package-lock.json | 39 ++ ee/server/services/package.json | 1 + package-lock.json | 55 ++- package.json | 1 + server/sdk/types/ITeamService.ts | 13 +- tests/end-to-end/api/13-roles.js | 440 ------------------ tsconfig.json | 5 +- 93 files changed, 2364 insertions(+), 1043 deletions(-) delete mode 100644 client/contexts/ServerContext/endpoints.ts delete mode 100644 client/contexts/ServerContext/endpoints/v1/dm.ts delete mode 100644 client/contexts/ServerContext/endpoints/v1/teams.ts rename client/contexts/ServerContext/endpoints/apps.ts => definition/rest/apps/index.ts (100%) create mode 100644 definition/rest/helpers/PaginatedRequest.ts create mode 100644 definition/rest/helpers/PaginatedResult.ts create mode 100644 definition/rest/index.ts create mode 100644 definition/rest/v1/banners.ts rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/channels.ts (70%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/chat.ts (84%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/cloud.ts (100%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/customUserStatus.ts (100%) create mode 100644 definition/rest/v1/dm.ts rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/dns.ts (62%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/emojiCustom.ts (73%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/groups.ts (69%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/im.ts (65%) create mode 100644 definition/rest/v1/instances.ts rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/ldap.ts (64%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/licenses.ts (64%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/misc.ts (100%) rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/omnichannel.ts (82%) create mode 100644 definition/rest/v1/permissions.ts create mode 100644 definition/rest/v1/roles.ts rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/rooms.ts (81%) create mode 100644 definition/rest/v1/settings.ts rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/statistics.ts (63%) create mode 100644 definition/rest/v1/teams/TeamsAddMembersProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsAddMembersProps.ts create mode 100644 definition/rest/v1/teams/TeamsConvertToChannelProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsConvertToChannelProps.ts create mode 100644 definition/rest/v1/teams/TeamsDeleteProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsDeleteProps.ts create mode 100644 definition/rest/v1/teams/TeamsLeaveProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsLeaveProps.ts create mode 100644 definition/rest/v1/teams/TeamsRemoveMemberProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsRemoveMemberProps.ts create mode 100644 definition/rest/v1/teams/TeamsRemoveRoomProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsRemoveRoomProps.ts create mode 100644 definition/rest/v1/teams/TeamsUpdateMemberProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsUpdateMemberProps.ts create mode 100644 definition/rest/v1/teams/TeamsUpdateProps.test.ts create mode 100644 definition/rest/v1/teams/TeamsUpdateProps.ts create mode 100644 definition/rest/v1/teams/index.ts rename {client/contexts/ServerContext/endpoints => definition/rest}/v1/users.ts (71%) create mode 100644 ee/app/license/definitions/ILicense.ts create mode 100644 ee/app/license/definitions/ILicenseTag.ts delete mode 100644 ee/client/contexts/ServerContext/endpoints/v1/engagementDashboard.ts create mode 100644 ee/definition/rest/index.ts create mode 100644 ee/definition/rest/v1/engagementDashboard.ts create mode 100644 ee/definition/rest/v1/omnichannel/businessHours.ts rename ee/server/{index.js => index.ts} (100%) delete mode 100644 tests/end-to-end/api/13-roles.js diff --git a/app/api/server/api.d.ts b/app/api/server/api.d.ts index 9e968448bbc0..94b44c774fe6 100644 --- a/app/api/server/api.d.ts +++ b/app/api/server/api.d.ts @@ -1,4 +1,118 @@ -import { APIClass } from '.'; +import { Endpoints } from '../../../definition/rest'; +import { Awaited } from '../../../definition/utils'; +import { IUser } from '../../../definition/IUser'; + + +export type ChangeTypeOfKeys< + T extends object, + Keys extends keyof T, + NewType +> = { + [key in keyof T]: key extends Keys ? NewType : T[key] +} + +type This = { + getPaginationItems(): ({ + offset: number; + count: number; + }); + parseJsonQuery(): ({ + sort: Record; + fields: Record; + query: Record; + }); + readonly urlParams: Record; + getUserFromParams(): IUser; +} + +type ThisLoggedIn = { + readonly user: IUser; + readonly userId: string; +} + +type ThisLoggedOut = { + readonly user: null; + readonly userId: null; +} + +type EndpointWithExtraOptions any, A> = WrappedFunction | ({ action: WrappedFunction } & (A extends true ? { twoFactorRequired: boolean } : {})); + +export type Methods = { + [K in keyof T as `${Lowercase}`]: T[K] extends (...args: any) => any ? EndpointWithExtraOptions<(this: This & (A extends true ? ThisLoggedIn : ThisLoggedOut) & Params[0]>) => ReturnType, A> : never; +}; + +type Params = K extends 'GET' ? { readonly queryParams: Partial

} : K extends 'POST' ? { readonly bodyParams: Partial

} : never; + +type SuccessResult = { + statusCode: 200; + success: true; +} & T extends (undefined) ? {} : { body: T } + +type UnauthorizedResult = { + statusCode: 403; + body: { + success: false; + error: string; + }; +} + +type FailureResult = { + statusCode: 400; +} & FailureBody; + +type FailureBody = Exclude extends object ? { body: T & E } : { + body: E & { error: string } & E extends Error ? { details: string } : {} & ST extends undefined ? {} : { stack: string } & ET extends undefined ? {} : { errorType: string }; +} + +type Errors = FailureResult | FailureResult | FailureResult | UnauthorizedResult; + +type WrappedFunction any> = (this: ThisParameterType, ...args: Parameters) => ReturnTypes; + +type ReturnTypes any> = PromisedOrNot> | PromisedOrNot>; + +type PromisedOrNot = Promise | T; + +type Options = { + permissionsRequired?: string[]; + twoFactorOptions?: unknown; + twoFactorRequired?: boolean; + authRequired?: boolean; +} + +export type RestEndpoints

= Methods; + +type ToLowerCaseKeys = { + [K in keyof T as `${Lowercase}`]: T[K]; +}; +type ToResultType = { + [K in keyof T]: T[K] extends (...args: any) => any ? Awaited> : never; +} +export type ResultTypeEndpoints

= ToResultType>; + +declare class APIClass { + addRoute

(route: P, endpoints: RestEndpoints

): void; + + addRoute

(route: P, options: O, endpoints: RestEndpoints): void; + + unauthorized(msg?: string): UnauthorizedResult; + + failure(result: string, errorType?: ET, stack?: ST, error?: E): FailureResult; + + failure(result: object): FailureResult; + + failure(): FailureResult; + + success(): SuccessResult; + + success(result: T): SuccessResult; + + defaultFieldsToExclude: { + joinCode: 0; + members: 0; + importIds: 0; + e2e: 0; + } +} export declare const API: { v1: APIClass; diff --git a/app/api/server/api.js b/app/api/server/api.js index 3b1d6bdfbb9e..01a71effe8a3 100644 --- a/app/api/server/api.js +++ b/app/api/server/api.js @@ -403,7 +403,7 @@ export class APIClass extends Restivus { api.processTwoFactor({ userId: this.userId, request: this.request, invocation, options: _options.twoFactorOptions, connection }); } - result = DDP._CurrentInvocation.withValue(invocation, () => originalAction.apply(this)) || API.v1.success(); + result = DDP._CurrentInvocation.withValue(invocation, () => Promise.await(originalAction.apply(this))) || API.v1.success(); log.http({ status: result.statusCode, diff --git a/app/api/server/helpers/getPaginationItems.js b/app/api/server/helpers/getPaginationItems.js index 0cff491a9763..259f79a1191a 100644 --- a/app/api/server/helpers/getPaginationItems.js +++ b/app/api/server/helpers/getPaginationItems.js @@ -10,7 +10,7 @@ API.helperMethods.set('getPaginationItems', function _getPaginationItems() { const offset = this.queryParams.offset ? parseInt(this.queryParams.offset) : 0; let count = defaultCount; - // Ensure count is an appropiate amount + // Ensure count is an appropriate amount if (typeof this.queryParams.count !== 'undefined') { count = parseInt(this.queryParams.count); } else { diff --git a/app/api/server/v1/banners.ts b/app/api/server/v1/banners.ts index 5dd5089814b4..678d6e726cf2 100644 --- a/app/api/server/v1/banners.ts +++ b/app/api/server/v1/banners.ts @@ -52,7 +52,7 @@ import { BannerPlatform } from '../../../../definition/IBanner'; * $ref: '#/components/schemas/ApiFailureV1' */ API.v1.addRoute('banners.getNew', { authRequired: true }, { // deprecated - get() { + async get() { check(this.queryParams, Match.ObjectIncluding({ platform: String, bid: Match.Maybe(String), @@ -67,7 +67,7 @@ API.v1.addRoute('banners.getNew', { authRequired: true }, { // deprecated throw new Meteor.Error('error-unknown-platform', 'Platform is unknown.'); } - const banners = Promise.await(Banner.getBannersForUser(this.userId, platform, bannerId)); + const banners = await Banner.getBannersForUser(this.userId, platform, bannerId); return API.v1.success({ banners }); }, @@ -119,13 +119,18 @@ API.v1.addRoute('banners.getNew', { authRequired: true }, { // deprecated * schema: * $ref: '#/components/schemas/ApiFailureV1' */ -API.v1.addRoute('banners/:id', { authRequired: true }, { - get() { +API.v1.addRoute('banners/:id', { authRequired: true }, { // TODO: move to users/:id/banners + async get() { check(this.urlParams, Match.ObjectIncluding({ id: String, })); + check(this.queryParams, Match.ObjectIncluding({ + platform: String, + })); + const { platform } = this.queryParams; + if (!platform) { throw new Meteor.Error('error-missing-param', 'The required "platform" param is missing.'); } @@ -135,7 +140,7 @@ API.v1.addRoute('banners/:id', { authRequired: true }, { throw new Meteor.Error('error-missing-param', 'The required "id" param is missing.'); } - const banners = Promise.await(Banner.getBannersForUser(this.userId, platform, id)); + const banners = await Banner.getBannersForUser(this.userId, platform, id); return API.v1.success({ banners }); }, @@ -179,7 +184,7 @@ API.v1.addRoute('banners/:id', { authRequired: true }, { * $ref: '#/components/schemas/ApiFailureV1' */ API.v1.addRoute('banners', { authRequired: true }, { - get() { + async get() { check(this.queryParams, Match.ObjectIncluding({ platform: String, })); @@ -193,7 +198,7 @@ API.v1.addRoute('banners', { authRequired: true }, { throw new Meteor.Error('error-unknown-platform', 'Platform is unknown.'); } - const banners = Promise.await(Banner.getBannersForUser(this.userId, platform)); + const banners = await Banner.getBannersForUser(this.userId, platform); return API.v1.success({ banners }); }, @@ -233,7 +238,7 @@ API.v1.addRoute('banners', { authRequired: true }, { * $ref: '#/components/schemas/ApiFailureV1' */ API.v1.addRoute('banners.dismiss', { authRequired: true }, { - post() { + async post() { check(this.bodyParams, Match.ObjectIncluding({ bannerId: String, })); @@ -244,7 +249,7 @@ API.v1.addRoute('banners.dismiss', { authRequired: true }, { throw new Meteor.Error('error-missing-param', 'The required "bannerId" param is missing.'); } - Promise.await(Banner.dismiss(this.userId, bannerId)); + await Banner.dismiss(this.userId, bannerId); return API.v1.success(); }, }); diff --git a/app/api/server/v1/chat.js b/app/api/server/v1/chat.js index eba0e4e3f668..fa3e917665fc 100644 --- a/app/api/server/v1/chat.js +++ b/app/api/server/v1/chat.js @@ -697,7 +697,7 @@ API.v1.addRoute('chat.getSnippetedMessages', { authRequired: true }, { }); API.v1.addRoute('chat.getDiscussions', { authRequired: true }, { - get() { + async get() { const { roomId, text } = this.queryParams; const { sort } = this.parseJsonQuery(); const { offset, count } = this.getPaginationItems(); @@ -705,7 +705,7 @@ API.v1.addRoute('chat.getDiscussions', { authRequired: true }, { if (!roomId) { throw new Meteor.Error('error-invalid-params', 'The required "roomId" query param is missing.'); } - const messages = Promise.await(findDiscussionsFromRoom({ + const messages = await findDiscussionsFromRoom({ uid: this.userId, roomId, text, @@ -714,7 +714,7 @@ API.v1.addRoute('chat.getDiscussions', { authRequired: true }, { count, sort, }, - })); + }); return API.v1.success(messages); }, }); diff --git a/app/api/server/v1/dns.ts b/app/api/server/v1/dns.ts index 902ef90d4782..a0b0fa5788e6 100644 --- a/app/api/server/v1/dns.ts +++ b/app/api/server/v1/dns.ts @@ -48,7 +48,7 @@ import { resolveSRV, resolveTXT } from '../../../federation/server/functions/res * $ref: '#/components/schemas/ApiFailureV1' */ API.v1.addRoute('dns.resolve.srv', { authRequired: true }, { - get() { + async get() { check(this.queryParams, Match.ObjectIncluding({ url: String, })); @@ -58,7 +58,7 @@ API.v1.addRoute('dns.resolve.srv', { authRequired: true }, { throw new Meteor.Error('error-missing-param', 'The required "url" param is missing.'); } - const resolved = Promise.await(resolveSRV(url)); + const resolved = await resolveSRV(url); return API.v1.success({ resolved }); }, @@ -99,7 +99,7 @@ API.v1.addRoute('dns.resolve.srv', { authRequired: true }, { * $ref: '#/components/schemas/ApiFailureV1' */ API.v1.addRoute('dns.resolve.txt', { authRequired: true }, { - post() { + async post() { check(this.queryParams, Match.ObjectIncluding({ url: String, })); @@ -109,7 +109,7 @@ API.v1.addRoute('dns.resolve.txt', { authRequired: true }, { throw new Meteor.Error('error-missing-param', 'The required "url" param is missing.'); } - const resolved = Promise.await(resolveTXT(url)); + const resolved = await resolveTXT(url); return API.v1.success({ resolved }); }, diff --git a/app/api/server/v1/instances.ts b/app/api/server/v1/instances.ts index d3db3489537a..54bd2a563d14 100644 --- a/app/api/server/v1/instances.ts +++ b/app/api/server/v1/instances.ts @@ -5,12 +5,12 @@ import { InstanceStatus } from '../../../models/server/raw'; import { IInstanceStatus } from '../../../../definition/IInstanceStatus'; API.v1.addRoute('instances.get', { authRequired: true }, { - get() { + async get() { if (!hasPermission(this.userId, 'view-statistics')) { return API.v1.unauthorized(); } - const instances = Promise.await(InstanceStatus.find().toArray()); + const instances = await InstanceStatus.find().toArray(); return API.v1.success({ instances: instances.map((instance: IInstanceStatus) => { diff --git a/app/api/server/v1/ldap.ts b/app/api/server/v1/ldap.ts index ee98484d1791..c424342d9712 100644 --- a/app/api/server/v1/ldap.ts +++ b/app/api/server/v1/ldap.ts @@ -7,7 +7,7 @@ import { SystemLogger } from '../../../../server/lib/logger/system'; import { LDAP } from '../../../../server/sdk'; API.v1.addRoute('ldap.testConnection', { authRequired: true }, { - post() { + async post() { if (!this.userId) { throw new Error('error-invalid-user'); } @@ -21,20 +21,20 @@ API.v1.addRoute('ldap.testConnection', { authRequired: true }, { } try { - Promise.await(LDAP.testConnection()); + await LDAP.testConnection(); } catch (error) { SystemLogger.error(error); throw new Error('Connection_failed'); } return API.v1.success({ - message: 'Connection_success', + message: 'Connection_success' as const, }); }, }); API.v1.addRoute('ldap.testSearch', { authRequired: true }, { - post() { + async post() { check(this.bodyParams, Match.ObjectIncluding({ username: String, })); @@ -51,10 +51,10 @@ API.v1.addRoute('ldap.testSearch', { authRequired: true }, { throw new Error('LDAP_disabled'); } - Promise.await(LDAP.testSearch(this.bodyParams.username)); + await LDAP.testSearch(this.bodyParams.username); return API.v1.success({ - message: 'LDAP_User_Found', + message: 'LDAP_User_Found' as const, }); }, }); diff --git a/app/api/server/v1/permissions.ts b/app/api/server/v1/permissions.ts index c2aab9afda54..988f4907e351 100644 --- a/app/api/server/v1/permissions.ts +++ b/app/api/server/v1/permissions.ts @@ -1,12 +1,13 @@ import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { hasPermission } from '../../../authorization/server'; import { API } from '../api'; import { Permissions, Roles } from '../../../models/server/raw'; +import { IPermission } from '../../../../definition/IPermission'; +import { isBodyParamsValidPermissionUpdate } from '../../../../definition/rest/v1/permissions'; API.v1.addRoute('permissions.listAll', { authRequired: true }, { - get() { + async get() { const { updatedSince } = this.queryParams; let updatedSinceDate: Date | undefined; @@ -17,7 +18,10 @@ API.v1.addRoute('permissions.listAll', { authRequired: true }, { updatedSinceDate = new Date(updatedSince); } - const result = Promise.await(Meteor.call('permissions/get', updatedSinceDate)); + const result = await Meteor.call('permissions/get', updatedSinceDate) as { + update: IPermission[]; + remove: IPermission[]; + }; if (Array.isArray(result)) { return API.v1.success({ @@ -31,51 +35,37 @@ API.v1.addRoute('permissions.listAll', { authRequired: true }, { }); API.v1.addRoute('permissions.update', { authRequired: true }, { - post() { + async post() { if (!hasPermission(this.userId, 'access-permissions')) { return API.v1.failure('Editing permissions is not allowed', 'error-edit-permissions-not-allowed'); } - check(this.bodyParams, { - permissions: [ - Match.ObjectIncluding({ - _id: String, - roles: [String], - }), - ], - }); + const { bodyParams } = this; - let permissionNotFound = false; - let roleNotFound = false; - Object.keys(this.bodyParams.permissions).forEach((key) => { - const element = this.bodyParams.permissions[key]; + if (!isBodyParamsValidPermissionUpdate(bodyParams)) { + return API.v1.failure('Invalid body params', 'error-invalid-body-params'); + } - if (!Promise.await(Permissions.findOneById(element._id))) { - permissionNotFound = true; - } + const permissionKeys = bodyParams.permissions.map(({ _id }) => _id); + const permissions = await Permissions.find({ _id: { $in: permissionKeys } }).toArray(); - Object.keys(element.roles).forEach((key) => { - const subElement = element.roles[key]; + if (permissions.length !== bodyParams.permissions.length) { + return API.v1.failure('Invalid permission', 'error-invalid-permission'); + } - if (!Promise.await(Roles.findOneById(subElement))) { - roleNotFound = true; - } - }); - }); + const roleKeys = [...new Set(bodyParams.permissions.flatMap((p) => p.roles))]; - if (permissionNotFound) { - return API.v1.failure('Invalid permission', 'error-invalid-permission'); - } if (roleNotFound) { + const roles = await Roles.find({ _id: { $in: roleKeys } }).toArray(); + + if (roles.length !== roleKeys.length) { return API.v1.failure('Invalid role', 'error-invalid-role'); } - Object.keys(this.bodyParams.permissions).forEach((key) => { - const element = this.bodyParams.permissions[key]; - - Permissions.createOrUpdate(element._id, element.roles); - }); + for await (const permission of bodyParams.permissions) { + await Permissions.setRoles(permission._id, permission.roles); + } - const result = Promise.await(Meteor.call('permissions/get')); + const result = await Meteor.call('permissions/get') as IPermission[]; return API.v1.success({ permissions: result, diff --git a/app/api/server/v1/roles.ts b/app/api/server/v1/roles.ts index 8d87ac7f25c7..95c4b34a5d7f 100644 --- a/app/api/server/v1/roles.ts +++ b/app/api/server/v1/roles.ts @@ -1,23 +1,25 @@ import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { Users } from '../../../models/server'; import { API } from '../api'; -import { getUsersInRole, hasPermission, hasRole } from '../../../authorization/server'; +import { getUsersInRole, hasRole } from '../../../authorization/server'; import { settings } from '../../../settings/server/index'; import { api } from '../../../../server/sdk/api'; import { Roles } from '../../../models/server/raw'; +import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; +import { isRoleAddUserToRoleProps, isRoleCreateProps, isRoleDeleteProps, isRoleRemoveUserFromRoleProps, isRoleUpdateProps } from '../../../../definition/rest/v1/roles'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; API.v1.addRoute('roles.list', { authRequired: true }, { - get() { - const roles = Promise.await(Roles.find({}, { fields: { _updatedAt: 0 } }).toArray()); + async get() { + const roles = await Roles.find({}, { projection: { _updatedAt: 0 } }).toArray(); return API.v1.success({ roles }); }, }); API.v1.addRoute('roles.sync', { authRequired: true }, { - get() { + async get() { const { updatedSince } = this.queryParams; if (isNaN(Date.parse(updatedSince))) { @@ -26,21 +28,18 @@ API.v1.addRoute('roles.sync', { authRequired: true }, { return API.v1.success({ roles: { - update: Promise.await(Roles.findByUpdatedDate(new Date(updatedSince), { fields: API.v1.defaultFieldsToExclude }).toArray()), - remove: Promise.await(Roles.trashFindDeletedAfter(new Date(updatedSince)).toArray()), + update: await Roles.findByUpdatedDate(new Date(updatedSince)).toArray(), + remove: await Roles.trashFindDeletedAfter(new Date(updatedSince)).toArray(), }, }); }, }); API.v1.addRoute('roles.create', { authRequired: true }, { - post() { - check(this.bodyParams, { - name: String, - scope: Match.Maybe(String), - description: Match.Maybe(String), - mandatory2fa: Match.Maybe(Boolean), - }); + async post() { + if (!isRoleCreateProps(this.bodyParams)) { + throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.'); + } const roleData = { name: this.bodyParams.name, @@ -49,19 +48,18 @@ API.v1.addRoute('roles.create', { authRequired: true }, { mandatory2fa: this.bodyParams.mandatory2fa, }; - if (!hasPermission(Meteor.userId(), 'access-permissions')) { + if (!await hasPermissionAsync(Meteor.userId(), 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); } - if (Promise.await(Roles.findOneByIdOrName(roleData.name))) { + if (await Roles.findOneByIdOrName(roleData.name)) { throw new Meteor.Error('error-duplicate-role-names-not-allowed', 'Role name already exists'); } if (['Users', 'Subscriptions'].includes(roleData.scope) === false) { roleData.scope = 'Users'; } - const a = Roles.createWithRandomId(roleData.name, roleData.scope, roleData.description, false, roleData.mandatory2fa); - const roleId = Promise.await(a).insertedId; + const roleId = (await Roles.createWithRandomId(roleData.name, roleData.scope, roleData.description, false, roleData.mandatory2fa)).insertedId; if (settings.get('UI_DisplayRoles')) { api.broadcast('user.roleUpdate', { @@ -70,19 +68,23 @@ API.v1.addRoute('roles.create', { authRequired: true }, { }); } + const role = await Roles.findOneByIdOrName(roleId); + + if (!role) { + return API.v1.failure('error-role-not-found', 'Role not found'); + } + return API.v1.success({ - role: Promise.await(Roles.findOneByIdOrName(roleId, { fields: API.v1.defaultFieldsToExclude })), + role, }); }, }); API.v1.addRoute('roles.addUserToRole', { authRequired: true }, { - post() { - check(this.bodyParams, { - roleName: String, - username: String, - roomId: Match.Maybe(String), - }); + async post() { + if (!isRoleAddUserToRoleProps(this.bodyParams)) { + throw new Meteor.Error('error-invalid-role-properties', isRoleAddUserToRoleProps.errors?.map((error) => error.message).join('\n')); + } const user = this.getUserFromParams(); const { roleName, roomId } = this.bodyParams; @@ -91,22 +93,26 @@ API.v1.addRoute('roles.addUserToRole', { authRequired: true }, { throw new Meteor.Error('error-user-already-in-role', 'User already in role'); } - Meteor.runAsUser(this.userId, () => { - Meteor.call('authorization:addUserToRole', roleName, user.username, roomId); - }); + await Meteor.call('authorization:addUserToRole', roleName, user.username, roomId); + + const role = await Roles.findOneByIdOrName(roleName); + + if (!role) { + return API.v1.failure('error-role-not-found', 'Role not found'); + } return API.v1.success({ - role: Promise.await(Roles.findOneByIdOrName(this.bodyParams.roleName, { fields: API.v1.defaultFieldsToExclude })), + role, }); }, }); API.v1.addRoute('roles.getUsersInRole', { authRequired: true }, { - get() { + async get() { const { roomId, role } = this.queryParams; const { offset, count = 50 } = this.getPaginationItems(); - const fields = { + const projection = { name: 1, username: 1, emails: 1, @@ -116,42 +122,39 @@ API.v1.addRoute('roles.getUsersInRole', { authRequired: true }, { if (!role) { throw new Meteor.Error('error-param-not-provided', 'Query param "role" is required'); } - if (!hasPermission(this.userId, 'access-permissions')) { + if (!await hasPermissionAsync(this.userId, 'access-permissions')) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } - if (roomId && !hasPermission(this.userId, 'view-other-user-channels')) { + if (roomId && !await hasPermissionAsync(this.userId, 'view-other-user-channels')) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } - const users = Promise.await(getUsersInRole(role, roomId, { - limit: count, + const users = await getUsersInRole(role, roomId, { + limit: count as number, sort: { username: 1 }, - skip: offset, - fields, - })); + skip: offset as number, + projection, + }); - return API.v1.success({ users: Promise.await(users.toArray()), total: Promise.await(users.count()) }); + return API.v1.success({ users: await users.toArray(), total: await users.count() }); }, }); API.v1.addRoute('roles.update', { authRequired: true }, { - post() { - check(this.bodyParams, { - roleId: String, - name: Match.Maybe(String), - scope: Match.Maybe(String), - description: Match.Maybe(String), - mandatory2fa: Match.Maybe(Boolean), - }); + async post() { + const { bodyParams } = this; + if (!isRoleUpdateProps(bodyParams)) { + throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.'); + } const roleData = { - roleId: this.bodyParams.roleId, - name: this.bodyParams.name, - scope: this.bodyParams.scope, - description: this.bodyParams.description, - mandatory2fa: this.bodyParams.mandatory2fa, + roleId: bodyParams.roleId, + name: bodyParams.name, + scope: bodyParams.scope || 'Users', + description: bodyParams.description, + mandatory2fa: bodyParams.mandatory2fa, }; - const role = Promise.await(Roles.findOneByIdOrName(roleData.roleId)); + const role = await Roles.findOneByIdOrName(roleData.roleId); if (!role) { throw new Meteor.Error('error-invalid-roleId', 'This role does not exist'); @@ -162,19 +165,17 @@ API.v1.addRoute('roles.update', { authRequired: true }, { } if (roleData.name) { - const otherRole = Promise.await(Roles.findOneByIdOrName(roleData.name)); + const otherRole = await Roles.findOneByIdOrName(roleData.name); if (otherRole && otherRole._id !== role._id) { throw new Meteor.Error('error-duplicate-role-names-not-allowed', 'Role name already exists'); } } - if (roleData.scope) { - if (['Users', 'Subscriptions'].includes(roleData.scope) === false) { - roleData.scope = 'Users'; - } + if (['Users', 'Subscriptions'].includes(roleData.scope) === false) { + throw new Meteor.Error('error-invalid-scope', 'Invalid scope'); } - Promise.await(Roles.updateById(roleData.roleId, roleData.name, roleData.scope, roleData.description, roleData.mandatory2fa)); + await Roles.updateById(roleData.roleId, roleData.name, roleData.scope, roleData.description, roleData.mandatory2fa); if (settings.get('UI_DisplayRoles')) { api.broadcast('user.roleUpdate', { @@ -183,23 +184,30 @@ API.v1.addRoute('roles.update', { authRequired: true }, { }); } + const updatedRole = await Roles.findOneByIdOrName(roleData.roleId); + + if (!updatedRole) { + return API.v1.failure(); + } + return API.v1.success({ - role: Promise.await(Roles.findOneByIdOrName(roleData.roleId, { fields: API.v1.defaultFieldsToExclude })), + role: updatedRole, }); }, }); API.v1.addRoute('roles.delete', { authRequired: true }, { - post() { - check(this.bodyParams, { - roleId: String, - }); + async post() { + const { bodyParams } = this; + if (!isRoleDeleteProps(bodyParams)) { + throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.'); + } - if (!hasPermission(this.userId, 'access-permissions')) { + if (!await hasPermissionAsync(this.userId, 'access-permissions')) { throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); } - const role = Promise.await(Roles.findOneByIdOrName(this.bodyParams.roleId)); + const role = await Roles.findOneByIdOrName(bodyParams.roleId); if (!role) { throw new Meteor.Error('error-invalid-roleId', 'This role does not exist'); @@ -209,33 +217,32 @@ API.v1.addRoute('roles.delete', { authRequired: true }, { throw new Meteor.Error('error-role-protected', 'Cannot delete a protected role'); } - const existingUsers = Promise.await(Roles.findUsersInRole(role.name, role.scope)); + const existingUsers = await Roles.findUsersInRole(role.name, role.scope); - if (existingUsers && Promise.await(existingUsers.count()) > 0) { + if (existingUsers && await existingUsers.count() > 0) { throw new Meteor.Error('error-role-in-use', 'Cannot delete role because it\'s in use'); } - Promise.await(Roles.removeById(role._id)); + await Roles.removeById(role._id); return API.v1.success(); }, }); API.v1.addRoute('roles.removeUserFromRole', { authRequired: true }, { - post() { - check(this.bodyParams, { - roleName: String, - username: String, - scope: Match.Maybe(String), - }); + async post() { + const { bodyParams } = this; + if (!isRoleRemoveUserFromRoleProps(bodyParams)) { + throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.'); + } const data = { - roleName: this.bodyParams.roleName, - username: this.bodyParams.username, + roleName: bodyParams.roleName, + username: bodyParams.username, scope: this.bodyParams.scope, }; - if (!hasPermission(this.userId, 'access-permissions')) { + if (!await hasPermissionAsync(this.userId, 'access-permissions')) { throw new Meteor.Error('error-not-allowed', 'Accessing permissions is not allowed'); } @@ -245,24 +252,24 @@ API.v1.addRoute('roles.removeUserFromRole', { authRequired: true }, { throw new Meteor.Error('error-invalid-user', 'There is no user with this username'); } - const role = Promise.await(Roles.findOneByIdOrName(data.roleName)); + const role = await Roles.findOneByIdOrName(data.roleName); if (!role) { throw new Meteor.Error('error-invalid-roleId', 'This role does not exist'); } - if (!hasRole(user._id, role.name, data.scope)) { + if (!await hasRoleAsync(user._id, role.name, data.scope)) { throw new Meteor.Error('error-user-not-in-role', 'User is not in this role'); } if (role._id === 'admin') { - const adminCount = Promise.await(Promise.await(Roles.findUsersInRole('admin')).count()); + const adminCount = await (await Roles.findUsersInRole('admin')).count(); if (adminCount === 1) { throw new Meteor.Error('error-admin-required', 'You need to have at least one admin'); } } - Promise.await(Roles.removeUserRoles(user._id, [role.name], data.scope)); + await Roles.removeUserRoles(user._id, [role.name], data.scope); if (settings.get('UI_DisplayRoles')) { api.broadcast('user.roleUpdate', { diff --git a/app/api/server/v1/settings.ts b/app/api/server/v1/settings.ts index 1f60f2ea2c58..b9233b1e09d1 100644 --- a/app/api/server/v1/settings.ts +++ b/app/api/server/v1/settings.ts @@ -1,14 +1,14 @@ import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import { ServiceConfiguration } from 'meteor/service-configuration'; import _ from 'underscore'; import { Settings } from '../../../models/server/raw'; import { hasPermission } from '../../../authorization/server'; -import { API } from '../api'; +import { API, ResultTypeEndpoints } from '../api'; import { SettingsEvents, settings } from '../../../settings/server'; import { setValue } from '../../../settings/server/raw'; import { ISetting, ISettingColor, isSettingAction, isSettingColor } from '../../../../definition/ISetting'; +import { isOauthCustomConfiguration, isSettingsUpdatePropDefault, isSettingsUpdatePropsActions, isSettingsUpdatePropsColor } from '../../../../definition/rest/v1/settings'; const fetchSettings = async (query: Parameters[0], sort: Parameters[1]['sort'], offset: Parameters[1]['skip'], count: Parameters[1]['limit'], fields: Parameters[1]['projection']): Promise => { @@ -24,74 +24,35 @@ const fetchSettings = async (query: Parameters[0], sort: P return settings; }; -type OauthCustomConfiguration = { - _id: string; - clientId?: string; - custom: unknown; - service?: string; - serverURL: unknown; - tokenPath: unknown; - identityPath: unknown; - authorizePath: unknown; - scope: unknown; - loginStyle: unknown; - tokenSentVia: unknown; - identityTokenSentVia: unknown; - keyField: unknown; - usernameField: unknown; - emailField: unknown; - nameField: unknown; - avatarField: unknown; - rolesClaim: unknown; - groupsClaim: unknown; - mapChannels: unknown; - channelsMap: unknown; - channelsAdmin: unknown; - mergeUsers: unknown; - mergeRoles: unknown; - accessTokenParam: unknown; - showButton: unknown; - - appId: unknown; - consumerKey: unknown; - - clientConfig: unknown; - buttonLabelText: unknown; - buttonLabelColor: unknown; - buttonColor: unknown; -} -const isOauthCustomConfiguration = (config: any): config is OauthCustomConfiguration => Boolean(config); - // settings endpoints API.v1.addRoute('settings.public', { authRequired: false }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); const { sort, fields, query } = this.parseJsonQuery(); - let ourQuery = { + const ourQuery = { + ...query, hidden: { $ne: true }, public: true, }; - ourQuery = Object.assign({}, query, ourQuery); - - const settings = Promise.await(fetchSettings(ourQuery, sort, offset, count, fields)); + const settings = await fetchSettings(ourQuery, sort, offset, count, fields); return API.v1.success({ settings, count: settings.length, offset, - total: Settings.find(ourQuery).count(), + total: await Settings.find(ourQuery).count(), }); }, }); API.v1.addRoute('settings.oauth', { authRequired: false }, { get() { - const mountOAuthServices = (): object => { - const oAuthServicesEnabled = ServiceConfiguration.configurations.find({}, { fields: { secret: 0 } }).fetch(); + const oAuthServicesEnabled = ServiceConfiguration.configurations.find({}, { fields: { secret: 0 } }).fetch(); - return oAuthServicesEnabled.map((service) => { + return API.v1.success({ + services: oAuthServicesEnabled.map((service) => { if (!isOauthCustomConfiguration(service)) { return service; } @@ -109,32 +70,25 @@ API.v1.addRoute('settings.oauth', { authRequired: false }, { buttonLabelColor: service.buttonLabelColor || '', custom: false, }; - }); - }; - - return API.v1.success({ - services: mountOAuthServices(), + }), }); }, }); API.v1.addRoute('settings.addCustomOAuth', { authRequired: true, twoFactorRequired: true }, { - post() { - if (!this.requestParams().name || !this.requestParams().name.trim()) { + async post() { + if (!this.bodyParams.name || !this.bodyParams.name.trim()) { throw new Meteor.Error('error-name-param-not-provided', 'The parameter "name" is required'); } - Meteor.runAsUser(this.userId, () => { - Meteor.call('addOAuthService', this.requestParams().name, this.userId); - }); - + await Meteor.call('addOAuthService', this.bodyParams.name, this.userId); return API.v1.success(); }, }); API.v1.addRoute('settings', { authRequired: true }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); const { sort, fields, query } = this.parseJsonQuery(); @@ -148,7 +102,7 @@ API.v1.addRoute('settings', { authRequired: true }, { ourQuery = Object.assign({}, query, ourQuery); - const settings = Promise.await(fetchSettings(ourQuery, sort, offset, count, fields)); + const settings = await fetchSettings(ourQuery, sort, offset, count, fields); return API.v1.success({ settings, @@ -160,11 +114,11 @@ API.v1.addRoute('settings', { authRequired: true }, { }); API.v1.addRoute('settings/:_id', { authRequired: true }, { - get() { + async get() { if (!hasPermission(this.userId, 'view-privileged-setting')) { return API.v1.unauthorized(); } - const setting = Promise.await(Settings.findOneNotHiddenById(this.urlParams._id)); + const setting = await Settings.findOneNotHiddenById(this.urlParams._id); if (!setting) { return API.v1.failure(); } @@ -172,35 +126,36 @@ API.v1.addRoute('settings/:_id', { authRequired: true }, { }, post: { twoFactorRequired: true, - action(this: any): void { + async action(): Promise['post']> { if (!hasPermission(this.userId, 'edit-privileged-setting')) { return API.v1.unauthorized(); } + if (typeof this.urlParams._id !== 'string') { + throw new Meteor.Error('error-id-param-not-provided', 'The parameter "id" is required'); + } + // allow special handling of particular setting types - const setting = Promise.await(Settings.findOneNotHiddenById(this.urlParams._id)); + const setting = await Settings.findOneNotHiddenById(this.urlParams._id); if (!setting) { return API.v1.failure(); } - if (isSettingAction(setting) && this.bodyParams && this.bodyParams.execute) { + if (isSettingAction(setting) && isSettingsUpdatePropsActions(this.bodyParams) && this.bodyParams.execute) { // execute the configured method Meteor.call(setting.value); return API.v1.success(); } - if (isSettingColor(setting) && this.bodyParams && this.bodyParams.editor && this.bodyParams.value) { + if (isSettingColor(setting) && isSettingsUpdatePropsColor(this.bodyParams)) { Settings.updateOptionsById(this.urlParams._id, { editor: this.bodyParams.editor }); Settings.updateValueNotHiddenById(this.urlParams._id, this.bodyParams.value); return API.v1.success(); } - check(this.bodyParams, { - value: Match.Any, - }); - if (Promise.await(Settings.updateValueNotHiddenById(this.urlParams._id, this.bodyParams.value))) { - const s = Promise.await(Settings.findOneNotHiddenById(this.urlParams._id)); + if (isSettingsUpdatePropDefault(this.bodyParams) && await Settings.updateValueNotHiddenById(this.urlParams._id, this.bodyParams.value)) { + const s = await Settings.findOneNotHiddenById(this.urlParams._id); if (!s) { return API.v1.failure(); } diff --git a/app/api/server/v1/teams.ts b/app/api/server/v1/teams.ts index e2a24d08413f..dab3ac1e0a82 100644 --- a/app/api/server/v1/teams.ts +++ b/app/api/server/v1/teams.ts @@ -9,13 +9,22 @@ import { hasAtLeastOnePermission, hasPermission } from '../../../authorization/s import { Users } from '../../../models/server'; import { removeUserFromRoom } from '../../../lib/server/functions/removeUserFromRoom'; import { IUser } from '../../../../definition/IUser'; +import { isTeamPropsWithTeamName, isTeamPropsWithTeamId } from '../../../../definition/rest/v1/teams'; +import { isTeamsConvertToChannelProps } from '../../../../definition/rest/v1/teams/TeamsConvertToChannelProps'; +import { isTeamsRemoveRoomProps } from '../../../../definition/rest/v1/teams/TeamsRemoveRoomProps'; +import { isTeamsUpdateMemberProps } from '../../../../definition/rest/v1/teams/TeamsUpdateMemberProps'; +import { isTeamsRemoveMemberProps } from '../../../../definition/rest/v1/teams/TeamsRemoveMemberProps'; +import { isTeamsAddMembersProps } from '../../../../definition/rest/v1/teams/TeamsAddMembersProps'; +import { isTeamsDeleteProps } from '../../../../definition/rest/v1/teams/TeamsDeleteProps'; +import { isTeamsLeaveProps } from '../../../../definition/rest/v1/teams/TeamsLeaveProps'; +import { isTeamsUpdateProps } from '../../../../definition/rest/v1/teams/TeamsUpdateProps'; API.v1.addRoute('teams.list', { authRequired: true }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); const { sort, query } = this.parseJsonQuery(); - const { records, total } = Promise.await(Team.list(this.userId, { offset, count }, { sort, query })); + const { records, total } = await Team.list(this.userId, { offset, count }, { sort, query }); return API.v1.success({ teams: records, @@ -27,14 +36,14 @@ API.v1.addRoute('teams.list', { authRequired: true }, { }); API.v1.addRoute('teams.listAll', { authRequired: true }, { - get() { + async get() { if (!hasPermission(this.userId, 'view-all-teams')) { return API.v1.unauthorized(); } - const { offset, count } = this.getPaginationItems(); + const { offset, count } = this.getPaginationItems() as { offset: number; count: number }; - const { records, total } = Promise.await(Team.listAll({ offset, count })); + const { records, total } = await Team.listAll({ offset, count }); return API.v1.success({ teams: records, @@ -46,7 +55,7 @@ API.v1.addRoute('teams.listAll', { authRequired: true }, { }); API.v1.addRoute('teams.create', { authRequired: true }, { - post() { + async post() { if (!hasPermission(this.userId, 'create-team')) { return API.v1.unauthorized(); } @@ -56,7 +65,7 @@ API.v1.addRoute('teams.create', { authRequired: true }, { return API.v1.failure('Body param "name" is required'); } - const team = Promise.await(Team.create(this.userId, { + const team = await Team.create(this.userId, { team: { name, type, @@ -64,26 +73,27 @@ API.v1.addRoute('teams.create', { authRequired: true }, { room, members, owner, - })); + }); return API.v1.success({ team }); }, }); API.v1.addRoute('teams.convertToChannel', { authRequired: true }, { - post() { - check(this.bodyParams, Match.ObjectIncluding({ - teamId: Match.Maybe(String), - teamName: Match.Maybe(String), - roomsToRemove: Match.Maybe([String]), - })); - const { roomsToRemove, teamId, teamName } = this.bodyParams; - - if (!teamId && !teamName) { - return API.v1.failure('missing-teamId-or-teamName'); + async post() { + if (!isTeamsConvertToChannelProps(this.bodyParams)) { + return API.v1.failure('invalid-body-params', isTeamsConvertToChannelProps.errors?.map((e) => e.message).join('\n ')); } - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + const { bodyParams } = this; + + const { roomsToRemove = [] } = bodyParams; + + const team = await ( + (isTeamPropsWithTeamId(bodyParams) && Team.getOneById(bodyParams.teamId)) + || (isTeamPropsWithTeamName(bodyParams) && Team.getOneByName(bodyParams.teamName)) + ); + if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -92,7 +102,7 @@ API.v1.addRoute('teams.convertToChannel', { authRequired: true }, { return API.v1.unauthorized(); } - const rooms: string[] = Promise.await(Team.getMatchingTeamRooms(team._id, roomsToRemove)); + const rooms = await Team.getMatchingTeamRooms(team._id, roomsToRemove); if (rooms.length) { rooms.forEach((room) => { @@ -100,7 +110,7 @@ API.v1.addRoute('teams.convertToChannel', { authRequired: true }, { }); } - Promise.all([ + await Promise.all([ Team.unsetTeamIdOfRooms(team._id), Team.removeAllMembersFromTeam(team._id), Team.deleteById(team._id), @@ -111,14 +121,14 @@ API.v1.addRoute('teams.convertToChannel', { authRequired: true }, { }); API.v1.addRoute('teams.addRooms', { authRequired: true }, { - post() { + async post() { const { rooms, teamId, teamName } = this.bodyParams; if (!teamId && !teamName) { return API.v1.failure('missing-teamId-or-teamName'); } - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + const team = await (teamId ? Team.getOneById(teamId) : Team.getOneByName(teamName)); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -127,17 +137,20 @@ API.v1.addRoute('teams.addRooms', { authRequired: true }, { return API.v1.unauthorized('error-no-permission-team-channel'); } - const validRooms = Promise.await(Team.addRooms(this.userId, rooms, team._id)); + const validRooms = await Team.addRooms(this.userId, rooms, team._id); return API.v1.success({ rooms: validRooms }); }, }); API.v1.addRoute('teams.removeRoom', { authRequired: true }, { - post() { + async post() { + if (!isTeamsRemoveRoomProps(this.bodyParams)) { + return API.v1.failure('body-params-invalid', isTeamsRemoveRoomProps.errors?.map((error) => error.message).join('\n ')); + } const { roomId, teamId, teamName } = this.bodyParams; - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + const team = await (teamId ? Team.getOneById(teamId) : Team.getOneByName(teamName)); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -148,17 +161,17 @@ API.v1.addRoute('teams.removeRoom', { authRequired: true }, { const canRemoveAny = !!hasPermission(this.userId, 'view-all-team-channels', team.roomId); - const room = Promise.await(Team.removeRoom(this.userId, roomId, team._id, canRemoveAny)); + const room = await Team.removeRoom(this.userId, roomId, team._id, canRemoveAny); return API.v1.success({ room }); }, }); API.v1.addRoute('teams.updateRoom', { authRequired: true }, { - post() { + async post() { const { roomId, isDefault } = this.bodyParams; - const team = Promise.await(Team.getOneByRoomId(roomId)); + const team = await Team.getOneByRoomId(roomId); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -168,18 +181,18 @@ API.v1.addRoute('teams.updateRoom', { authRequired: true }, { } const canUpdateAny = !!hasPermission(this.userId, 'view-all-team-channels', team.roomId); - const room = Promise.await(Team.updateRoom(this.userId, roomId, isDefault, canUpdateAny)); + const room = await Team.updateRoom(this.userId, roomId, isDefault, canUpdateAny); return API.v1.success({ room }); }, }); API.v1.addRoute('teams.listRooms', { authRequired: true }, { - get() { + async get() { const { teamId, teamName, filter, type } = this.queryParams; const { offset, count } = this.getPaginationItems(); - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + const team = await (teamId ? Team.getOneById(teamId) : Team.getOneByName(teamName)); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -198,7 +211,7 @@ API.v1.addRoute('teams.listRooms', { authRequired: true }, { allowPrivateTeam, }; - const { records, total } = Promise.await(Team.listRooms(this.userId, team._id, listFilter, { offset, count })); + const { records, total } = await Team.listRooms(this.userId, team._id, listFilter, { offset, count }); return API.v1.success({ rooms: records, @@ -210,11 +223,17 @@ API.v1.addRoute('teams.listRooms', { authRequired: true }, { }); API.v1.addRoute('teams.listRoomsOfUser', { authRequired: true }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); const { teamId, teamName, userId, canUserDelete = false } = this.queryParams; - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + + if (!teamId && !teamName) { + return API.v1.failure('missing-teamId-or-teamName'); + } + + const team = await (teamId ? Team.getOneById(teamId) : Team.getOneByName(teamName!)); + if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -225,7 +244,7 @@ API.v1.addRoute('teams.listRoomsOfUser', { authRequired: true }, { return API.v1.unauthorized(); } - const { records, total } = Promise.await(Team.listRoomsOfUser(this.userId, team._id, userId, allowPrivateTeam, canUserDelete, { offset, count })); + const { records, total } = await Team.listRoomsOfUser(this.userId, team._id, userId, allowPrivateTeam, canUserDelete, { offset, count }); return API.v1.success({ rooms: records, @@ -237,7 +256,7 @@ API.v1.addRoute('teams.listRoomsOfUser', { authRequired: true }, { }); API.v1.addRoute('teams.members', { authRequired: true }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); check(this.queryParams, Match.ObjectIncluding({ @@ -253,7 +272,7 @@ API.v1.addRoute('teams.members', { authRequired: true }, { return API.v1.failure('missing-teamId-or-teamName'); } - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + const team = await (teamId ? Team.getOneById(teamId) : Team.getOneByName(teamName)); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -265,7 +284,7 @@ API.v1.addRoute('teams.members', { authRequired: true }, { status: status ? { $in: status } : undefined, } as FilterQuery; - const { records, total } = Promise.await(Team.members(this.userId, team._id, canSeeAllMembers, { offset, count }, query)); + const { records, total } = await Team.members(this.userId, team._id, canSeeAllMembers, { offset, count }, query); return API.v1.success({ members: records, @@ -277,10 +296,19 @@ API.v1.addRoute('teams.members', { authRequired: true }, { }); API.v1.addRoute('teams.addMembers', { authRequired: true }, { - post() { - const { teamId, teamName, members } = this.bodyParams; + async post() { + if (!isTeamsAddMembersProps(this.bodyParams)) { + return API.v1.failure('invalid-params'); + } + + const { bodyParams } = this; + const { members } = bodyParams; + + const team = await ( + (isTeamPropsWithTeamId(bodyParams) && Team.getOneById(bodyParams.teamId)) + || (isTeamPropsWithTeamName(bodyParams) && Team.getOneByName(bodyParams.teamName)) + ); - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -289,17 +317,25 @@ API.v1.addRoute('teams.addMembers', { authRequired: true }, { return API.v1.unauthorized(); } - Promise.await(Team.addMembers(this.userId, team._id, members)); + await Team.addMembers(this.userId, team._id, members); return API.v1.success(); }, }); API.v1.addRoute('teams.updateMember', { authRequired: true }, { - post() { - const { teamId, teamName, member } = this.bodyParams; + async post() { + if (!isTeamsUpdateMemberProps(this.bodyParams)) { + return API.v1.failure('invalid-params', isTeamsUpdateMemberProps.errors?.map((e) => e.message).join('\n ')); + } - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + const { bodyParams } = this; + const { member } = bodyParams; + + const team = await ( + (isTeamPropsWithTeamId(bodyParams) && Team.getOneById(bodyParams.teamId)) + || (isTeamPropsWithTeamName(bodyParams) && Team.getOneByName(bodyParams.teamName)) + ); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -308,17 +344,26 @@ API.v1.addRoute('teams.updateMember', { authRequired: true }, { return API.v1.unauthorized(); } - Promise.await(Team.updateMember(team._id, member)); + await Team.updateMember(team._id, member); return API.v1.success(); }, }); API.v1.addRoute('teams.removeMember', { authRequired: true }, { - post() { - const { teamId, teamName, userId, rooms } = this.bodyParams; + async post() { + if (!isTeamsRemoveMemberProps(this.bodyParams)) { + return API.v1.failure('invalid-params', isTeamsRemoveMemberProps.errors?.map((e) => e.message).join('\n ')); + } + + const { bodyParams } = this; + const { userId, rooms } = bodyParams; + + const team = await ( + (isTeamPropsWithTeamId(bodyParams) && Team.getOneById(bodyParams.teamId)) + || (isTeamPropsWithTeamName(bodyParams) && Team.getOneByName(bodyParams.teamName)) + ); - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -332,12 +377,12 @@ API.v1.addRoute('teams.removeMember', { authRequired: true }, { return API.v1.failure('invalid-user'); } - if (!Promise.await(Team.removeMembers(this.userId, team._id, [{ userId }]))) { + if (!await Team.removeMembers(this.userId, team._id, [{ userId }])) { return API.v1.failure(); } if (rooms?.length) { - const roomsFromTeam: string[] = Promise.await(Team.getMatchingTeamRooms(team._id, rooms)); + const roomsFromTeam: string[] = await Team.getMatchingTeamRooms(team._id, rooms); roomsFromTeam.forEach((rid) => { removeUserFromRoom(rid, user, { @@ -350,20 +395,30 @@ API.v1.addRoute('teams.removeMember', { authRequired: true }, { }); API.v1.addRoute('teams.leave', { authRequired: true }, { - post() { - const { teamId, teamName, rooms } = this.bodyParams; + async post() { + if (!isTeamsLeaveProps(this.bodyParams)) { + return API.v1.failure('invalid-params', isTeamsLeaveProps.errors?.map((e) => e.message).join('\n ')); + } + + const { bodyParams } = this; + + const { rooms = [] } = bodyParams; + + const team = await ( + (isTeamPropsWithTeamId(bodyParams) && Team.getOneById(bodyParams.teamId)) + || (isTeamPropsWithTeamName(bodyParams) && Team.getOneByName(bodyParams.teamName)) + ); - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); if (!team) { return API.v1.failure('team-does-not-exist'); } - Promise.await(Team.removeMembers(this.userId, team._id, [{ + await Team.removeMembers(this.userId, team._id, [{ userId: this.userId, - }])); + }]); - if (rooms?.length) { - const roomsFromTeam: string[] = Promise.await(Team.getMatchingTeamRooms(team._id, rooms)); + if (rooms.length) { + const roomsFromTeam: string[] = await Team.getMatchingTeamRooms(team._id, rooms); roomsFromTeam.forEach((rid) => { removeUserFromRoom(rid, this.user); @@ -375,16 +430,16 @@ API.v1.addRoute('teams.leave', { authRequired: true }, { }); API.v1.addRoute('teams.info', { authRequired: true }, { - get() { + async get() { const { teamId, teamName } = this.queryParams; if (!teamId && !teamName) { return API.v1.failure('Provide either the "teamId" or "teamName"'); } - const teamInfo = teamId - ? Promise.await(Team.getInfoById(teamId)) - : Promise.await(Team.getInfoByName(teamName)); + const teamInfo = await (teamId + ? Team.getInfoById(teamId) + : Team.getInfoByName(teamName)); if (!teamInfo) { return API.v1.failure('Team not found'); @@ -395,18 +450,19 @@ API.v1.addRoute('teams.info', { authRequired: true }, { }); API.v1.addRoute('teams.delete', { authRequired: true }, { - post() { - const { teamId, teamName, roomsToRemove } = this.bodyParams; + async post() { + const { bodyParams } = this; + const { roomsToRemove = [] } = this.bodyParams; - if (!teamId && !teamName) { - return API.v1.failure('Provide either the "teamId" or "teamName"'); + if (!isTeamsDeleteProps(bodyParams)) { + return API.v1.failure('invalid-params', isTeamsDeleteProps.errors?.map((e) => e.message).join('\n ')); } - if (roomsToRemove && !Array.isArray(roomsToRemove)) { - return API.v1.failure('The list of rooms to remove is invalid.'); - } + const team = await ( + (isTeamPropsWithTeamId(bodyParams) && Team.getOneById(bodyParams.teamId)) + || (isTeamPropsWithTeamName(bodyParams) && Team.getOneByName(bodyParams.teamName)) + ); - const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); if (!team) { return API.v1.failure('Team not found.'); } @@ -415,7 +471,7 @@ API.v1.addRoute('teams.delete', { authRequired: true }, { return API.v1.unauthorized(); } - const rooms: string[] = Promise.await(Team.getMatchingTeamRooms(team._id, roomsToRemove)); + const rooms: string[] = await Team.getMatchingTeamRooms(team._id, roomsToRemove); // Remove the team's main room Meteor.call('eraseRoom', team.roomId); @@ -428,41 +484,42 @@ API.v1.addRoute('teams.delete', { authRequired: true }, { } // Move every other room back to the workspace - Promise.await(Team.unsetTeamIdOfRooms(team._id)); + await Team.unsetTeamIdOfRooms(team._id); // Delete all team memberships - Team.removeAllMembersFromTeam(teamId); + Team.removeAllMembersFromTeam(team._id); // And finally delete the team itself - Promise.await(Team.deleteById(team._id)); + await Team.deleteById(team._id); return API.v1.success(); }, }); API.v1.addRoute('teams.autocomplete', { authRequired: true }, { - get() { + async get() { const { name } = this.queryParams; - const teams = Promise.await(Team.autocomplete(this.userId, name)); + const teams = await Team.autocomplete(this.userId, name); return API.v1.success({ teams }); }, }); API.v1.addRoute('teams.update', { authRequired: true }, { - post() { - check(this.bodyParams, { - teamId: String, - data: { - name: Match.Maybe(String), - type: Match.Maybe(Number), - }, - }); + async post() { + const { bodyParams } = this; + if (!isTeamsUpdateProps(bodyParams)) { + return API.v1.failure('invalid-params', isTeamsUpdateProps.errors?.map((e) => e.message).join('\n ')); + } + + const { data } = bodyParams; - const { teamId, data } = this.bodyParams; + const team = await ( + (isTeamPropsWithTeamId(bodyParams) && Team.getOneById(bodyParams.teamId)) + || (isTeamPropsWithTeamName(bodyParams) && Team.getOneByName(bodyParams.teamName)) + ); - const team = teamId && Promise.await(Team.getOneById(teamId)); if (!team) { return API.v1.failure('team-does-not-exist'); } @@ -471,7 +528,7 @@ API.v1.addRoute('teams.update', { authRequired: true }, { return API.v1.unauthorized(); } - Promise.await(Team.update(this.userId, teamId, { name: data.name, type: data.type })); + await Team.update(this.userId, team._id, data); return API.v1.success(); }, diff --git a/app/authorization/server/functions/getUsersInRole.ts b/app/authorization/server/functions/getUsersInRole.ts index 8a8bcadf3dba..740431af3f06 100644 --- a/app/authorization/server/functions/getUsersInRole.ts +++ b/app/authorization/server/functions/getUsersInRole.ts @@ -9,6 +9,6 @@ export function getUsersInRole(name: IRole['name'], scope?: string): Promise>): Promise>; -export function getUsersInRole

(name: IRole['name'], scope: string | undefined, options: FindOneOptions

): Promise>; +export function getUsersInRole

(name: IRole['name'], scope: string | undefined, options: FindOneOptions

): Promise>; -export function getUsersInRole

(name: IRole['name'], scope: string | undefined, options?: any | undefined): Promise> { return Roles.findUsersInRole(name, scope, options); } +export function getUsersInRole

(name: IRole['name'], scope: string | undefined, options?: any | undefined): Promise> { return Roles.findUsersInRole(name, scope, options); } diff --git a/app/models/server/raw/BaseRaw.ts b/app/models/server/raw/BaseRaw.ts index 500fe55d6e1a..1385bf890c6f 100644 --- a/app/models/server/raw/BaseRaw.ts +++ b/app/models/server/raw/BaseRaw.ts @@ -166,7 +166,7 @@ export class BaseRaw = undefined> implements IBase find(query: FilterQuery, options: WithoutProjection>): Cursor>; - find

(query: FilterQuery, options: FindOneOptions

): Cursor

; + find

(query: FilterQuery, options: FindOneOptions

): Cursor

; find

(query: FilterQuery | undefined = {}, options?: any): Cursor

| Cursor { const optionsDef = this.doNotMixInclusionAndExclusionFields(options); diff --git a/app/models/server/raw/Permissions.ts b/app/models/server/raw/Permissions.ts index 0c5ae1f8533e..1b0bacacc53b 100644 --- a/app/models/server/raw/Permissions.ts +++ b/app/models/server/raw/Permissions.ts @@ -30,6 +30,10 @@ export class PermissionsRaw extends BaseRaw { await this.update({ _id: permission, roles: { $ne: role } }, { $addToSet: { roles: role } }); } + async setRoles(permission: string, roles: string[]): Promise { + await this.update({ _id: permission }, { $set: { roles } }); + } + async removeRole(permission: string, role: string): Promise { await this.update({ _id: permission, roles: role }, { $pull: { roles: role } }); } diff --git a/app/models/server/raw/Roles.ts b/app/models/server/raw/Roles.ts index db7913eb7e2a..c858d2445ce1 100644 --- a/app/models/server/raw/Roles.ts +++ b/app/models/server/raw/Roles.ts @@ -17,12 +17,12 @@ export class RolesRaw extends BaseRaw { } - findByUpdatedDate

(updatedAfterDate: Date, options: FindOneOptions

): Cursor

| Cursor { + findByUpdatedDate(updatedAfterDate: Date, options?: FindOneOptions): Cursor { const query = { _updatedAt: { $gte: new Date(updatedAfterDate) }, }; - return this.find(query, options); + return options ? this.find(query, options) : this.find(query); } @@ -135,7 +135,7 @@ export class RolesRaw extends BaseRaw { return this.findOne(query, options); } - updateById(_id: IRole['_id'], name: IRole['name'], scope: IRole['scope'], description: IRole['description'], mandatory2fa: IRole['mandatory2fa']): Promise { + updateById(_id: IRole['_id'], name: IRole['name'], scope: IRole['scope'], description: IRole['description'] = '', mandatory2fa: IRole['mandatory2fa'] = false): Promise { const queryData = { name, scope, @@ -151,7 +151,7 @@ export class RolesRaw extends BaseRaw { findUsersInRole(name: IRole['name'], scope: string | undefined, options: WithoutProjection>): Promise>; - findUsersInRole

(name: IRole['name'], scope: string | undefined, options: FindOneOptions

): Promise>; + findUsersInRole

(name: IRole['name'], scope: string | undefined, options: FindOneOptions

): Promise>; async findUsersInRole

(name: IRole['name'], scope: string | undefined, options?: any | undefined): Promise | Cursor

> { const role = await this.findOne({ name }, { scope: 1 } as FindOneOptions); diff --git a/app/models/server/raw/Settings.ts b/app/models/server/raw/Settings.ts index 2d3a11b2ccea..7e84d539b05e 100644 --- a/app/models/server/raw/Settings.ts +++ b/app/models/server/raw/Settings.ts @@ -116,7 +116,7 @@ export class SettingsRaw extends BaseRaw { filter._id = { $in: ids }; } - return this.find(filter, { fields: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1, requiredOnWizard: 1 } }) as any; + return this.find(filter, { projection: { _id: 1, value: 1, editor: 1, enterprise: 1, invalidValue: 1, modules: 1, requiredOnWizard: 1 } }); } findSetupWizardSettings(): Cursor { diff --git a/client/contexts/ServerContext/ServerContext.ts b/client/contexts/ServerContext/ServerContext.ts index d791fea0d2d4..15b8c50f97f5 100644 --- a/client/contexts/ServerContext/ServerContext.ts +++ b/client/contexts/ServerContext/ServerContext.ts @@ -2,7 +2,7 @@ import { createContext, useCallback, useContext, useMemo } from 'react'; import { IServerInfo } from '../../../definition/IServerInfo'; import type { Serialized } from '../../../definition/Serialized'; -import type { PathFor, Params, Return, Method } from './endpoints'; +import type { PathFor, Params, Return, Method } from '../../../definition/rest'; import { ServerMethodFunction, ServerMethodName, diff --git a/client/contexts/ServerContext/endpoints.ts b/client/contexts/ServerContext/endpoints.ts deleted file mode 100644 index 0a57ef4479d0..000000000000 --- a/client/contexts/ServerContext/endpoints.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { ExtractKeys, ValueOf } from '../../../definition/utils'; -import type { EngagementDashboardEndpoints } from '../../../ee/client/contexts/ServerContext/endpoints/v1/engagementDashboard'; -import type { AppsEndpoints } from './endpoints/apps'; -import type { ChannelsEndpoints } from './endpoints/v1/channels'; -import type { ChatEndpoints } from './endpoints/v1/chat'; -import type { CloudEndpoints } from './endpoints/v1/cloud'; -import type { CustomUserStatusEndpoints } from './endpoints/v1/customUserStatus'; -import type { DmEndpoints } from './endpoints/v1/dm'; -import type { DnsEndpoints } from './endpoints/v1/dns'; -import type { EmojiCustomEndpoints } from './endpoints/v1/emojiCustom'; -import type { GroupsEndpoints } from './endpoints/v1/groups'; -import type { ImEndpoints } from './endpoints/v1/im'; -import type { LDAPEndpoints } from './endpoints/v1/ldap'; -import type { LicensesEndpoints } from './endpoints/v1/licenses'; -import type { MiscEndpoints } from './endpoints/v1/misc'; -import type { OmnichannelEndpoints } from './endpoints/v1/omnichannel'; -import type { RoomsEndpoints } from './endpoints/v1/rooms'; -import type { StatisticsEndpoints } from './endpoints/v1/statistics'; -import type { TeamsEndpoints } from './endpoints/v1/teams'; -import type { UsersEndpoints } from './endpoints/v1/users'; - -type Endpoints = ChatEndpoints & - ChannelsEndpoints & - CloudEndpoints & - CustomUserStatusEndpoints & - DmEndpoints & - DnsEndpoints & - EmojiCustomEndpoints & - GroupsEndpoints & - ImEndpoints & - LDAPEndpoints & - RoomsEndpoints & - TeamsEndpoints & - UsersEndpoints & - EngagementDashboardEndpoints & - AppsEndpoints & - OmnichannelEndpoints & - StatisticsEndpoints & - LicensesEndpoints & - MiscEndpoints; - -type Endpoint = UnionizeEndpoints; - -type UnionizeEndpoints = ValueOf< - { - [P in keyof EE]: UnionizeMethods; - } ->; - -type ExtractOperations = ExtractKeys any>; - -type UnionizeMethods = ValueOf< - { - [M in keyof OO as ExtractOperations]: ( - method: M, - path: OO extends { path: string } ? OO['path'] : P, - ...params: Parameters any>> - ) => ReturnType any>>; - } ->; - -export type Method = Parameters[0]; -export type Path = Parameters[1]; - -export type MethodFor

= P extends any - ? Parameters any>>[0] - : never; -export type PathFor = M extends any - ? Parameters any>>[1] - : never; - -type Operation> = M extends any - ? P extends any - ? Extract any> - : never - : never; - -type ExtractParams = Q extends [any, any] - ? [undefined?] - : Q extends [any, any, any, ...any[]] - ? [Q[2]] - : never; - -export type Params> = ExtractParams< - Parameters> ->; -export type Return> = ReturnType>; diff --git a/client/contexts/ServerContext/endpoints/v1/dm.ts b/client/contexts/ServerContext/endpoints/v1/dm.ts deleted file mode 100644 index 0b20aad1819c..000000000000 --- a/client/contexts/ServerContext/endpoints/v1/dm.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { IRoom } from '../../../../../definition/IRoom'; -import type { IUser } from '../../../../../definition/IUser'; - -export type DmEndpoints = { - 'dm.create': { - POST: ( - params: ( - | { - username: Exclude; - } - | { - usernames: string; - } - ) & { - excludeSelf?: boolean; - }, - ) => { - room: IRoom & { rid: IRoom['_id'] }; - }; - }; -}; diff --git a/client/contexts/ServerContext/endpoints/v1/teams.ts b/client/contexts/ServerContext/endpoints/v1/teams.ts deleted file mode 100644 index 70f8a7c10b12..000000000000 --- a/client/contexts/ServerContext/endpoints/v1/teams.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { IRoom } from '../../../../../definition/IRoom'; -import type { IRecordsWithTotal, ITeam } from '../../../../../definition/ITeam'; -import type { IUser } from '../../../../../definition/IUser'; - -export type TeamsEndpoints = { - 'teams.addRooms': { - POST: (params: { rooms: IRoom['_id'][]; teamId: string }) => void; - }; - 'teams.info': { - GET: (params: { teamId: IRoom['teamId'] }) => { teamInfo: ITeam }; - }; - 'teams.listRooms': { - GET: (params: { - teamId: ITeam['_id']; - offset?: number; - count?: number; - filter: string; - type: string; - }) => Omit, 'records'> & { - count: number; - offset: number; - rooms: IRecordsWithTotal['records']; - }; - }; - 'teams.listRoomsOfUser': { - GET: (params: { - teamId: ITeam['_id']; - teamName?: string; - userId?: string; - canUserDelete?: boolean; - offset?: number; - count?: number; - }) => Omit, 'records'> & { - count: number; - offset: number; - rooms: IRecordsWithTotal['records']; - }; - }; - 'teams.create': { - POST: (params: { - name: ITeam['name']; - type?: ITeam['type']; - members?: IUser['_id'][]; - room: { - id?: string; - name?: IRoom['name']; - members?: IUser['_id'][]; - readOnly?: boolean; - extraData?: { - teamId?: string; - teamMain?: boolean; - } & { [key: string]: string | boolean }; - options?: { - nameValidationRegex?: string; - creator: string; - subscriptionExtra?: { - open: boolean; - ls: Date; - prid: IRoom['_id']; - }; - } & { - [key: string]: - | string - | { - open: boolean; - ls: Date; - prid: IRoom['_id']; - }; - }; - }; - owner?: IUser['_id']; - }) => { - team: ITeam; - }; - }; -}; diff --git a/client/contexts/ServerContext/index.ts b/client/contexts/ServerContext/index.ts index a2807467fddf..cd41c0b16c17 100644 --- a/client/contexts/ServerContext/index.ts +++ b/client/contexts/ServerContext/index.ts @@ -1,3 +1,2 @@ export * from './ServerContext'; -export * from './endpoints'; export * from './methods'; diff --git a/client/hooks/useEndpointAction.ts b/client/hooks/useEndpointAction.ts index 65a7f2ccaed3..0deb6cdd7d7c 100644 --- a/client/hooks/useEndpointAction.ts +++ b/client/hooks/useEndpointAction.ts @@ -1,8 +1,8 @@ import { useCallback } from 'react'; import { Serialized } from '../../definition/Serialized'; +import { Method, Params, PathFor, Return } from '../../definition/rest'; import { useEndpoint } from '../contexts/ServerContext'; -import { Method, Params, PathFor, Return } from '../contexts/ServerContext/endpoints'; import { useToastMessageDispatch } from '../contexts/ToastMessagesContext'; export const useEndpointAction = >( diff --git a/client/hooks/useEndpointActionExperimental.ts b/client/hooks/useEndpointActionExperimental.ts index b6cb286f46ae..0e82f6d42830 100644 --- a/client/hooks/useEndpointActionExperimental.ts +++ b/client/hooks/useEndpointActionExperimental.ts @@ -1,8 +1,8 @@ import { useCallback } from 'react'; import { Serialized } from '../../definition/Serialized'; +import { Method, Params, PathFor, Return } from '../../definition/rest'; import { useEndpoint } from '../contexts/ServerContext'; -import { Method, Params, PathFor, Return } from '../contexts/ServerContext/endpoints'; import { useToastMessageDispatch } from '../contexts/ToastMessagesContext'; export const useEndpointActionExperimental = >( diff --git a/client/hooks/useEndpointData.ts b/client/hooks/useEndpointData.ts index 6412cd9d7f3b..1e8f5ebc880c 100644 --- a/client/hooks/useEndpointData.ts +++ b/client/hooks/useEndpointData.ts @@ -1,8 +1,8 @@ import { useCallback, useEffect } from 'react'; import { Serialized } from '../../definition/Serialized'; +import { Params, PathFor, Return } from '../../definition/rest'; import { useEndpoint } from '../contexts/ServerContext'; -import { Params, PathFor, Return } from '../contexts/ServerContext/endpoints'; import { useToastMessageDispatch } from '../contexts/ToastMessagesContext'; import { AsyncState, useAsyncState } from './useAsyncState'; diff --git a/client/providers/ServerProvider.tsx b/client/providers/ServerProvider.tsx index 89b1c28b9599..43f600d2262e 100644 --- a/client/providers/ServerProvider.tsx +++ b/client/providers/ServerProvider.tsx @@ -3,11 +3,8 @@ import React, { FC } from 'react'; import { Info as info, APIClient } from '../../app/utils/client'; import { Serialized } from '../../definition/Serialized'; +import { Method, Params, Return, PathFor } from '../../definition/rest'; import { - Method, - Params, - PathFor, - Return, ServerContext, ServerMethodName, ServerMethodParameters, diff --git a/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts b/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts index 6f144e0400a8..c310c75b73ad 100644 --- a/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts +++ b/client/views/teams/contextualBar/channels/hooks/useTeamsChannelList.ts @@ -41,9 +41,10 @@ export const useTeamsChannelList = ( }); return { - items: rooms.map(({ _updatedAt, lastMessage, lm, jitsiTimeout, ...room }) => ({ + items: rooms.map(({ _updatedAt, lastMessage, lm, ts, jitsiTimeout, ...room }) => ({ jitsiTimeout: new Date(jitsiTimeout), ...(lm && { lm: new Date(lm) }), + ...(ts && { ts: new Date(ts) }), _updatedAt: new Date(_updatedAt), ...(lastMessage && { lastMessage: mapMessageFromApi(lastMessage) }), ...room, diff --git a/definition/IMessage/IMessage.ts b/definition/IMessage/IMessage.ts index 55f46c283868..8fa8b66c348d 100644 --- a/definition/IMessage/IMessage.ts +++ b/definition/IMessage/IMessage.ts @@ -1,11 +1,11 @@ import { MessageSurfaceLayout } from '@rocket.chat/ui-kit'; import { parser } from '@rocket.chat/message-parser'; -import { IRocketChatRecord } from '../IRocketChatRecord'; -import { IUser } from '../IUser'; -import { ChannelName, RoomID } from '../IRoom'; -import { MessageAttachment } from './MessageAttachment/MessageAttachment'; -import { FileProp } from './MessageAttachment/Files/FileProp'; +import type { IRocketChatRecord } from '../IRocketChatRecord'; +import type { IUser } from '../IUser'; +import type { ChannelName, RoomID } from '../IRoom'; +import type { MessageAttachment } from './MessageAttachment/MessageAttachment'; +import type { FileProp } from './MessageAttachment/Files/FileProp'; type MentionType = 'user' | 'team'; diff --git a/definition/IRoom.ts b/definition/IRoom.ts index 8597a9035e82..04e94c1cb404 100644 --- a/definition/IRoom.ts +++ b/definition/IRoom.ts @@ -63,6 +63,7 @@ export interface IRoom extends IRocketChatRecord { muted?: string[]; usernames?: string[]; + ts?: Date; } export interface ICreatedRoom extends IRoom { diff --git a/definition/IUser.ts b/definition/IUser.ts index e40908cec83a..55d1b62b29b9 100644 --- a/definition/IUser.ts +++ b/definition/IUser.ts @@ -95,7 +95,7 @@ export interface IRole { name: string; protected: boolean; // scope?: string; - scope?: 'Users' | 'Subscriptions'; + scope: 'Users' | 'Subscriptions'; _id: string; } diff --git a/client/contexts/ServerContext/endpoints/apps.ts b/definition/rest/apps/index.ts similarity index 100% rename from client/contexts/ServerContext/endpoints/apps.ts rename to definition/rest/apps/index.ts diff --git a/definition/rest/helpers/PaginatedRequest.ts b/definition/rest/helpers/PaginatedRequest.ts new file mode 100644 index 000000000000..44962dea2062 --- /dev/null +++ b/definition/rest/helpers/PaginatedRequest.ts @@ -0,0 +1,4 @@ +export type PaginatedRequest = { + count: number; + offset: number; +}; diff --git a/definition/rest/helpers/PaginatedResult.ts b/definition/rest/helpers/PaginatedResult.ts new file mode 100644 index 000000000000..e78980c0d1e7 --- /dev/null +++ b/definition/rest/helpers/PaginatedResult.ts @@ -0,0 +1,5 @@ +export type PaginatedResult = { + count: number; + offset: number; + total: number; +}; diff --git a/definition/rest/index.ts b/definition/rest/index.ts new file mode 100644 index 000000000000..b7bad4b5ffda --- /dev/null +++ b/definition/rest/index.ts @@ -0,0 +1,97 @@ +import type { EnterpriseEndpoints } from '../../ee/definition/rest'; +import type { ExtractKeys, ValueOf } from '../utils'; +import type { AppsEndpoints } from './apps'; +import { BannersEndpoints } from './v1/banners'; +import type { ChannelsEndpoints } from './v1/channels'; +import type { ChatEndpoints } from './v1/chat'; +import type { CloudEndpoints } from './v1/cloud'; +import type { CustomUserStatusEndpoints } from './v1/customUserStatus'; +import type { DmEndpoints } from './v1/dm'; +import type { DnsEndpoints } from './v1/dns'; +import type { EmojiCustomEndpoints } from './v1/emojiCustom'; +import type { GroupsEndpoints } from './v1/groups'; +import type { ImEndpoints } from './v1/im'; +import { InstancesEndpoints } from './v1/instances'; +import type { LDAPEndpoints } from './v1/ldap'; +import type { LicensesEndpoints } from './v1/licenses'; +import type { MiscEndpoints } from './v1/misc'; +import type { OmnichannelEndpoints } from './v1/omnichannel'; +import { PermissionsEndpoints } from './v1/permissions'; +import { RolesEndpoints } from './v1/roles'; +import type { RoomsEndpoints } from './v1/rooms'; +import { SettingsEndpoints } from './v1/settings'; +import type { StatisticsEndpoints } from './v1/statistics'; +import type { TeamsEndpoints } from './v1/teams'; +import type { UsersEndpoints } from './v1/users'; + +type CommunityEndpoints = BannersEndpoints & ChatEndpoints & +ChannelsEndpoints & +CloudEndpoints & +CustomUserStatusEndpoints & +DmEndpoints & +DnsEndpoints & +EmojiCustomEndpoints & +GroupsEndpoints & +ImEndpoints & +LDAPEndpoints & +RoomsEndpoints & +RolesEndpoints & +TeamsEndpoints & +SettingsEndpoints & +UsersEndpoints & +AppsEndpoints & +OmnichannelEndpoints & +StatisticsEndpoints & +LicensesEndpoints & +MiscEndpoints & +PermissionsEndpoints & +InstancesEndpoints; + +export type Endpoints = CommunityEndpoints & EnterpriseEndpoints; + +type Endpoint = UnionizeEndpoints; + +type UnionizeEndpoints = ValueOf< +{ + [P in keyof EE]: UnionizeMethods; +} +>; + +type ExtractOperations = ExtractKeys any>; + +type UnionizeMethods = ValueOf< +{ + [M in keyof OO as ExtractOperations]: ( + method: M, + path: OO extends { path: string } ? OO['path'] : P, + ...params: Parameters any>> + ) => ReturnType any>>; +} +>; + +export type Method = Parameters[0]; +export type Path = Parameters[1]; + +export type MethodFor

= P extends any + ? Parameters any>>[0] + : never; +export type PathFor = M extends any + ? Parameters any>>[1] + : never; + +type Operation> = M extends any + ? P extends any + ? Extract any> + : never + : never; + +type ExtractParams = Q extends [any, any] + ? [undefined?] + : Q extends [any, any, any, ...any[]] + ? [Q[2]] + : never; + +export type Params> = ExtractParams< +Parameters> +>; +export type Return> = ReturnType>; diff --git a/definition/rest/v1/banners.ts b/definition/rest/v1/banners.ts new file mode 100644 index 000000000000..e448abb7b410 --- /dev/null +++ b/definition/rest/v1/banners.ts @@ -0,0 +1,26 @@ +import { IBanner } from '../../IBanner'; + +export type BannersEndpoints = { + /* @deprecated */ + 'banners.getNew': { + GET: () => ({ + banners: IBanner[]; + }); + }; + + 'banners/:id': { + GET: (params: { platform: string }) => ({ + banners: IBanner[]; + }); + }; + + 'banners': { + GET: () => ({ + banners: IBanner[]; + }); + }; + + 'banners.dismiss': { + POST: (params: { bannerId: string }) => void; + }; +}; diff --git a/client/contexts/ServerContext/endpoints/v1/channels.ts b/definition/rest/v1/channels.ts similarity index 70% rename from client/contexts/ServerContext/endpoints/v1/channels.ts rename to definition/rest/v1/channels.ts index 0e2abbf7751c..1a5bbca27ba3 100644 --- a/client/contexts/ServerContext/endpoints/v1/channels.ts +++ b/definition/rest/v1/channels.ts @@ -1,6 +1,6 @@ -import type { IMessage } from '../../../../../definition/IMessage/IMessage'; -import type { IRoom } from '../../../../../definition/IRoom'; -import type { IUser } from '../../../../../definition/IUser'; +import type { IMessage } from '../../IMessage/IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; export type ChannelsEndpoints = { 'channels.files': { diff --git a/client/contexts/ServerContext/endpoints/v1/chat.ts b/definition/rest/v1/chat.ts similarity index 84% rename from client/contexts/ServerContext/endpoints/v1/chat.ts rename to definition/rest/v1/chat.ts index 0ed5c1e02980..134f832b64ee 100644 --- a/client/contexts/ServerContext/endpoints/v1/chat.ts +++ b/definition/rest/v1/chat.ts @@ -1,5 +1,5 @@ -import type { IMessage } from '../../../../../definition/IMessage'; -import type { IRoom } from '../../../../../definition/IRoom'; +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; export type ChatEndpoints = { 'chat.getMessage': { diff --git a/client/contexts/ServerContext/endpoints/v1/cloud.ts b/definition/rest/v1/cloud.ts similarity index 100% rename from client/contexts/ServerContext/endpoints/v1/cloud.ts rename to definition/rest/v1/cloud.ts diff --git a/client/contexts/ServerContext/endpoints/v1/customUserStatus.ts b/definition/rest/v1/customUserStatus.ts similarity index 100% rename from client/contexts/ServerContext/endpoints/v1/customUserStatus.ts rename to definition/rest/v1/customUserStatus.ts diff --git a/definition/rest/v1/dm.ts b/definition/rest/v1/dm.ts new file mode 100644 index 000000000000..4ce71b05469b --- /dev/null +++ b/definition/rest/v1/dm.ts @@ -0,0 +1,21 @@ +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; + +export type DmEndpoints = { + 'dm.create': { + POST: ( + params: ( + | { + username: Exclude; + } + | { + usernames: string; + } + ) & { + excludeSelf?: boolean; + }, + ) => { + room: IRoom & { rid: IRoom['_id'] }; + }; + }; +}; diff --git a/client/contexts/ServerContext/endpoints/v1/dns.ts b/definition/rest/v1/dns.ts similarity index 62% rename from client/contexts/ServerContext/endpoints/v1/dns.ts rename to definition/rest/v1/dns.ts index 136e8d698a98..b2d553e036f2 100644 --- a/client/contexts/ServerContext/endpoints/v1/dns.ts +++ b/definition/rest/v1/dns.ts @@ -5,8 +5,9 @@ export type DnsEndpoints = { }; }; 'dns.resolve.txt': { - GET: (params: { url: string }) => { - resolved: Record; + POST: (params: { url: string }) => { + resolved: string; + // resolved: Record; }; }; }; diff --git a/client/contexts/ServerContext/endpoints/v1/emojiCustom.ts b/definition/rest/v1/emojiCustom.ts similarity index 73% rename from client/contexts/ServerContext/endpoints/v1/emojiCustom.ts rename to definition/rest/v1/emojiCustom.ts index 63648e6f3e26..0a49286ecfd5 100644 --- a/client/contexts/ServerContext/endpoints/v1/emojiCustom.ts +++ b/definition/rest/v1/emojiCustom.ts @@ -1,4 +1,4 @@ -import type { ICustomEmojiDescriptor } from '../../../../../definition/ICustomEmojiDescriptor'; +import type { ICustomEmojiDescriptor } from '../../ICustomEmojiDescriptor'; export type EmojiCustomEndpoints = { 'emoji-custom.list': { diff --git a/client/contexts/ServerContext/endpoints/v1/groups.ts b/definition/rest/v1/groups.ts similarity index 69% rename from client/contexts/ServerContext/endpoints/v1/groups.ts rename to definition/rest/v1/groups.ts index 5b01d443d451..3b5a584795ad 100644 --- a/client/contexts/ServerContext/endpoints/v1/groups.ts +++ b/definition/rest/v1/groups.ts @@ -1,6 +1,6 @@ -import type { IMessage } from '../../../../../definition/IMessage'; -import type { IRoom } from '../../../../../definition/IRoom'; -import type { IUser } from '../../../../../definition/IUser'; +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; export type GroupsEndpoints = { 'groups.files': { diff --git a/client/contexts/ServerContext/endpoints/v1/im.ts b/definition/rest/v1/im.ts similarity index 65% rename from client/contexts/ServerContext/endpoints/v1/im.ts rename to definition/rest/v1/im.ts index 700cdecfda44..b88a5b2be0c7 100644 --- a/client/contexts/ServerContext/endpoints/v1/im.ts +++ b/definition/rest/v1/im.ts @@ -1,17 +1,17 @@ -import type { IMessage } from '../../../../../definition/IMessage'; -import type { IRoom } from '../../../../../definition/IRoom'; -import type { IUser } from '../../../../../definition/IUser'; +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; export type ImEndpoints = { 'im.create': { POST: ( params: ( | { - username: Exclude; - } + username: Exclude; + } | { - usernames: string; - } + usernames: string; + } ) & { excludeSelf?: boolean; }, diff --git a/definition/rest/v1/instances.ts b/definition/rest/v1/instances.ts new file mode 100644 index 000000000000..c792fc9b5fd5 --- /dev/null +++ b/definition/rest/v1/instances.ts @@ -0,0 +1,16 @@ +import { IInstanceStatus } from '../../IInstanceStatus'; + +export type InstancesEndpoints = { + 'instances.get': { + GET: () => ({ + instances: (IInstanceStatus | { + connection: { + address: unknown; + currentStatus: unknown; + instanceRecord: unknown; + broadcastAuth: unknown; + }; + })[]; + }); + }; +}; diff --git a/client/contexts/ServerContext/endpoints/v1/ldap.ts b/definition/rest/v1/ldap.ts similarity index 64% rename from client/contexts/ServerContext/endpoints/v1/ldap.ts rename to definition/rest/v1/ldap.ts index 09b19d5637a3..7553d749eaf9 100644 --- a/client/contexts/ServerContext/endpoints/v1/ldap.ts +++ b/definition/rest/v1/ldap.ts @@ -1,4 +1,4 @@ -import type { TranslationKey } from '../../../TranslationContext'; +import type { TranslationKey } from '../../../client/contexts/TranslationContext'; export type LDAPEndpoints = { 'ldap.testConnection': { @@ -7,9 +7,9 @@ export type LDAPEndpoints = { }; }; 'ldap.testSearch': { - POST: (params: { username: string }) => { + POST: (params: { username: string }) => ({ message: TranslationKey; - }; + }); }; 'ldap.syncNow': { POST: () => { diff --git a/client/contexts/ServerContext/endpoints/v1/licenses.ts b/definition/rest/v1/licenses.ts similarity index 64% rename from client/contexts/ServerContext/endpoints/v1/licenses.ts rename to definition/rest/v1/licenses.ts index 5d78d69dd5ed..cc4b0dba981d 100644 --- a/client/contexts/ServerContext/endpoints/v1/licenses.ts +++ b/definition/rest/v1/licenses.ts @@ -1,9 +1,12 @@ -import type { ILicense } from '../../../../../ee/app/license/server/license'; +import type { ILicense } from '../../../ee/app/license/definitions/ILicense'; export type LicensesEndpoints = { 'licenses.get': { GET: () => { licenses: Array }; }; + 'licenses.add': { + POST: (params: { license: string }) => void; + }; 'licenses.maxActiveUsers': { GET: () => { maxActiveUsers: number | null; activeUsers: number }; }; diff --git a/client/contexts/ServerContext/endpoints/v1/misc.ts b/definition/rest/v1/misc.ts similarity index 100% rename from client/contexts/ServerContext/endpoints/v1/misc.ts rename to definition/rest/v1/misc.ts diff --git a/client/contexts/ServerContext/endpoints/v1/omnichannel.ts b/definition/rest/v1/omnichannel.ts similarity index 82% rename from client/contexts/ServerContext/endpoints/v1/omnichannel.ts rename to definition/rest/v1/omnichannel.ts index 4254db71253a..1598355cb5ba 100644 --- a/client/contexts/ServerContext/endpoints/v1/omnichannel.ts +++ b/definition/rest/v1/omnichannel.ts @@ -1,10 +1,10 @@ -import { ILivechatDepartment } from '../../../../../definition/ILivechatDepartment'; -import { ILivechatMonitor } from '../../../../../definition/ILivechatMonitor'; -import { ILivechatTag } from '../../../../../definition/ILivechatTag'; -import { IOmnichannelCannedResponse } from '../../../../../definition/IOmnichannelCannedResponse'; -import { IOmnichannelRoom, IRoom } from '../../../../../definition/IRoom'; -import { ISetting } from '../../../../../definition/ISetting'; -import { IUser } from '../../../../../definition/IUser'; +import { ILivechatDepartment } from '../../ILivechatDepartment'; +import { ILivechatMonitor } from '../../ILivechatMonitor'; +import { ILivechatTag } from '../../ILivechatTag'; +import { IOmnichannelCannedResponse } from '../../IOmnichannelCannedResponse'; +import { IOmnichannelRoom, IRoom } from '../../IRoom'; +import { ISetting } from '../../ISetting'; +import { IUser } from '../../IUser'; export type OmnichannelEndpoints = { 'livechat/appearance': { @@ -49,7 +49,7 @@ export type OmnichannelEndpoints = { }; }; 'livechat/department/:_id': { - path: `livechat/department/${string}`; + path: `livechat/department/${ string }`; GET: () => { department: ILivechatDepartment; }; @@ -138,7 +138,7 @@ export type OmnichannelEndpoints = { DELETE: (params: { _id: IOmnichannelCannedResponse['_id'] }) => void; }; 'canned-responses/:_id': { - path: `canned-responses/${string}`; + path: `canned-responses/${ string }`; GET: () => { cannedResponse: IOmnichannelCannedResponse; }; diff --git a/definition/rest/v1/permissions.ts b/definition/rest/v1/permissions.ts new file mode 100644 index 000000000000..b9ec5ca95864 --- /dev/null +++ b/definition/rest/v1/permissions.ts @@ -0,0 +1,43 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +import { IPermission } from '../../IPermission'; + +const ajv = new Ajv(); + +type PermissionsUpdateProps = { permissions: { _id: string; roles: string[] }[] }; + +const permissionUpdatePropsSchema: JSONSchemaType = { + type: 'object', + properties: { + permissions: { + type: 'array', + items: { + type: 'object', + properties: { + _id: { type: 'string' }, + roles: { type: 'array', items: { type: 'string' }, uniqueItems: true }, + }, + additionalProperties: false, + required: ['_id', 'roles'], + }, + }, + }, + required: ['permissions'], + additionalProperties: false, +}; + +export const isBodyParamsValidPermissionUpdate = ajv.compile(permissionUpdatePropsSchema); + +export type PermissionsEndpoints = { + 'permissions.listAll': { + GET: (params: { updatedSince?: string }) => ({ + update: IPermission[]; + remove: IPermission[]; + }); + }; + 'permissions.update': { + POST: (params: PermissionsUpdateProps) => ({ + permissions: IPermission[]; + }); + }; +}; diff --git a/definition/rest/v1/roles.ts b/definition/rest/v1/roles.ts new file mode 100644 index 000000000000..844d125083b3 --- /dev/null +++ b/definition/rest/v1/roles.ts @@ -0,0 +1,186 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +import { IRole, IUser } from '../../IUser'; + +const ajv = new Ajv(); + +type RoleCreateProps = Pick & Partial>; + +const roleCreatePropsSchema: JSONSchemaType = { + type: 'object', + properties: { + name: { + type: 'string', + }, + description: { + type: 'string', + nullable: true, + }, + scope: { + type: 'string', + enum: ['Users', 'Subscriptions'], + nullable: true, + }, + mandatory2fa: { + type: 'boolean', + nullable: true, + }, + }, + required: ['name'], + additionalProperties: false, +}; + +export const isRoleCreateProps = ajv.compile(roleCreatePropsSchema); + +type RoleUpdateProps = { roleId: IRole['_id']; name: IRole['name'] } & Partial; + +const roleUpdatePropsSchema: JSONSchemaType = { + type: 'object', + properties: { + roleId: { + type: 'string', + }, + name: { + type: 'string', + }, + description: { + type: 'string', + nullable: true, + }, + scope: { + type: 'string', + enum: ['Users', 'Subscriptions'], + nullable: true, + }, + mandatory2fa: { + type: 'boolean', + nullable: true, + }, + }, + required: ['roleId', 'name'], + additionalProperties: false, +}; + +export const isRoleUpdateProps = ajv.compile(roleUpdatePropsSchema); + +type RoleDeleteProps = { roleId: IRole['_id'] }; + +const roleDeletePropsSchema: JSONSchemaType = { + type: 'object', + properties: { + roleId: { + type: 'string', + }, + }, + required: ['roleId'], + additionalProperties: false, +}; + +export const isRoleDeleteProps = ajv.compile(roleDeletePropsSchema); + +type RoleAddUserToRoleProps = { + username: string; + roleName: string; + roomId?: string; +} + +const roleAddUserToRolePropsSchema: JSONSchemaType = { + type: 'object', + properties: { + username: { + type: 'string', + }, + roleName: { + type: 'string', + }, + roomId: { + type: 'string', + nullable: true, + }, + }, + required: ['username', 'roleName'], + additionalProperties: false, +}; + + +export const isRoleAddUserToRoleProps = ajv.compile(roleAddUserToRolePropsSchema); + +type RoleRemoveUserFromRoleProps = { + username: string; + roleName: string; + roomId?: string; +} + +const roleRemoveUserFromRolePropsSchema: JSONSchemaType = { + type: 'object', + properties: { + username: { + type: 'string', + }, + roleName: { + type: 'string', + }, + roomId: { + type: 'string', + nullable: true, + }, + }, + required: ['username', 'roleName'], + additionalProperties: false, +}; + +export const isRoleRemoveUserFromRoleProps = ajv.compile(roleRemoveUserFromRolePropsSchema); + +type RoleSyncProps = { + updatedSince?: string; +} + +export type RolesEndpoints = { + 'roles.list': { + GET: () => ({ + roles: IRole[]; + }); + }; + 'roles.sync': { + GET: (params: RoleSyncProps) => ({ + roles: { + update: IRole[]; + remove: IRole[]; + }; + }); + }; + 'roles.create': { + POST: (params: RoleCreateProps) => ({ + role: IRole; + }); + }; + + 'roles.addUserToRole': { + POST: (params: RoleAddUserToRoleProps) => ({ + role: IRole; + }); + }; + + 'roles.getUsersInRole': { + GET: (params: { roomId: string; role: string; offset: number; count: number }) => ({ + users: IUser[]; + total: number; + }); + }; + + 'roles.update': { + POST: (role: RoleUpdateProps) => ({ + role: IRole; + }); + }; + + 'roles.delete': { + POST: (prop: RoleDeleteProps) => void; + }; + + 'roles.removeUserFromRole': { + POST: (props: RoleRemoveUserFromRoleProps) => ({ + role: IRole; + }); + }; +}; diff --git a/client/contexts/ServerContext/endpoints/v1/rooms.ts b/definition/rest/v1/rooms.ts similarity index 81% rename from client/contexts/ServerContext/endpoints/v1/rooms.ts rename to definition/rest/v1/rooms.ts index 960610ec558a..92f0bc5895cb 100644 --- a/client/contexts/ServerContext/endpoints/v1/rooms.ts +++ b/definition/rest/v1/rooms.ts @@ -1,6 +1,6 @@ -import type { IMessage } from '../../../../../definition/IMessage'; -import type { IRoom } from '../../../../../definition/IRoom'; -import type { IUser } from '../../../../../definition/IUser'; +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; export type RoomsEndpoints = { 'rooms.autocomplete.channelAndPrivate': { diff --git a/definition/rest/v1/settings.ts b/definition/rest/v1/settings.ts new file mode 100644 index 000000000000..71ffab95bbf2 --- /dev/null +++ b/definition/rest/v1/settings.ts @@ -0,0 +1,100 @@ +import { ISetting, ISettingColor } from '../../ISetting'; +import { PaginatedResult } from '../helpers/PaginatedResult'; + +type SettingsUpdateProps = SettingsUpdatePropDefault | SettingsUpdatePropsActions | SettingsUpdatePropsColor; + +type SettingsUpdatePropsActions = { + execute: boolean; +} + +export type OauthCustomConfiguration = { + _id: string; + clientId?: string; + custom: unknown; + service?: string; + serverURL: unknown; + tokenPath: unknown; + identityPath: unknown; + authorizePath: unknown; + scope: unknown; + loginStyle: unknown; + tokenSentVia: unknown; + identityTokenSentVia: unknown; + keyField: unknown; + usernameField: unknown; + emailField: unknown; + nameField: unknown; + avatarField: unknown; + rolesClaim: unknown; + groupsClaim: unknown; + mapChannels: unknown; + channelsMap: unknown; + channelsAdmin: unknown; + mergeUsers: unknown; + mergeRoles: unknown; + accessTokenParam: unknown; + showButton: unknown; + + appId: unknown; + consumerKey?: string; + + clientConfig: unknown; + buttonLabelText: unknown; + buttonLabelColor: unknown; + buttonColor: unknown; +} + +export const isOauthCustomConfiguration = (config: any): config is OauthCustomConfiguration => Boolean(config); + +export const isSettingsUpdatePropsActions = (props: Partial): props is SettingsUpdatePropsActions => 'execute' in props; + +type SettingsUpdatePropsColor = { + editor: ISettingColor['editor']; + value: ISetting['value']; +} + +export const isSettingsUpdatePropsColor = (props: Partial): props is SettingsUpdatePropsColor => 'editor' in props && 'value' in props; + +type SettingsUpdatePropDefault = { + value: ISetting['value']; +} + +export const isSettingsUpdatePropDefault = (props: Partial): props is SettingsUpdatePropDefault => 'value' in props; + +export type SettingsEndpoints = { + 'settings.public': { + GET: () => PaginatedResult & { + settings: Array; + }; + }; + + 'settings.oauth': { + GET: () => ({ + services: Partial[]; + }); + }; + + 'settings.addCustomOAuth': { + POST: (params: { name: string }) => void; + }; + + 'settings': { + GET: () => ({ + settings: ISetting[]; + }); + }; + + 'settings/:_id': { + GET: () => Pick; + POST: (params: SettingsUpdateProps) => void; + }; + + 'service.configurations': { + GET: () => { + configurations: Array<{ + appId: string; + secret: string; + }>; + }; + }; +}; diff --git a/client/contexts/ServerContext/endpoints/v1/statistics.ts b/definition/rest/v1/statistics.ts similarity index 63% rename from client/contexts/ServerContext/endpoints/v1/statistics.ts rename to definition/rest/v1/statistics.ts index 178d8f5d66b8..5820d2be290b 100644 --- a/client/contexts/ServerContext/endpoints/v1/statistics.ts +++ b/definition/rest/v1/statistics.ts @@ -1,4 +1,4 @@ -import type { IStats } from '../../../../../definition/IStats'; +import type { IStats } from '../../IStats'; export type StatisticsEndpoints = { statistics: { diff --git a/definition/rest/v1/teams/TeamsAddMembersProps.test.ts b/definition/rest/v1/teams/TeamsAddMembersProps.test.ts new file mode 100644 index 000000000000..f2d7ec71bc25 --- /dev/null +++ b/definition/rest/v1/teams/TeamsAddMembersProps.test.ts @@ -0,0 +1,71 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsAddMembersProps } from './TeamsAddMembersProps'; + +describe('TeamsAddMemberProps (definition/rest/v1)', () => { + describe('isTeamsAddMembersProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsAddMembersProps); + }); + it('should return false if the parameter is empty', () => { + chai.assert.isFalse(isTeamsAddMembersProps({})); + }); + + it('should return false if teamId is provided but no member was provided', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ teamId: '123' })); + }); + + it('should return false if teamName is provided but no member was provided', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ teamName: '123' })); + }); + + it('should return false if members is provided but no teamId or teamName were provided', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ members: [{ userId: '123' }] })); + }); + + it('should return false if teamName was provided but members are empty', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ teamName: '123', members: [] })); + }); + + it('should return false if teamId was provided but members are empty', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ teamId: '123', members: [] })); + }); + + it('should return false if members with role is provided but no teamId or teamName were provided', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }] })); + }); + + it('should return true if members is provided and teamId is provided', () => { + chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123' }], teamId: '123' })); + }); + + it('should return true if members is provided and teamName is provided', () => { + chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123' }], teamName: '123' })); + }); + + it('should return true if members with role is provided and teamId is provided', () => { + chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }], teamId: '123' })); + }); + + it('should return true if members with role is provided and teamName is provided', () => { + chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }], teamName: '123' })); + }); + + it('should return false if teamName was provided and members contains an invalid property', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ teamName: '123', members: [{ userId: '123', roles: ['123'], invalid: true }] })); + }); + + it('should return false if teamId was provided and members contains an invalid property', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ teamId: '123', members: [{ userId: '123', roles: ['123'], invalid: true }] })); + }); + + it('should return false if teamName informed but contains an invalid property', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ member: [{ userId: '123', roles: ['123'] }], teamName: '123', invalid: true })); + }); + + it('should return false if teamId informed but contains an invalid property', () => { + chai.assert.isFalse(isTeamsAddMembersProps({ member: [{ userId: '123', roles: ['123'] }], teamId: '123', invalid: true })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsAddMembersProps.ts b/definition/rest/v1/teams/TeamsAddMembersProps.ts new file mode 100644 index 000000000000..599989fbf7af --- /dev/null +++ b/definition/rest/v1/teams/TeamsAddMembersProps.ts @@ -0,0 +1,80 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +import { ITeamMemberParams } from '../../../../server/sdk/types/ITeamService'; + +const ajv = new Ajv(); + +export type TeamsAddMembersProps = ({ teamId: string } | { teamName: string }) & { members: ITeamMemberParams[] }; + +const teamsAddMembersPropsSchema: JSONSchemaType = { + oneOf: [ + { + type: 'object', + properties: { + teamId: { + type: 'string', + }, + members: { + type: 'array', + items: { + + type: 'object', + properties: { + userId: { + type: 'string', + }, + roles: { + type: 'array', + items: { + type: 'string', + }, + nullable: true, + }, + }, + required: ['userId'], + additionalProperties: false, + }, + minItems: 1, + uniqueItems: true, + }, + }, + required: ['teamId', 'members'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + teamName: { + type: 'string', + }, + members: { + type: 'array', + items: { + + type: 'object', + properties: { + userId: { + type: 'string', + }, + roles: { + type: 'array', + items: { + type: 'string', + }, + nullable: true, + }, + }, + required: ['userId'], + additionalProperties: false, + }, + minItems: 1, + uniqueItems: true, + }, + }, + required: ['teamName', 'members'], + additionalProperties: false, + }, + ], +}; + +export const isTeamsAddMembersProps = ajv.compile(teamsAddMembersPropsSchema); diff --git a/definition/rest/v1/teams/TeamsConvertToChannelProps.test.ts b/definition/rest/v1/teams/TeamsConvertToChannelProps.test.ts new file mode 100644 index 000000000000..5c292c6f1b30 --- /dev/null +++ b/definition/rest/v1/teams/TeamsConvertToChannelProps.test.ts @@ -0,0 +1,39 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsConvertToChannelProps } from './TeamsConvertToChannelProps'; + +describe('TeamsConvertToChannelProps (definition/rest/v1)', () => { + describe('isTeamsConvertToChannelProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsConvertToChannelProps); + }); + it('should return false if neither teamName or teamId is provided', () => { + chai.assert.isFalse(isTeamsConvertToChannelProps({})); + }); + + it('should return true if teamName is provided', () => { + chai.assert.isTrue(isTeamsConvertToChannelProps({ teamName: 'teamName' })); + }); + + it('should return true if teamId is provided', () => { + chai.assert.isTrue(isTeamsConvertToChannelProps({ teamId: 'teamId' })); + }); + + it('should return false if both teamName and teamId are provided', () => { + chai.assert.isFalse(isTeamsConvertToChannelProps({ teamName: 'teamName', teamId: 'teamId' })); + }); + + it('should return false if teamName is not a string', () => { + chai.assert.isFalse(isTeamsConvertToChannelProps({ teamName: 1 })); + }); + + it('should return false if teamId is not a string', () => { + chai.assert.isFalse(isTeamsConvertToChannelProps({ teamId: 1 })); + }); + + it('should return false if an additionalProperties is provided', () => { + chai.assert.isFalse(isTeamsConvertToChannelProps({ teamName: 'teamName', additionalProperties: 'additionalProperties' })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsConvertToChannelProps.ts b/definition/rest/v1/teams/TeamsConvertToChannelProps.ts new file mode 100644 index 000000000000..af93a5702093 --- /dev/null +++ b/definition/rest/v1/teams/TeamsConvertToChannelProps.ts @@ -0,0 +1,54 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + + +const ajv = new Ajv(); + +export type TeamsConvertToChannelProps = { + roomsToRemove?: string[]; +} & ({ teamId: string } | { teamName: string }); + +const teamsConvertToTeamsPropsSchema: JSONSchemaType = { + oneOf: [ + { + type: 'object', + + properties: { + roomsToRemove: { + type: 'array', + items: { + type: 'string', + }, + nullable: true, + }, + teamId: { + type: 'string', + }, + }, + required: [ + 'teamId', + ], + additionalProperties: false, + }, + { + type: 'object', + properties: { + roomsToRemove: { + type: 'array', + items: { + type: 'string', + }, + nullable: true, + }, + teamName: { + type: 'string', + }, + }, + required: [ + 'teamName', + ], + additionalProperties: false, + }, + ], +}; + +export const isTeamsConvertToChannelProps = ajv.compile(teamsConvertToTeamsPropsSchema); diff --git a/definition/rest/v1/teams/TeamsDeleteProps.test.ts b/definition/rest/v1/teams/TeamsDeleteProps.test.ts new file mode 100644 index 000000000000..9efc17cd1dec --- /dev/null +++ b/definition/rest/v1/teams/TeamsDeleteProps.test.ts @@ -0,0 +1,64 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsDeleteProps } from './TeamsDeleteProps'; + +describe('TeamsDeleteProps (definition/rest/v1)', () => { + describe('isTeamsDeleteProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsDeleteProps); + }); + + it('should return false if neither teamName or teamId is provided', () => { + chai.assert.isFalse(isTeamsDeleteProps({})); + }); + + it('should return true if teamId is provided', () => { + chai.assert.isTrue(isTeamsDeleteProps({ teamId: 'teamId' })); + }); + + it('should return true if teamName is provided', () => { + chai.assert.isTrue(isTeamsDeleteProps({ teamName: 'teamName' })); + }); + + it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is empty', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: [] })); + }); + + it('should return false if teamName and roomsToRemove are provided, but roomsToRemove is empty', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: [] })); + }); + + it('should return true if teamId and roomsToRemove are provided', () => { + chai.assert.isTrue(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: ['roomId'] })); + }); + + it('should return true if teamName and roomsToRemove are provided', () => { + chai.assert.isTrue(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: ['roomId'] })); + }); + + it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is not an array', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: {} })); + }); + + it('should return false if teamName and roomsToRemove are provided, but roomsToRemove is not an array', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: {} })); + }); + + it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is not an array of strings', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: [1] })); + }); + + it('should return false if teamName and roomsToRemove are provided, but roomsToRemove is not an array of strings', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: [1] })); + }); + + it('should return false if teamName and rooms are provided but an extra property is provided', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: ['roomsToRemove'], extra: 'extra' })); + }); + + it('should return false if teamId and rooms are provided but an extra property is provided', () => { + chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: ['roomsToRemove'], extra: 'extra' })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsDeleteProps.ts b/definition/rest/v1/teams/TeamsDeleteProps.ts new file mode 100644 index 000000000000..22582488d0ac --- /dev/null +++ b/definition/rest/v1/teams/TeamsDeleteProps.ts @@ -0,0 +1,50 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +const ajv = new Ajv(); + +export type TeamsDeleteProps = ({ teamId: string } | { teamName: string }) & { roomsToRemove?: string[] }; + +const teamsDeletePropsSchema: JSONSchemaType = { + oneOf: [ + { + type: 'object', + properties: { + teamId: { + type: 'string', + }, + roomsToRemove: { + type: 'array', + items: { + type: 'string', + }, + minItems: 1, + uniqueItems: true, + nullable: true, + }, + }, + required: ['teamId'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + teamName: { + type: 'string', + }, + roomsToRemove: { + type: 'array', + items: { + type: 'string', + }, + minItems: 1, + uniqueItems: true, + nullable: true, + }, + }, + required: ['teamName'], + additionalProperties: false, + }, + ], +}; + +export const isTeamsDeleteProps = ajv.compile(teamsDeletePropsSchema); diff --git a/definition/rest/v1/teams/TeamsLeaveProps.test.ts b/definition/rest/v1/teams/TeamsLeaveProps.test.ts new file mode 100644 index 000000000000..1b6255d97970 --- /dev/null +++ b/definition/rest/v1/teams/TeamsLeaveProps.test.ts @@ -0,0 +1,64 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsLeaveProps } from './TeamsLeaveProps'; + +describe('TeamsLeaveProps (definition/rest/v1)', () => { + describe('isTeamsLeaveProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsLeaveProps); + }); + + it('should return false if neither teamName or teamId is provided', () => { + chai.assert.isFalse(isTeamsLeaveProps({})); + }); + + it('should return true if teamId is provided', () => { + chai.assert.isTrue(isTeamsLeaveProps({ teamId: 'teamId' })); + }); + + it('should return true if teamName is provided', () => { + chai.assert.isTrue(isTeamsLeaveProps({ teamName: 'teamName' })); + }); + + it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is empty', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: [] })); + }); + + it('should return false if teamName and rooms are provided, but rooms is empty', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: [] })); + }); + + it('should return true if teamId and rooms are provided', () => { + chai.assert.isTrue(isTeamsLeaveProps({ teamId: 'teamId', rooms: ['roomId'] })); + }); + + it('should return true if teamName and rooms are provided', () => { + chai.assert.isTrue(isTeamsLeaveProps({ teamName: 'teamName', rooms: ['roomId'] })); + }); + + it('should return false if teamId and rooms are provided, but rooms is not an array', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: {} })); + }); + + it('should return false if teamName and rooms are provided, but rooms is not an array', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: {} })); + }); + + it('should return false if teamId and rooms are provided, but rooms is not an array of strings', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: [1] })); + }); + + it('should return false if teamName and rooms are provided, but rooms is not an array of strings', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: [1] })); + }); + + it('should return false if teamName and rooms are provided but an extra property is provided', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: ['rooms'], extra: 'extra' })); + }); + + it('should return false if teamId and rooms are provided but an extra property is provided', () => { + chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: ['rooms'], extra: 'extra' })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsLeaveProps.ts b/definition/rest/v1/teams/TeamsLeaveProps.ts new file mode 100644 index 000000000000..ac526886237b --- /dev/null +++ b/definition/rest/v1/teams/TeamsLeaveProps.ts @@ -0,0 +1,51 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + + +const ajv = new Ajv(); + +export type TeamsLeaveProps = ({ teamId: string } | { teamName: string }) & { rooms?: string[] }; + +const teamsLeavePropsSchema: JSONSchemaType = { + oneOf: [ + { + type: 'object', + properties: { + teamId: { + type: 'string', + }, + rooms: { + type: 'array', + items: { + type: 'string', + }, + minItems: 1, + uniqueItems: true, + nullable: true, + }, + }, + required: ['teamId'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + teamName: { + type: 'string', + }, + rooms: { + type: 'array', + items: { + type: 'string', + }, + minItems: 1, + uniqueItems: true, + nullable: true, + }, + }, + required: ['teamName'], + additionalProperties: false, + }, + ], +}; + +export const isTeamsLeaveProps = ajv.compile(teamsLeavePropsSchema); diff --git a/definition/rest/v1/teams/TeamsRemoveMemberProps.test.ts b/definition/rest/v1/teams/TeamsRemoveMemberProps.test.ts new file mode 100644 index 000000000000..f66c765e0348 --- /dev/null +++ b/definition/rest/v1/teams/TeamsRemoveMemberProps.test.ts @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsRemoveMemberProps } from './TeamsRemoveMemberProps'; + +describe('Teams (definition/rest/v1)', () => { + describe('isTeamsRemoveMemberProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsRemoveMemberProps); + }); + it('should return false if parameter is empty', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({})); + }); + it('should return false if teamId is is informed but missing userId', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId' })); + }); + it('should return false if teamName is is informed but missing userId', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName' })); + }); + + it('should return true if teamId and userId are informed', () => { + chai.assert.isTrue(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId' })); + }); + it('should return true if teamName and userId are informed', () => { + chai.assert.isTrue(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId' })); + }); + + + it('should return false if teamName and userId are informed but rooms are empty', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: [] })); + }); + + it('should return false if teamId and userId are informed and rooms are empty', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [] })); + }); + + it('should return false if teamId and userId are informed but rooms are empty', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [] })); + }); + + it('should return true if teamId and userId are informed and rooms are informed', () => { + chai.assert.isTrue(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: ['room'] })); + }); + + it('should return false if teamId and userId are informed and rooms are informed but rooms is not an array of strings', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [123] })); + }); + + it('should return false if teamName and userId are informed and rooms are informed but there is an extra property', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: ['room'], extra: 'extra' })); + }); + + it('should return false if teamId and userId are informed and rooms are informed but there is an extra property', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: ['room'], extra: 'extra' })); + }); + + it('should return false if teamName and userId are informed and rooms are informed but there is an extra property', () => { + chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: ['room'], extra: 'extra' })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsRemoveMemberProps.ts b/definition/rest/v1/teams/TeamsRemoveMemberProps.ts new file mode 100644 index 000000000000..7518fd351c8e --- /dev/null +++ b/definition/rest/v1/teams/TeamsRemoveMemberProps.ts @@ -0,0 +1,56 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +const ajv = new Ajv(); + +export type TeamsRemoveMemberProps = ({ teamId: string } | { teamName: string }) & { userId: string; rooms?: Array }; + +const teamsRemoveMemberPropsSchema: JSONSchemaType = { + oneOf: [ + { + type: 'object', + properties: { + teamId: { + type: 'string', + }, + userId: { + type: 'string', + }, + rooms: { + type: 'array', + items: { + type: 'string', + }, + minItems: 1, + uniqueItems: true, + nullable: true, + }, + }, + required: ['teamId', 'userId'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + teamName: { + type: 'string', + }, + userId: { + type: 'string', + }, + rooms: { + type: 'array', + items: { + type: 'string', + }, + minItems: 1, + uniqueItems: true, + nullable: true, + }, + }, + required: ['teamName', 'userId'], + additionalProperties: false, + }, + ], +}; + +export const isTeamsRemoveMemberProps = ajv.compile(teamsRemoveMemberPropsSchema); diff --git a/definition/rest/v1/teams/TeamsRemoveRoomProps.test.ts b/definition/rest/v1/teams/TeamsRemoveRoomProps.test.ts new file mode 100644 index 000000000000..226185dcc955 --- /dev/null +++ b/definition/rest/v1/teams/TeamsRemoveRoomProps.test.ts @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsRemoveRoomProps } from './TeamsRemoveRoomProps'; + +describe('TeamsRemoveRoomProps (definition/rest/v1)', () => { + describe('isTeamsRemoveRoomProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsRemoveRoomProps); + }); + it('should return false if roomId is not provided', () => { + chai.assert.isFalse(isTeamsRemoveRoomProps({})); + }); + it('should return false if roomId is provided but no teamId or teamName were provided', () => { + chai.assert.isFalse(isTeamsRemoveRoomProps({ roomId: 'roomId' })); + }); + it('should return false if roomId is provided and teamId is provided', () => { + chai.assert.isTrue(isTeamsRemoveRoomProps({ roomId: 'roomId', teamId: 'teamId' })); + }); + it('should return true if roomId is provided and teamName is provided', () => { + chai.assert.isTrue(isTeamsRemoveRoomProps({ roomId: 'roomId', teamName: 'teamName' })); + }); + it('should return false if roomId and teamName are provided but an additional property is provided', () => { + chai.assert.isFalse(isTeamsRemoveRoomProps({ roomId: 'roomId', teamName: 'teamName', foo: 'bar' })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsRemoveRoomProps.ts b/definition/rest/v1/teams/TeamsRemoveRoomProps.ts new file mode 100644 index 000000000000..a41db5538898 --- /dev/null +++ b/definition/rest/v1/teams/TeamsRemoveRoomProps.ts @@ -0,0 +1,40 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +import type { IRoom } from '../../../IRoom'; + +const ajv = new Ajv(); + +export type TeamsRemoveRoomProps = ({ teamId: string } | { teamName: string }) & { roomId: IRoom['_id'] }; + +export const teamsRemoveRoomPropsSchema: JSONSchemaType = { + oneOf: [ + { + type: 'object', + properties: { + teamId: { + type: 'string', + }, + roomId: { + type: 'string', + }, + }, + required: ['teamId', 'roomId'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + teamName: { + type: 'string', + }, + roomId: { + type: 'string', + }, + }, + required: ['teamName', 'roomId'], + additionalProperties: false, + }, + ], +}; + +export const isTeamsRemoveRoomProps = ajv.compile(teamsRemoveRoomPropsSchema); diff --git a/definition/rest/v1/teams/TeamsUpdateMemberProps.test.ts b/definition/rest/v1/teams/TeamsUpdateMemberProps.test.ts new file mode 100644 index 000000000000..ed6a329772a4 --- /dev/null +++ b/definition/rest/v1/teams/TeamsUpdateMemberProps.test.ts @@ -0,0 +1,59 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsUpdateMemberProps } from './TeamsUpdateMemberProps'; + +describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { + describe('isTeamsUpdateMemberProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsUpdateMemberProps); + }); + it('should return false if the parameter is empty', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({})); + }); + + it('should return false if teamId is provided but no member was provided', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({ teamId: '123' })); + }); + + it('should return false if teamName is provided but no member was provided', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({ teamName: '123' })); + }); + + it('should return false if member is provided but no teamId or teamName were provided', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123' } })); + }); + + it('should return false if member with role is provided but no teamId or teamName were provided', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] } })); + }); + + it('should return true if member is provided and teamId is provided', () => { + chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123' }, teamId: '123' })); + }); + + it('should return true if member is provided and teamName is provided', () => { + chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123' }, teamName: '123' })); + }); + + it('should return true if member with role is provided and teamId is provided', () => { + chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamId: '123' })); + }); + + it('should return true if member with role is provided and teamName is provided', () => { + chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamName: '123' })); + }); + + it('should return false if teamName was provided and member contains an invalid property', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', invalid: '123' }, teamName: '123' })); + }); + + it('should return false if teamId was provided and member contains an invalid property', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', invalid: '123' }, teamId: '123' })); + }); + + it('should return false if contains an invalid property', () => { + chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamName: '123', invalid: true })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsUpdateMemberProps.ts b/definition/rest/v1/teams/TeamsUpdateMemberProps.ts new file mode 100644 index 000000000000..5a65fc6238ff --- /dev/null +++ b/definition/rest/v1/teams/TeamsUpdateMemberProps.ts @@ -0,0 +1,68 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +import { ITeamMemberParams } from '../../../../server/sdk/types/ITeamService'; + +const ajv = new Ajv(); + +export type TeamsUpdateMemberProps = ({ teamId: string } | { teamName: string }) & { member: ITeamMemberParams }; + +const teamsUpdateMemberPropsSchema: JSONSchemaType = { + oneOf: [ + { + type: 'object', + properties: { + teamId: { + type: 'string', + }, + member: { + type: 'object', + properties: { + userId: { + type: 'string', + }, + roles: { + type: 'array', + items: { + type: 'string', + }, + nullable: true, + }, + }, + required: ['userId'], + additionalProperties: false, + }, + }, + required: ['teamId', 'member'], + additionalProperties: false, + }, + { + type: 'object', + properties: { + teamName: { + type: 'string', + }, + member: { + type: 'object', + properties: { + userId: { + type: 'string', + }, + roles: { + type: 'array', + items: { + type: 'string', + }, + nullable: true, + }, + }, + required: ['userId'], + additionalProperties: false, + }, + }, + required: ['teamName', 'member'], + additionalProperties: false, + }, + ], +}; + +export const isTeamsUpdateMemberProps = ajv.compile(teamsUpdateMemberPropsSchema); diff --git a/definition/rest/v1/teams/TeamsUpdateProps.test.ts b/definition/rest/v1/teams/TeamsUpdateProps.test.ts new file mode 100644 index 000000000000..ce09985901d1 --- /dev/null +++ b/definition/rest/v1/teams/TeamsUpdateProps.test.ts @@ -0,0 +1,161 @@ +/* eslint-env mocha */ +import chai from 'chai'; + +import { isTeamsUpdateProps } from './TeamsUpdateProps'; + +describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { + describe('isTeamsUpdateProps', () => { + it('should be a function', () => { + chai.assert.isFunction(isTeamsUpdateProps); + }); + it('should return false when provided anything that is not an TeamsUpdateProps', () => { + chai.assert.isFalse(isTeamsUpdateProps(undefined)); + chai.assert.isFalse(isTeamsUpdateProps(null)); + chai.assert.isFalse(isTeamsUpdateProps('')); + chai.assert.isFalse(isTeamsUpdateProps(123)); + chai.assert.isFalse(isTeamsUpdateProps({})); + chai.assert.isFalse(isTeamsUpdateProps([])); + chai.assert.isFalse(isTeamsUpdateProps(new Date())); + chai.assert.isFalse(isTeamsUpdateProps(new Error())); + }); + it('should return false when only teamName is provided to TeamsUpdateProps', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamName: 'teamName', + })); + }); + + it('should return false when only teamId is provided to TeamsUpdateProps', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamId: 'teamId', + })); + }); + + it('should return false when teamName and data are provided to TeamsUpdateProps but data is an empty object', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamName: 'teamName', + data: {}, + })); + }); + + it('should return false when teamId and data are provided to TeamsUpdateProps but data is an empty object', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamId: 'teamId', + data: {}, + })); + }); + + it('should return false when teamName and data are provided to TeamsUpdateProps but data is not an object', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamName: 'teamName', + data: 'data', + })); + }); + + it('should return false when teamId and data are provided to TeamsUpdateProps but data is not an object', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamId: 'teamId', + data: 'data', + })); + }); + + it('should return true when teamName and data.name are provided to TeamsUpdateProps', () => { + chai.assert.isTrue(isTeamsUpdateProps({ + teamName: 'teamName', + data: { + name: 'name', + }, + })); + }); + + it('should return true when teamId and data.name are provided to TeamsUpdateProps', () => { + chai.assert.isTrue(isTeamsUpdateProps({ + teamId: 'teamId', + data: { + name: 'name', + }, + })); + }); + + it('should return true when teamName and data.type are provided to TeamsUpdateProps', () => { + chai.assert.isTrue(isTeamsUpdateProps({ + teamName: 'teamName', + data: { + type: 0, + }, + })); + }); + + it('should return true when teamId and data.type are provided to TeamsUpdateProps', () => { + chai.assert.isTrue(isTeamsUpdateProps({ + teamId: 'teamId', + data: { + type: 0, + }, + })); + }); + + it('should return true when teamName and data.name and data.type are provided to TeamsUpdateProps', () => { + chai.assert.isTrue(isTeamsUpdateProps({ + teamName: 'teamName', + data: { + name: 'name', + type: 0, + }, + })); + }); + + it('should return true when teamId and data.name and data.type are provided to TeamsUpdateProps', () => { + chai.assert.isTrue(isTeamsUpdateProps({ + teamId: 'teamId', + data: { + name: 'name', + type: 0, + }, + })); + }); + + it('should return false when teamName, data.name, data.type are some more extra data are provided to TeamsUpdateProps', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamName: 'teamName', + data: { + name: 'name', + type: 0, + extra: 'extra', + }, + })); + }); + + it('should return false when teamId, data.name, data.type are some more extra data are provided to TeamsUpdateProps', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamId: 'teamId', + data: { + name: 'name', + type: 0, + extra: 'extra', + }, + })); + }); + + it('should return false when teamName, data.name, data.type are some more extra parameter are provided to TeamsUpdateProps', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamName: 'teamName', + extra: 'extra', + data: { + name: 'name', + type: 0, + }, + })); + }); + + it('should return false when teamId, data.name, data.type are some more extra parameter are provided to TeamsUpdateProps', () => { + chai.assert.isFalse(isTeamsUpdateProps({ + teamId: 'teamId', + extra: 'extra', + data: { + name: 'name', + type: 0, + }, + })); + }); + }); +}); diff --git a/definition/rest/v1/teams/TeamsUpdateProps.ts b/definition/rest/v1/teams/TeamsUpdateProps.ts new file mode 100644 index 000000000000..b019efdb1549 --- /dev/null +++ b/definition/rest/v1/teams/TeamsUpdateProps.ts @@ -0,0 +1,75 @@ +import Ajv, { JSONSchemaType } from 'ajv'; + +import { TEAM_TYPE } from '../../../ITeam'; + +const ajv = new Ajv(); + +export type TeamsUpdateProps = ({ teamId: string } | { teamName: string }) & { + data: ({ + name: string; + type?: TEAM_TYPE; + } | { + name?: string; + type: TEAM_TYPE; + }); +}; + +const teamsUpdatePropsSchema: JSONSchemaType = { + type: 'object', + properties: { + updateRoom: { + type: 'boolean', + nullable: true, + }, + teamId: { + type: 'string', + nullable: true, + }, + teamName: { + type: 'string', + nullable: true, + }, + data: { + type: 'object', + properties: { + name: { + type: 'string', + nullable: true, + }, + type: { + type: 'number', + enum: [ + TEAM_TYPE.PUBLIC, + TEAM_TYPE.PRIVATE, + ], + }, + }, + additionalProperties: false, + required: [], + anyOf: [ + { + required: ['name'], + }, + { + required: ['type'], + }, + ], + }, + name: { + type: 'string', + nullable: true, + }, + }, + required: [], + oneOf: [ + { + required: ['teamId', 'data'], + }, + { + required: ['teamName', 'data'], + }, + ], + additionalProperties: false, +}; + +export const isTeamsUpdateProps = ajv.compile(teamsUpdatePropsSchema); diff --git a/definition/rest/v1/teams/index.ts b/definition/rest/v1/teams/index.ts new file mode 100644 index 000000000000..72694da7ea0c --- /dev/null +++ b/definition/rest/v1/teams/index.ts @@ -0,0 +1,148 @@ + + +import type { IRoom } from '../../../IRoom'; +import type { ITeam } from '../../../ITeam'; +import type { IUser } from '../../../IUser'; +import { PaginatedResult } from '../../helpers/PaginatedResult'; +import { PaginatedRequest } from '../../helpers/PaginatedRequest'; +import { ITeamAutocompleteResult, ITeamMemberInfo } from '../../../../server/sdk/types/ITeamService'; +import { TeamsRemoveRoomProps } from './TeamsRemoveRoomProps'; +import { TeamsConvertToChannelProps } from './TeamsConvertToChannelProps'; +import { TeamsUpdateMemberProps } from './TeamsUpdateMemberProps'; +import { TeamsAddMembersProps } from './TeamsAddMembersProps'; +import { TeamsRemoveMemberProps } from './TeamsRemoveMemberProps'; +import { TeamsDeleteProps } from './TeamsDeleteProps'; +import { TeamsLeaveProps } from './TeamsLeaveProps'; +import { TeamsUpdateProps } from './TeamsUpdateProps'; + + +type TeamProps = + | TeamsRemoveRoomProps + | TeamsConvertToChannelProps + | TeamsUpdateMemberProps + | TeamsAddMembersProps + | TeamsRemoveMemberProps + | TeamsDeleteProps + | TeamsLeaveProps + | TeamsUpdateProps; + +export const isTeamPropsWithTeamName = (props: T): props is T & { teamName: string } => 'teamName' in props; + +export const isTeamPropsWithTeamId = (props: T): props is T & { teamId: string } => 'teamId' in props; + +export type TeamsEndpoints = { + 'teams.list': { + GET: () => PaginatedResult & { teams: ITeam[] }; + }; + 'teams.listAll': { + GET: () => { teams: ITeam[] } & PaginatedResult; + }; + 'teams.create': { + POST: (params: { + name: ITeam['name']; + type?: ITeam['type']; + members?: IUser['_id'][]; + room: { + id?: string; + name?: IRoom['name']; + members?: IUser['_id'][]; + readOnly?: boolean; + extraData?: { + teamId?: string; + teamMain?: boolean; + } & { [key: string]: string | boolean }; + options?: { + nameValidationRegex?: string; + creator: string; + subscriptionExtra?: { + open: boolean; + ls: Date; + prid: IRoom['_id']; + }; + } & { + [key: string]: + | string + | { + open: boolean; + ls: Date; + prid: IRoom['_id']; + }; + }; + }; + owner?: IUser['_id']; + }) => { + team: ITeam; + }; + }; + + 'teams.convertToChannel': { + POST: (params: TeamsConvertToChannelProps) => void; + }; + + 'teams.addRooms': { + POST: (params: { rooms: IRoom['_id'][]; teamId: string } | { rooms: IRoom['_id'][]; teamName: string }) => ({ rooms: IRoom[] }); + }; + + 'teams.removeRoom': { + POST: (params: TeamsRemoveRoomProps) => ({ room: IRoom }); + }; + + 'teams.members': { + GET: (params: ({ teamId: string } | { teamName: string }) & { status?: string[]; username?: string; name?: string }) => (PaginatedResult & { members: ITeamMemberInfo[] }); + }; + + 'teams.addMembers': { + POST: (params: TeamsAddMembersProps) => void; + }; + + 'teams.updateMember': { + POST: (params: TeamsUpdateMemberProps) => void; + }; + + 'teams.removeMember': { + POST: (params: TeamsRemoveMemberProps) => void; + }; + + 'teams.leave': { + POST: (params: TeamsLeaveProps) => void; + }; + + + 'teams.info': { + GET: (params: ({ teamId: string } | { teamName: string }) & {}) => ({ teamInfo: Partial }); + }; + + 'teams.autocomplete': { + GET: (params: { name: string }) => ({ teams: ITeamAutocompleteResult[] }); + }; + + 'teams.update': { + POST: (params: TeamsUpdateProps) => void; + }; + + 'teams.delete': { + POST: (params: TeamsDeleteProps) => void; + }; + + 'teams.listRoomsOfUser': { + GET: (params: { + teamId: ITeam['_id']; + userId: IUser['_id']; + canUserDelete?: boolean; + } | { + teamName: ITeam['name']; + userId: IUser['_id']; + canUserDelete?: boolean; + } + ) => PaginatedResult & { rooms: IRoom[] }; + }; + + 'teams.listRooms': { + GET: (params: PaginatedRequest & ({ teamId: string } | { teamId: string }) & { filter?: string; type?: string }) => PaginatedResult & { rooms: IRoom[] }; + }; + + + 'teams.updateRoom': { + POST: (params: { roomId: IRoom['_id']; isDefault: boolean }) => ({ room: IRoom }); + }; +}; diff --git a/client/contexts/ServerContext/endpoints/v1/users.ts b/definition/rest/v1/users.ts similarity index 71% rename from client/contexts/ServerContext/endpoints/v1/users.ts rename to definition/rest/v1/users.ts index cbe8a74c8d40..337a2182f2e3 100644 --- a/client/contexts/ServerContext/endpoints/v1/users.ts +++ b/definition/rest/v1/users.ts @@ -1,5 +1,5 @@ -import type { ITeam } from '../../../../../definition/ITeam'; -import type { IUser } from '../../../../../definition/IUser'; +import type { ITeam } from '../../ITeam'; +import type { IUser } from '../../IUser'; export type UsersEndpoints = { 'users.2fa.sendEmailCode': { diff --git a/definition/utils.ts b/definition/utils.ts index 90e7f59df57d..72339afa798a 100644 --- a/definition/utils.ts +++ b/definition/utils.ts @@ -7,3 +7,5 @@ export type ValueOf = T[keyof T]; export type UnionToIntersection = (T extends any ? (x: T) => void : never) extends (x: infer U) => void ? U : never; + +export type Awaited = T extends PromiseLike ? Awaited : T; diff --git a/ee/app/license/definitions/ILicense.ts b/ee/app/license/definitions/ILicense.ts new file mode 100644 index 000000000000..014912bef1ad --- /dev/null +++ b/ee/app/license/definitions/ILicense.ts @@ -0,0 +1,11 @@ +import { ILicenseTag } from './ILicenseTag'; + +export interface ILicense { + url: string; + expiry: string; + maxActiveUsers: number; + modules: string[]; + maxGuestUsers: number; + maxRoomsPerGuest: number; + tag?: ILicenseTag; +} diff --git a/ee/app/license/definitions/ILicenseTag.ts b/ee/app/license/definitions/ILicenseTag.ts new file mode 100644 index 000000000000..2f11fdebd5db --- /dev/null +++ b/ee/app/license/definitions/ILicenseTag.ts @@ -0,0 +1,4 @@ +export interface ILicenseTag { + name: string; + color: string; +} diff --git a/ee/app/license/server/license.ts b/ee/app/license/server/license.ts index ad0b962759f9..dd666753f621 100644 --- a/ee/app/license/server/license.ts +++ b/ee/app/license/server/license.ts @@ -4,24 +4,11 @@ import { Users } from '../../../../app/models/server'; import { getBundleModules, isBundle, getBundleFromModule } from './bundles'; import decrypt from './decrypt'; import { getTagColor } from './getTagColor'; +import { ILicense } from '../definitions/ILicense'; +import { ILicenseTag } from '../definitions/ILicenseTag'; const EnterpriseLicenses = new EventEmitter(); -interface ILicenseTag { - name: string; - color: string; -} - -export interface ILicense { - url: string; - expiry: string; - maxActiveUsers: number; - modules: string[]; - maxGuestUsers: number; - maxRoomsPerGuest: number; - tag?: ILicenseTag; -} - export interface IValidLicense { valid?: boolean; license: ILicense; diff --git a/ee/app/livechat-enterprise/server/api/business-hours.ts b/ee/app/livechat-enterprise/server/api/business-hours.ts index aab6a58bb406..4640ad58ae3c 100644 --- a/ee/app/livechat-enterprise/server/api/business-hours.ts +++ b/ee/app/livechat-enterprise/server/api/business-hours.ts @@ -1,21 +1,20 @@ import { API } from '../../../../../app/api/server'; import { findBusinessHours } from '../business-hour/lib/business-hour'; -// @ts-ignore API.v1.addRoute('livechat/business-hours.list', { authRequired: true }, { - get() { + async get() { const { offset, count } = this.getPaginationItems(); const { sort } = this.parseJsonQuery(); const { name } = this.queryParams; // @ts-ignore - return API.v1.success(Promise.await(findBusinessHours( + return API.v1.success(await findBusinessHours( this.userId, { offset, count, sort, }, - name))); + name)); }, }); diff --git a/ee/app/livechat-enterprise/server/business-hour/Helper.ts b/ee/app/livechat-enterprise/server/business-hour/Helper.ts index ef729e8def20..dd8da6293688 100644 --- a/ee/app/livechat-enterprise/server/business-hour/Helper.ts +++ b/ee/app/livechat-enterprise/server/business-hour/Helper.ts @@ -10,7 +10,7 @@ import { import { ILivechatBusinessHour, LivechatBusinessHourTypes } from '../../../../../definition/ILivechatBusinessHour'; const getAllAgentIdsWithoutDepartment = async (): Promise => { - const agentIdsWithDepartment = (await LivechatDepartmentAgents.find({}, { fields: { agentId: 1 } }).toArray()).map((dept: any) => dept.agentId); + const agentIdsWithDepartment = (await LivechatDepartmentAgents.find({}, { projection: { agentId: 1 } }).toArray()).map((dept: any) => dept.agentId); const agentIdsWithoutDepartment = (await Users.findUsersInRolesWithQuery('livechat-agent', { _id: { $nin: agentIdsWithDepartment }, }, { projection: { _id: 1 } }).toArray()).map((user: any) => user._id); diff --git a/ee/client/contexts/ServerContext/endpoints/v1/engagementDashboard.ts b/ee/client/contexts/ServerContext/endpoints/v1/engagementDashboard.ts deleted file mode 100644 index 1365da57e7f9..000000000000 --- a/ee/client/contexts/ServerContext/endpoints/v1/engagementDashboard.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IDailyActiveUsers } from '../../../../../../definition/IUser'; -import { Serialized } from '../../../../../../definition/Serialized'; - -export type EngagementDashboardEndpoints = { - 'engagement-dashboard/users/active-users': { - GET: (params: { start: string; end: string }) => { - month: Serialized[]; - }; - }; -}; diff --git a/ee/definition/rest/index.ts b/ee/definition/rest/index.ts new file mode 100644 index 000000000000..292ebd766aa7 --- /dev/null +++ b/ee/definition/rest/index.ts @@ -0,0 +1,4 @@ +import type { EngagementDashboardEndpoints } from './v1/engagementDashboard'; +import type { OmnichannelBusinessHoursEndpoints } from './v1/omnichannel/businessHours'; + +export type EnterpriseEndpoints = EngagementDashboardEndpoints & OmnichannelBusinessHoursEndpoints; diff --git a/ee/definition/rest/v1/engagementDashboard.ts b/ee/definition/rest/v1/engagementDashboard.ts new file mode 100644 index 000000000000..f20ab3307f6e --- /dev/null +++ b/ee/definition/rest/v1/engagementDashboard.ts @@ -0,0 +1,62 @@ +import { IDirectMessageRoom, IRoom } from '../../../../definition/IRoom'; +import { IDailyActiveUsers } from '../../../../definition/IUser'; +import { Serialized } from '../../../../definition/Serialized'; + +export type EngagementDashboardEndpoints = { + '/v1/engagement-dashboard/channels/list': { + GET: (params: { start: Date; end: Date; offset: number; count: number }) => { + channels: { + room: { + _id: IRoom['_id']; + name: IRoom['name'] | IRoom['fname']; + ts: IRoom['ts']; + t: IRoom['t']; + _updatedAt: IRoom['_updatedAt']; + usernames?: IDirectMessageRoom['usernames']; + }; + messages: number; + lastWeekMessages: number; + diffFromLastWeek: number; + }[]; + count: number; + offset: number; + total: number; + }; + }; + 'engagement-dashboard/messages/origin': { + GET: (params: { start: Date; end: Date }) => { + origins: { + t: IRoom['t']; + messages: number; + }[]; + }; + }; + 'engagement-dashboard/messages/top-five-popular-channels': { + GET: (params: { start: Date; end: Date }) => { + channels: { + t: IRoom['t']; + messages: number; + name: IRoom['name'] | IRoom['fname']; + usernames?: IDirectMessageRoom['usernames']; + }[]; + }; + }; + 'engagement-dashboard/messages/messages-sent': { + GET: (params: { start: Date; end: Date }) => { + days: { day: Date; messages: number }[]; + period: { + count: number; + variation: number; + }; + yesterday: { + count: number; + variation: number; + }; + }; + }; + 'engagement-dashboard/users/active-users': { + GET: (params: { start: string; end: string }) => { + month: Serialized[]; + }; + }; +}; diff --git a/ee/definition/rest/v1/omnichannel/businessHours.ts b/ee/definition/rest/v1/omnichannel/businessHours.ts new file mode 100644 index 000000000000..77b352c61259 --- /dev/null +++ b/ee/definition/rest/v1/omnichannel/businessHours.ts @@ -0,0 +1,7 @@ +import { ILivechatBusinessHour } from '../../../../../definition/ILivechatBusinessHour'; + +export type OmnichannelBusinessHoursEndpoints = { + 'livechat/business-hours.list': { + GET: () => ({ businessHours: ILivechatBusinessHour[] }); + }; +} diff --git a/ee/server/api/ldap.ts b/ee/server/api/ldap.ts index 1c4627e585d0..c51d21e588a4 100644 --- a/ee/server/api/ldap.ts +++ b/ee/server/api/ldap.ts @@ -5,7 +5,7 @@ import { LDAPEE } from '../sdk'; import { hasLicense } from '../../app/license/server/license'; API.v1.addRoute('ldap.syncNow', { authRequired: true }, { - post() { + async post() { if (!this.userId) { throw new Error('error-invalid-user'); } @@ -22,10 +22,10 @@ API.v1.addRoute('ldap.syncNow', { authRequired: true }, { throw new Error('LDAP_disabled'); } - LDAPEE.sync(); + await LDAPEE.sync(); return API.v1.success({ - message: 'Sync_in_progress', + message: 'Sync_in_progress' as const, }); }, }); diff --git a/ee/server/api/licenses.ts b/ee/server/api/licenses.ts index 0972584c3983..c59ab4e40606 100644 --- a/ee/server/api/licenses.ts +++ b/ee/server/api/licenses.ts @@ -1,9 +1,10 @@ import { check } from 'meteor/check'; -import { ILicense, getLicenses, validateFormat, flatModules, getMaxActiveUsers } from '../../app/license/server/license'; +import { getLicenses, validateFormat, flatModules, getMaxActiveUsers } from '../../app/license/server/license'; import { Settings, Users } from '../../../app/models/server'; import { API } from '../../../app/api/server/api'; import { hasPermission } from '../../../app/authorization/server'; +import { ILicense } from '../../app/license/definitions/ILicense'; function licenseTransform(license: ILicense): ILicense { return { diff --git a/ee/server/index.js b/ee/server/index.ts similarity index 100% rename from ee/server/index.js rename to ee/server/index.ts diff --git a/ee/server/lib/ldap/Manager.ts b/ee/server/lib/ldap/Manager.ts index 6b1c46b34eed..b146244a1850 100644 --- a/ee/server/lib/ldap/Manager.ts +++ b/ee/server/lib/ldap/Manager.ts @@ -192,7 +192,7 @@ export class LDAPEEManager extends LDAPManager { } const roles = await Roles.find({}, { - fields: { + projection: { _updatedAt: 0, }, }).toArray() as Array; diff --git a/ee/server/services/package-lock.json b/ee/server/services/package-lock.json index 3b1cfe814ece..110aa6323e6e 100644 --- a/ee/server/services/package-lock.json +++ b/ee/server/services/package-lock.json @@ -419,6 +419,17 @@ "debug": "4" } }, + "ajv": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.7.1.tgz", + "integrity": "sha512-gPpOObTO1QjbnN1sVMjJcp1TF9nggMfO4MBR5uQl6ZVTOaEPq5i4oq/6R9q2alMMPB3eg53wFv1RuJBLuxf3Hw==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, "amp": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", @@ -1225,6 +1236,11 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "fast-json-patch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.0.tgz", @@ -1676,6 +1692,11 @@ "pako": "^0.2.5" } }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -2490,6 +2511,11 @@ "once": "^1.3.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -2564,6 +2590,11 @@ "ttl": "^1.3.0" } }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "require-in-the-middle": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz", @@ -3070,6 +3101,14 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/ee/server/services/package.json b/ee/server/services/package.json index 5000e768d23c..984e7ce794bf 100644 --- a/ee/server/services/package.json +++ b/ee/server/services/package.json @@ -19,6 +19,7 @@ "license": "MIT", "dependencies": { "@rocket.chat/string-helpers": "^0.29.0", + "ajv": "^8.7.1", "bcrypt": "^5.0.1", "body-parser": "^1.19.0", "colorette": "^1.3.0", diff --git a/package-lock.json b/package-lock.json index 99c21395cfe9..d9df5f975bd9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11328,14 +11328,21 @@ } }, "ajv": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", - "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.7.1.tgz", + "integrity": "sha512-gPpOObTO1QjbnN1sVMjJcp1TF9nggMfO4MBR5uQl6ZVTOaEPq5i4oq/6R9q2alMMPB3eg53wFv1RuJBLuxf3Hw==", "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" + }, + "dependencies": { + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } } }, "ajv-errors": { @@ -18659,9 +18666,9 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-diff": { "version": "1.2.0", @@ -21311,6 +21318,19 @@ "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } } }, "hard-rejection": { @@ -31868,8 +31888,7 @@ "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, "require_optional": { "version": "1.0.1", @@ -34854,6 +34873,20 @@ "ajv": "^6.1.0", "ajv-errors": "^1.0.0", "ajv-keywords": "^3.1.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } } }, "source-map": { diff --git a/package.json b/package.json index 2bccf092d9e5..cf0956924ece 100644 --- a/package.json +++ b/package.json @@ -189,6 +189,7 @@ "@types/lodash.debounce": "^4.0.6", "adm-zip": "0.4.14", "agenda": "github:RocketChat/agenda#3.1.2", + "ajv": "^8.7.1", "apn": "2.2.0", "archiver": "^3.1.1", "atlassian-crowd-patched": "^0.5.1", diff --git a/server/sdk/types/ITeamService.ts b/server/sdk/types/ITeamService.ts index 50396cd20552..697a67e40641 100644 --- a/server/sdk/types/ITeamService.ts +++ b/server/sdk/types/ITeamService.ts @@ -47,11 +47,14 @@ export interface IListRoomsFilter { allowPrivateTeam: boolean; } -export interface ITeamUpdateData { - name: string; - type: TEAM_TYPE; - updateRoom?: boolean; // default is true -} +export type ITeamUpdateData = + { updateRoom?: boolean } & ({ + name: string; + type?: TEAM_TYPE; + } | { + name?: string; + type: TEAM_TYPE; + }) export type ITeamAutocompleteResult = Pick; diff --git a/tests/end-to-end/api/13-roles.js b/tests/end-to-end/api/13-roles.js deleted file mode 100644 index 162152a07214..000000000000 --- a/tests/end-to-end/api/13-roles.js +++ /dev/null @@ -1,440 +0,0 @@ -import { expect } from 'chai'; - -import { - getCredentials, - api, - request, - credentials, - login, - apiRoleNameUsers, - apiRoleNameSubscriptions, - apiRoleScopeUsers, - apiRoleDescription, - apiRoleScopeSubscriptions, -} from '../../data/api-data.js'; -import { password } from '../../data/user'; -import { updatePermission } from '../../data/permissions.helper'; -import { createUser, login as doLogin } from '../../data/users.helper'; - -function createRole(name, scope, description) { - return new Promise((resolve) => { - request.post(api('roles.create')) - .set(credentials) - .send({ - name, - scope, - description, - }) - .end((err, req) => { - resolve(req.body.role); - }); - }); -} - -function addUserToRole(roleName, username, scope) { - return new Promise((resolve) => { - request.post(api('roles.addUserToRole')) - .set(credentials) - .send({ - roleName, - username, - roomId: scope, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end((err, req) => { - resolve(req.body.role); - }); - }); -} - -describe('[Roles]', function() { - this.retries(0); - - before((done) => getCredentials(done)); - - describe('GET [/roles.list]', () => { - it('should return all roles', (done) => { - request.get(api('roles.list')) - .set(credentials) - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.property('roles').and.to.be.an('array'); - }) - .end(done); - }); - }); - - describe('GET [/roles.sync]', () => { - it('should return an array of roles which are updated after updatedSice date when search by "updatedSince" query parameter', (done) => { - request.get(api('roles.sync?updatedSince=2018-11-27T13:52:01Z')) - .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('roles'); - expect(res.body.roles).to.have.property('update').and.to.be.an('array'); - expect(res.body.roles).to.have.property('remove').and.to.be.an('array'); - }) - .end(done); - }); - - it('should return an error when updatedSince query parameter is not a valid ISODate string', (done) => { - request.get(api('roles.sync?updatedSince=fsafdf')) - .set(credentials) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - }) - .end(done); - }); - }); - - describe('POST [/roles.create]', () => { - it('should create a new role with Users scope', (done) => { - request.post(api('roles.create')) - .set(credentials) - .send({ - name: apiRoleNameUsers, - description: apiRoleDescription, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id'); - expect(res.body).to.have.nested.property('role.name', apiRoleNameUsers); - expect(res.body).to.have.nested.property('role.scope', apiRoleScopeUsers); - expect(res.body).to.have.nested.property('role.description', apiRoleDescription); - }) - .end(done); - }); - - it('should create a new role with Subscriptions scope', (done) => { - request.post(api('roles.create')) - .set(credentials) - .send({ - name: apiRoleNameSubscriptions, - scope: apiRoleScopeSubscriptions, - description: apiRoleDescription, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id'); - expect(res.body).to.have.nested.property('role.name', apiRoleNameSubscriptions); - expect(res.body).to.have.nested.property('role.scope', apiRoleScopeSubscriptions); - expect(res.body).to.have.nested.property('role.description', apiRoleDescription); - }) - .end(done); - }); - - it('should NOT create a new role with an existing role name', (done) => { - request.post(api('roles.create')) - .set(credentials) - .send({ - name: apiRoleNameUsers, - description: apiRoleDescription, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body).to.have.nested.property('error', 'Role name already exists [error-duplicate-role-names-not-allowed]'); - }) - .end(done); - }); - }); - - describe('POST [/roles.addUserToRole]', () => { - it('should assign a role with User scope to an user', (done) => { - request.post(api('roles.addUserToRole')) - .set(credentials) - .send({ - roleName: apiRoleNameUsers, - username: login.user, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id'); - expect(res.body).to.have.nested.property('role.name', apiRoleNameUsers); - expect(res.body).to.have.nested.property('role.scope', apiRoleScopeUsers); - }) - .end(done); - }); - - it('should assign a role with Subscriptions scope to an user', (done) => { - request.post(api('roles.addUserToRole')) - .set(credentials) - .send({ - roleName: apiRoleNameSubscriptions, - username: login.user, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id'); - expect(res.body).to.have.nested.property('role.name', apiRoleNameSubscriptions); - expect(res.body).to.have.nested.property('role.scope', apiRoleScopeSubscriptions); - }) - .end(done); - }); - }); - - describe('GET [/roles.getUsersInRole]', () => { - let userCredentials; - before((done) => { - createUser().then((createdUser) => { - doLogin(createdUser.username, password).then((createdUserCredentials) => { - userCredentials = createdUserCredentials; - updatePermission('access-permissions', ['admin', 'user']).then(done); - }); - }); - }); - it('should return an error when "role" query param is not provided', (done) => { - request.get(api('roles.getUsersInRole')) - .set(userCredentials) - .query({ - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-param-not-provided'); - }) - .end(done); - }); - it('should return an error when the user does not the necessary permission', (done) => { - updatePermission('access-permissions', ['admin']).then(() => { - request.get(api('roles.getUsersInRole')) - .set(userCredentials) - .query({ - role: 'admin', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-not-allowed'); - }) - .end(done); - }); - }); - it('should return an error when the user try access rooms permissions and does not have the necessary permission', (done) => { - updatePermission('access-permissions', ['admin', 'user']).then(() => { - updatePermission('view-other-user-channels', []).then(() => { - request.get(api('roles.getUsersInRole')) - .set(userCredentials) - .query({ - role: 'admin', - roomId: 'GENERAL', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-not-allowed'); - }) - .end(done); - }); - }); - }); - it('should return the list of users', (done) => { - updatePermission('access-permissions', ['admin', 'user']).then(() => { - updatePermission('view-other-user-channels', ['admin', 'user']).then(() => { - request.get(api('roles.getUsersInRole')) - .set(userCredentials) - .query({ - role: 'admin', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body.users).to.be.an('array'); - }) - .end(done); - }); - }); - }); - it('should return the list of users when find by room Id', (done) => { - request.get(api('roles.getUsersInRole')) - .set(userCredentials) - .query({ - role: 'admin', - roomId: 'GENERAL', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body.users).to.be.an('array'); - }) - .end(done); - }); - }); - - describe('POST [/roles.update]', () => { - const roleName = `role-${ Date.now() }`; - let newRole; - before(async () => { - newRole = await createRole(roleName, 'Users', 'Role description test'); - }); - - it('should update an existing role', (done) => { - const newRoleName = `${ roleName }Updated`; - const newRoleDescription = 'New role description'; - - request.post(api('roles.update')) - .set(credentials) - .send({ - roleId: newRole._id, - name: newRoleName, - description: newRoleDescription, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id', newRole._id); - expect(res.body).to.have.nested.property('role.name', newRoleName); - expect(res.body).to.have.nested.property('role.scope', newRole.scope); - expect(res.body).to.have.nested.property('role.description', newRoleDescription); - }) - .end(done); - }); - - it('should NOT update a role with an existing role name', (done) => { - request.post(api('roles.update')) - .set(credentials) - .send({ - roleId: newRole._id, - name: apiRoleNameUsers, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body).to.have.nested.property('error', 'Role name already exists [error-duplicate-role-names-not-allowed]'); - }) - .end(done); - }); - }); - - describe('POST [/roles.delete]', () => { - let roleWithUser; - let roleWithoutUser; - before(async () => { - roleWithUser = await createRole(`roleWithUser-${ Date.now() }`, 'Users'); - roleWithoutUser = await createRole(`roleWithoutUser-${ Date.now() }`, 'Users'); - - await addUserToRole(roleWithUser.name, login.user); - }); - - it('should delete a role that it is not being used', (done) => { - request.post(api('roles.delete')) - .set(credentials) - .send({ - roleId: roleWithoutUser._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - - it('should NOT delete a role that it is protected', (done) => { - request.post(api('roles.delete')) - .set(credentials) - .send({ - roleId: 'admin', - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body).to.have.nested.property('error', 'Cannot delete a protected role [error-role-protected]'); - }) - .end(done); - }); - - it('should NOT delete a role that it is being used', (done) => { - request.post(api('roles.delete')) - .set(credentials) - .send({ - roleId: roleWithUser._id, - }) - .expect('Content-Type', 'application/json') - .expect(400) - .expect((res) => { - expect(res.body).to.have.property('success', false); - expect(res.body).to.have.nested.property('error', 'Cannot delete role because it\'s in use [error-role-in-use]'); - }) - .end(done); - }); - }); - - describe('POST [/roles.removeUserFromRole]', () => { - let usersScopedRole; - let subscriptionsScopedRole; - - before(async () => { - usersScopedRole = await createRole(`usersScopedRole-${ Date.now() }`, 'Users'); - subscriptionsScopedRole = await createRole(`subscriptionsScopedRole-${ Date.now() }`, 'Subscriptions'); - - await addUserToRole(usersScopedRole.name, login.user); - await addUserToRole(subscriptionsScopedRole.name, login.user, 'GENERAL'); - }); - - it('should unassign a role with User scope from an user', (done) => { - request.post(api('roles.removeUserFromRole')) - .set(credentials) - .send({ - roleName: usersScopedRole.name, - username: login.user, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id', usersScopedRole._id); - expect(res.body).to.have.nested.property('role.name', usersScopedRole.name); - expect(res.body).to.have.nested.property('role.scope', usersScopedRole.scope); - expect(res.body).to.have.nested.property('role.description', usersScopedRole.description); - }) - .end(done); - }); - - it('should unassign a role with Subscriptions scope from an user', (done) => { - request.post(api('roles.removeUserFromRole')) - .set(credentials) - .send({ - roleName: subscriptionsScopedRole.name, - username: login.user, - scope: 'GENERAL', - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id', subscriptionsScopedRole._id); - expect(res.body).to.have.nested.property('role.name', subscriptionsScopedRole.name); - expect(res.body).to.have.nested.property('role.scope', subscriptionsScopedRole.scope); - expect(res.body).to.have.nested.property('role.description', subscriptionsScopedRole.description); - }) - .end(done); - }); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index dff6cb6c6ddd..1094df397a41 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -42,9 +42,10 @@ "exclude": [ "./.meteor/**", "./packages/**", - "./imports/client" + "./imports/client**", + "**/dist/**", // "./ee/server/services/**" - // "node_modules" + "node_modules" ], "ts-node": { "files": true From 462978d83e790ba57e50d9d19161b792c3336cae Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 16 Nov 2021 09:16:10 -0300 Subject: [PATCH 20/23] Definition unit tests --- .mocharc.definition.js | 30 +++++++++++++++++++ ...s.test.ts => TeamsAddMembersProps.spec.ts} | 0 ....ts => TeamsConvertToChannelProps.spec.ts} | 0 ...Props.test.ts => TeamsDeleteProps.spec.ts} | 0 ...eProps.test.ts => TeamsLeaveProps.spec.ts} | 0 ...test.ts => TeamsRemoveMemberProps.spec.ts} | 0 ...s.test.ts => TeamsRemoveRoomProps.spec.ts} | 0 ...test.ts => TeamsUpdateMemberProps.spec.ts} | 0 ...Props.test.ts => TeamsUpdateProps.spec.ts} | 0 9 files changed, 30 insertions(+) create mode 100644 .mocharc.definition.js rename definition/rest/v1/teams/{TeamsAddMembersProps.test.ts => TeamsAddMembersProps.spec.ts} (100%) rename definition/rest/v1/teams/{TeamsConvertToChannelProps.test.ts => TeamsConvertToChannelProps.spec.ts} (100%) rename definition/rest/v1/teams/{TeamsDeleteProps.test.ts => TeamsDeleteProps.spec.ts} (100%) rename definition/rest/v1/teams/{TeamsLeaveProps.test.ts => TeamsLeaveProps.spec.ts} (100%) rename definition/rest/v1/teams/{TeamsRemoveMemberProps.test.ts => TeamsRemoveMemberProps.spec.ts} (100%) rename definition/rest/v1/teams/{TeamsRemoveRoomProps.test.ts => TeamsRemoveRoomProps.spec.ts} (100%) rename definition/rest/v1/teams/{TeamsUpdateMemberProps.test.ts => TeamsUpdateMemberProps.spec.ts} (100%) rename definition/rest/v1/teams/{TeamsUpdateProps.test.ts => TeamsUpdateProps.spec.ts} (100%) diff --git a/.mocharc.definition.js b/.mocharc.definition.js new file mode 100644 index 000000000000..00787edf8b6b --- /dev/null +++ b/.mocharc.definition.js @@ -0,0 +1,30 @@ +'use strict'; + +/** + * Mocha configuration for client-side unit and integration tests. + */ + +const base = require('./.mocharc.base.json'); + +/** + * Mocha will run `ts-node` without doing type checking to speed-up the tests. It should be fine as `npm run typecheck` + * covers test files too. + */ + +Object.assign(process.env, { + TS_NODE_FILES: true, + TS_NODE_TRANSPILE_ONLY: true, +}, process.env); + +module.exports = { + ...base, // see https://github.com/mochajs/mocha/issues/3916 + require: [ + ...base.require, + ], + exit: false, + slow: 200, + spec: [ + 'definition/**/*.spec.ts', + 'definition/**/*.spec.tsx', + ], +}; diff --git a/definition/rest/v1/teams/TeamsAddMembersProps.test.ts b/definition/rest/v1/teams/TeamsAddMembersProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsAddMembersProps.test.ts rename to definition/rest/v1/teams/TeamsAddMembersProps.spec.ts diff --git a/definition/rest/v1/teams/TeamsConvertToChannelProps.test.ts b/definition/rest/v1/teams/TeamsConvertToChannelProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsConvertToChannelProps.test.ts rename to definition/rest/v1/teams/TeamsConvertToChannelProps.spec.ts diff --git a/definition/rest/v1/teams/TeamsDeleteProps.test.ts b/definition/rest/v1/teams/TeamsDeleteProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsDeleteProps.test.ts rename to definition/rest/v1/teams/TeamsDeleteProps.spec.ts diff --git a/definition/rest/v1/teams/TeamsLeaveProps.test.ts b/definition/rest/v1/teams/TeamsLeaveProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsLeaveProps.test.ts rename to definition/rest/v1/teams/TeamsLeaveProps.spec.ts diff --git a/definition/rest/v1/teams/TeamsRemoveMemberProps.test.ts b/definition/rest/v1/teams/TeamsRemoveMemberProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsRemoveMemberProps.test.ts rename to definition/rest/v1/teams/TeamsRemoveMemberProps.spec.ts diff --git a/definition/rest/v1/teams/TeamsRemoveRoomProps.test.ts b/definition/rest/v1/teams/TeamsRemoveRoomProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsRemoveRoomProps.test.ts rename to definition/rest/v1/teams/TeamsRemoveRoomProps.spec.ts diff --git a/definition/rest/v1/teams/TeamsUpdateMemberProps.test.ts b/definition/rest/v1/teams/TeamsUpdateMemberProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsUpdateMemberProps.test.ts rename to definition/rest/v1/teams/TeamsUpdateMemberProps.spec.ts diff --git a/definition/rest/v1/teams/TeamsUpdateProps.test.ts b/definition/rest/v1/teams/TeamsUpdateProps.spec.ts similarity index 100% rename from definition/rest/v1/teams/TeamsUpdateProps.test.ts rename to definition/rest/v1/teams/TeamsUpdateProps.spec.ts From 37574824fdd834db836864f810952aec482ddfd2 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 16 Nov 2021 09:48:16 -0300 Subject: [PATCH 21/23] testunit-definition --- .github/workflows/build_and_test.yml | 3 +++ .mocharc.definition.js | 1 - package.json | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 8f4e85f39d3e..6631a6cb53c7 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -242,6 +242,9 @@ jobs: run: | npm install + - name: Unit Test (definitions) + run: npm run testunit-definition + - name: Unit Test run: npm run testunit diff --git a/.mocharc.definition.js b/.mocharc.definition.js index 00787edf8b6b..f7036b4d28f7 100644 --- a/.mocharc.definition.js +++ b/.mocharc.definition.js @@ -25,6 +25,5 @@ module.exports = { slow: 200, spec: [ 'definition/**/*.spec.ts', - 'definition/**/*.spec.tsx', ], }; diff --git a/package.json b/package.json index cf0956924ece..2122ded6b449 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "testapi": "mocha --config ./.mocharc.api.js", "testunit": "mocha --config ./.mocharc.js", "testunit-client": "mocha --config ./.mocharc.client.js", + "testunit-definition": "mocha --config ./.mocharc.definition.js", "testunit-watch": "mocha --watch --config ./.mocharc.js", "test": "npm run testapi && npm run testui", "translation-diff": "node .scripts/translationDiff.js", From 09a6d11e122165b7640d299025873ed3a19ed855 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 16 Nov 2021 10:39:12 -0300 Subject: [PATCH 22/23] Change chai imports --- .../v1/teams/TeamsAddMembersProps.spec.ts | 35 ++++++------ .../teams/TeamsConvertToChannelProps.spec.ts | 19 ++++--- .../rest/v1/teams/TeamsDeleteProps.spec.ts | 31 ++++++----- .../rest/v1/teams/TeamsLeaveProps.spec.ts | 31 ++++++----- .../v1/teams/TeamsRemoveMemberProps.spec.ts | 31 ++++++----- .../v1/teams/TeamsRemoveRoomProps.spec.ts | 15 +++--- .../v1/teams/TeamsUpdateMemberProps.spec.ts | 29 +++++----- .../rest/v1/teams/TeamsUpdateProps.spec.ts | 53 +++++++++---------- 8 files changed, 118 insertions(+), 126 deletions(-) diff --git a/definition/rest/v1/teams/TeamsAddMembersProps.spec.ts b/definition/rest/v1/teams/TeamsAddMembersProps.spec.ts index f2d7ec71bc25..d06fcc7507d1 100644 --- a/definition/rest/v1/teams/TeamsAddMembersProps.spec.ts +++ b/definition/rest/v1/teams/TeamsAddMembersProps.spec.ts @@ -1,71 +1,70 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsAddMembersProps } from './TeamsAddMembersProps'; describe('TeamsAddMemberProps (definition/rest/v1)', () => { describe('isTeamsAddMembersProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsAddMembersProps); + assert.isFunction(isTeamsAddMembersProps); }); it('should return false if the parameter is empty', () => { - chai.assert.isFalse(isTeamsAddMembersProps({})); + assert.isFalse(isTeamsAddMembersProps({})); }); it('should return false if teamId is provided but no member was provided', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ teamId: '123' })); + assert.isFalse(isTeamsAddMembersProps({ teamId: '123' })); }); it('should return false if teamName is provided but no member was provided', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ teamName: '123' })); + assert.isFalse(isTeamsAddMembersProps({ teamName: '123' })); }); it('should return false if members is provided but no teamId or teamName were provided', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ members: [{ userId: '123' }] })); + assert.isFalse(isTeamsAddMembersProps({ members: [{ userId: '123' }] })); }); it('should return false if teamName was provided but members are empty', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ teamName: '123', members: [] })); + assert.isFalse(isTeamsAddMembersProps({ teamName: '123', members: [] })); }); it('should return false if teamId was provided but members are empty', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ teamId: '123', members: [] })); + assert.isFalse(isTeamsAddMembersProps({ teamId: '123', members: [] })); }); it('should return false if members with role is provided but no teamId or teamName were provided', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }] })); + assert.isFalse(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }] })); }); it('should return true if members is provided and teamId is provided', () => { - chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123' }], teamId: '123' })); + assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123' }], teamId: '123' })); }); it('should return true if members is provided and teamName is provided', () => { - chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123' }], teamName: '123' })); + assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123' }], teamName: '123' })); }); it('should return true if members with role is provided and teamId is provided', () => { - chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }], teamId: '123' })); + assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }], teamId: '123' })); }); it('should return true if members with role is provided and teamName is provided', () => { - chai.assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }], teamName: '123' })); + assert.isTrue(isTeamsAddMembersProps({ members: [{ userId: '123', roles: ['123'] }], teamName: '123' })); }); it('should return false if teamName was provided and members contains an invalid property', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ teamName: '123', members: [{ userId: '123', roles: ['123'], invalid: true }] })); + assert.isFalse(isTeamsAddMembersProps({ teamName: '123', members: [{ userId: '123', roles: ['123'], invalid: true }] })); }); it('should return false if teamId was provided and members contains an invalid property', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ teamId: '123', members: [{ userId: '123', roles: ['123'], invalid: true }] })); + assert.isFalse(isTeamsAddMembersProps({ teamId: '123', members: [{ userId: '123', roles: ['123'], invalid: true }] })); }); it('should return false if teamName informed but contains an invalid property', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ member: [{ userId: '123', roles: ['123'] }], teamName: '123', invalid: true })); + assert.isFalse(isTeamsAddMembersProps({ member: [{ userId: '123', roles: ['123'] }], teamName: '123', invalid: true })); }); it('should return false if teamId informed but contains an invalid property', () => { - chai.assert.isFalse(isTeamsAddMembersProps({ member: [{ userId: '123', roles: ['123'] }], teamId: '123', invalid: true })); + assert.isFalse(isTeamsAddMembersProps({ member: [{ userId: '123', roles: ['123'] }], teamId: '123', invalid: true })); }); }); }); diff --git a/definition/rest/v1/teams/TeamsConvertToChannelProps.spec.ts b/definition/rest/v1/teams/TeamsConvertToChannelProps.spec.ts index 5c292c6f1b30..0222e0acfe1c 100644 --- a/definition/rest/v1/teams/TeamsConvertToChannelProps.spec.ts +++ b/definition/rest/v1/teams/TeamsConvertToChannelProps.spec.ts @@ -1,39 +1,38 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsConvertToChannelProps } from './TeamsConvertToChannelProps'; describe('TeamsConvertToChannelProps (definition/rest/v1)', () => { describe('isTeamsConvertToChannelProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsConvertToChannelProps); + assert.isFunction(isTeamsConvertToChannelProps); }); it('should return false if neither teamName or teamId is provided', () => { - chai.assert.isFalse(isTeamsConvertToChannelProps({})); + assert.isFalse(isTeamsConvertToChannelProps({})); }); it('should return true if teamName is provided', () => { - chai.assert.isTrue(isTeamsConvertToChannelProps({ teamName: 'teamName' })); + assert.isTrue(isTeamsConvertToChannelProps({ teamName: 'teamName' })); }); it('should return true if teamId is provided', () => { - chai.assert.isTrue(isTeamsConvertToChannelProps({ teamId: 'teamId' })); + assert.isTrue(isTeamsConvertToChannelProps({ teamId: 'teamId' })); }); it('should return false if both teamName and teamId are provided', () => { - chai.assert.isFalse(isTeamsConvertToChannelProps({ teamName: 'teamName', teamId: 'teamId' })); + assert.isFalse(isTeamsConvertToChannelProps({ teamName: 'teamName', teamId: 'teamId' })); }); it('should return false if teamName is not a string', () => { - chai.assert.isFalse(isTeamsConvertToChannelProps({ teamName: 1 })); + assert.isFalse(isTeamsConvertToChannelProps({ teamName: 1 })); }); it('should return false if teamId is not a string', () => { - chai.assert.isFalse(isTeamsConvertToChannelProps({ teamId: 1 })); + assert.isFalse(isTeamsConvertToChannelProps({ teamId: 1 })); }); it('should return false if an additionalProperties is provided', () => { - chai.assert.isFalse(isTeamsConvertToChannelProps({ teamName: 'teamName', additionalProperties: 'additionalProperties' })); + assert.isFalse(isTeamsConvertToChannelProps({ teamName: 'teamName', additionalProperties: 'additionalProperties' })); }); }); }); diff --git a/definition/rest/v1/teams/TeamsDeleteProps.spec.ts b/definition/rest/v1/teams/TeamsDeleteProps.spec.ts index 9efc17cd1dec..aa5fbfa287c8 100644 --- a/definition/rest/v1/teams/TeamsDeleteProps.spec.ts +++ b/definition/rest/v1/teams/TeamsDeleteProps.spec.ts @@ -1,64 +1,63 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsDeleteProps } from './TeamsDeleteProps'; describe('TeamsDeleteProps (definition/rest/v1)', () => { describe('isTeamsDeleteProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsDeleteProps); + assert.isFunction(isTeamsDeleteProps); }); it('should return false if neither teamName or teamId is provided', () => { - chai.assert.isFalse(isTeamsDeleteProps({})); + assert.isFalse(isTeamsDeleteProps({})); }); it('should return true if teamId is provided', () => { - chai.assert.isTrue(isTeamsDeleteProps({ teamId: 'teamId' })); + assert.isTrue(isTeamsDeleteProps({ teamId: 'teamId' })); }); it('should return true if teamName is provided', () => { - chai.assert.isTrue(isTeamsDeleteProps({ teamName: 'teamName' })); + assert.isTrue(isTeamsDeleteProps({ teamName: 'teamName' })); }); it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is empty', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: [] })); + assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: [] })); }); it('should return false if teamName and roomsToRemove are provided, but roomsToRemove is empty', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: [] })); + assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: [] })); }); it('should return true if teamId and roomsToRemove are provided', () => { - chai.assert.isTrue(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: ['roomId'] })); + assert.isTrue(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: ['roomId'] })); }); it('should return true if teamName and roomsToRemove are provided', () => { - chai.assert.isTrue(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: ['roomId'] })); + assert.isTrue(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: ['roomId'] })); }); it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is not an array', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: {} })); + assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: {} })); }); it('should return false if teamName and roomsToRemove are provided, but roomsToRemove is not an array', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: {} })); + assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: {} })); }); it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is not an array of strings', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: [1] })); + assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: [1] })); }); it('should return false if teamName and roomsToRemove are provided, but roomsToRemove is not an array of strings', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: [1] })); + assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: [1] })); }); it('should return false if teamName and rooms are provided but an extra property is provided', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: ['roomsToRemove'], extra: 'extra' })); + assert.isFalse(isTeamsDeleteProps({ teamName: 'teamName', roomsToRemove: ['roomsToRemove'], extra: 'extra' })); }); it('should return false if teamId and rooms are provided but an extra property is provided', () => { - chai.assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: ['roomsToRemove'], extra: 'extra' })); + assert.isFalse(isTeamsDeleteProps({ teamId: 'teamId', roomsToRemove: ['roomsToRemove'], extra: 'extra' })); }); }); }); diff --git a/definition/rest/v1/teams/TeamsLeaveProps.spec.ts b/definition/rest/v1/teams/TeamsLeaveProps.spec.ts index 1b6255d97970..6661ab100bf1 100644 --- a/definition/rest/v1/teams/TeamsLeaveProps.spec.ts +++ b/definition/rest/v1/teams/TeamsLeaveProps.spec.ts @@ -1,64 +1,63 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsLeaveProps } from './TeamsLeaveProps'; describe('TeamsLeaveProps (definition/rest/v1)', () => { describe('isTeamsLeaveProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsLeaveProps); + assert.isFunction(isTeamsLeaveProps); }); it('should return false if neither teamName or teamId is provided', () => { - chai.assert.isFalse(isTeamsLeaveProps({})); + assert.isFalse(isTeamsLeaveProps({})); }); it('should return true if teamId is provided', () => { - chai.assert.isTrue(isTeamsLeaveProps({ teamId: 'teamId' })); + assert.isTrue(isTeamsLeaveProps({ teamId: 'teamId' })); }); it('should return true if teamName is provided', () => { - chai.assert.isTrue(isTeamsLeaveProps({ teamName: 'teamName' })); + assert.isTrue(isTeamsLeaveProps({ teamName: 'teamName' })); }); it('should return false if teamId and roomsToRemove are provided, but roomsToRemove is empty', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: [] })); + assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: [] })); }); it('should return false if teamName and rooms are provided, but rooms is empty', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: [] })); + assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: [] })); }); it('should return true if teamId and rooms are provided', () => { - chai.assert.isTrue(isTeamsLeaveProps({ teamId: 'teamId', rooms: ['roomId'] })); + assert.isTrue(isTeamsLeaveProps({ teamId: 'teamId', rooms: ['roomId'] })); }); it('should return true if teamName and rooms are provided', () => { - chai.assert.isTrue(isTeamsLeaveProps({ teamName: 'teamName', rooms: ['roomId'] })); + assert.isTrue(isTeamsLeaveProps({ teamName: 'teamName', rooms: ['roomId'] })); }); it('should return false if teamId and rooms are provided, but rooms is not an array', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: {} })); + assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: {} })); }); it('should return false if teamName and rooms are provided, but rooms is not an array', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: {} })); + assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: {} })); }); it('should return false if teamId and rooms are provided, but rooms is not an array of strings', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: [1] })); + assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: [1] })); }); it('should return false if teamName and rooms are provided, but rooms is not an array of strings', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: [1] })); + assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: [1] })); }); it('should return false if teamName and rooms are provided but an extra property is provided', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: ['rooms'], extra: 'extra' })); + assert.isFalse(isTeamsLeaveProps({ teamName: 'teamName', rooms: ['rooms'], extra: 'extra' })); }); it('should return false if teamId and rooms are provided but an extra property is provided', () => { - chai.assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: ['rooms'], extra: 'extra' })); + assert.isFalse(isTeamsLeaveProps({ teamId: 'teamId', rooms: ['rooms'], extra: 'extra' })); }); }); }); diff --git a/definition/rest/v1/teams/TeamsRemoveMemberProps.spec.ts b/definition/rest/v1/teams/TeamsRemoveMemberProps.spec.ts index f66c765e0348..2d3afdaac334 100644 --- a/definition/rest/v1/teams/TeamsRemoveMemberProps.spec.ts +++ b/definition/rest/v1/teams/TeamsRemoveMemberProps.spec.ts @@ -1,61 +1,60 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsRemoveMemberProps } from './TeamsRemoveMemberProps'; describe('Teams (definition/rest/v1)', () => { describe('isTeamsRemoveMemberProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsRemoveMemberProps); + assert.isFunction(isTeamsRemoveMemberProps); }); it('should return false if parameter is empty', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({})); + assert.isFalse(isTeamsRemoveMemberProps({})); }); it('should return false if teamId is is informed but missing userId', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId' })); + assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId' })); }); it('should return false if teamName is is informed but missing userId', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName' })); + assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName' })); }); it('should return true if teamId and userId are informed', () => { - chai.assert.isTrue(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId' })); + assert.isTrue(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId' })); }); it('should return true if teamName and userId are informed', () => { - chai.assert.isTrue(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId' })); + assert.isTrue(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId' })); }); it('should return false if teamName and userId are informed but rooms are empty', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: [] })); + assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: [] })); }); it('should return false if teamId and userId are informed and rooms are empty', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [] })); + assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [] })); }); it('should return false if teamId and userId are informed but rooms are empty', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [] })); + assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [] })); }); it('should return true if teamId and userId are informed and rooms are informed', () => { - chai.assert.isTrue(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: ['room'] })); + assert.isTrue(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: ['room'] })); }); it('should return false if teamId and userId are informed and rooms are informed but rooms is not an array of strings', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [123] })); + assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: [123] })); }); it('should return false if teamName and userId are informed and rooms are informed but there is an extra property', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: ['room'], extra: 'extra' })); + assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: ['room'], extra: 'extra' })); }); it('should return false if teamId and userId are informed and rooms are informed but there is an extra property', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: ['room'], extra: 'extra' })); + assert.isFalse(isTeamsRemoveMemberProps({ teamId: 'teamId', userId: 'userId', rooms: ['room'], extra: 'extra' })); }); it('should return false if teamName and userId are informed and rooms are informed but there is an extra property', () => { - chai.assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: ['room'], extra: 'extra' })); + assert.isFalse(isTeamsRemoveMemberProps({ teamName: 'teamName', userId: 'userId', rooms: ['room'], extra: 'extra' })); }); }); }); diff --git a/definition/rest/v1/teams/TeamsRemoveRoomProps.spec.ts b/definition/rest/v1/teams/TeamsRemoveRoomProps.spec.ts index 226185dcc955..4a6a065831d7 100644 --- a/definition/rest/v1/teams/TeamsRemoveRoomProps.spec.ts +++ b/definition/rest/v1/teams/TeamsRemoveRoomProps.spec.ts @@ -1,27 +1,26 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsRemoveRoomProps } from './TeamsRemoveRoomProps'; describe('TeamsRemoveRoomProps (definition/rest/v1)', () => { describe('isTeamsRemoveRoomProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsRemoveRoomProps); + assert.isFunction(isTeamsRemoveRoomProps); }); it('should return false if roomId is not provided', () => { - chai.assert.isFalse(isTeamsRemoveRoomProps({})); + assert.isFalse(isTeamsRemoveRoomProps({})); }); it('should return false if roomId is provided but no teamId or teamName were provided', () => { - chai.assert.isFalse(isTeamsRemoveRoomProps({ roomId: 'roomId' })); + assert.isFalse(isTeamsRemoveRoomProps({ roomId: 'roomId' })); }); it('should return false if roomId is provided and teamId is provided', () => { - chai.assert.isTrue(isTeamsRemoveRoomProps({ roomId: 'roomId', teamId: 'teamId' })); + assert.isTrue(isTeamsRemoveRoomProps({ roomId: 'roomId', teamId: 'teamId' })); }); it('should return true if roomId is provided and teamName is provided', () => { - chai.assert.isTrue(isTeamsRemoveRoomProps({ roomId: 'roomId', teamName: 'teamName' })); + assert.isTrue(isTeamsRemoveRoomProps({ roomId: 'roomId', teamName: 'teamName' })); }); it('should return false if roomId and teamName are provided but an additional property is provided', () => { - chai.assert.isFalse(isTeamsRemoveRoomProps({ roomId: 'roomId', teamName: 'teamName', foo: 'bar' })); + assert.isFalse(isTeamsRemoveRoomProps({ roomId: 'roomId', teamName: 'teamName', foo: 'bar' })); }); }); }); diff --git a/definition/rest/v1/teams/TeamsUpdateMemberProps.spec.ts b/definition/rest/v1/teams/TeamsUpdateMemberProps.spec.ts index ed6a329772a4..8e25f1516059 100644 --- a/definition/rest/v1/teams/TeamsUpdateMemberProps.spec.ts +++ b/definition/rest/v1/teams/TeamsUpdateMemberProps.spec.ts @@ -1,59 +1,58 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsUpdateMemberProps } from './TeamsUpdateMemberProps'; describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { describe('isTeamsUpdateMemberProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsUpdateMemberProps); + assert.isFunction(isTeamsUpdateMemberProps); }); it('should return false if the parameter is empty', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({})); + assert.isFalse(isTeamsUpdateMemberProps({})); }); it('should return false if teamId is provided but no member was provided', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({ teamId: '123' })); + assert.isFalse(isTeamsUpdateMemberProps({ teamId: '123' })); }); it('should return false if teamName is provided but no member was provided', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({ teamName: '123' })); + assert.isFalse(isTeamsUpdateMemberProps({ teamName: '123' })); }); it('should return false if member is provided but no teamId or teamName were provided', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123' } })); + assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123' } })); }); it('should return false if member with role is provided but no teamId or teamName were provided', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] } })); + assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] } })); }); it('should return true if member is provided and teamId is provided', () => { - chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123' }, teamId: '123' })); + assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123' }, teamId: '123' })); }); it('should return true if member is provided and teamName is provided', () => { - chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123' }, teamName: '123' })); + assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123' }, teamName: '123' })); }); it('should return true if member with role is provided and teamId is provided', () => { - chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamId: '123' })); + assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamId: '123' })); }); it('should return true if member with role is provided and teamName is provided', () => { - chai.assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamName: '123' })); + assert.isTrue(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamName: '123' })); }); it('should return false if teamName was provided and member contains an invalid property', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', invalid: '123' }, teamName: '123' })); + assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', invalid: '123' }, teamName: '123' })); }); it('should return false if teamId was provided and member contains an invalid property', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', invalid: '123' }, teamId: '123' })); + assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', invalid: '123' }, teamId: '123' })); }); it('should return false if contains an invalid property', () => { - chai.assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamName: '123', invalid: true })); + assert.isFalse(isTeamsUpdateMemberProps({ member: { userId: '123', roles: ['123'] }, teamName: '123', invalid: true })); }); }); }); diff --git a/definition/rest/v1/teams/TeamsUpdateProps.spec.ts b/definition/rest/v1/teams/TeamsUpdateProps.spec.ts index ce09985901d1..8f8fdb8b072f 100644 --- a/definition/rest/v1/teams/TeamsUpdateProps.spec.ts +++ b/definition/rest/v1/teams/TeamsUpdateProps.spec.ts @@ -1,65 +1,64 @@ -/* eslint-env mocha */ -import chai from 'chai'; +import { assert } from 'chai'; import { isTeamsUpdateProps } from './TeamsUpdateProps'; describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { describe('isTeamsUpdateProps', () => { it('should be a function', () => { - chai.assert.isFunction(isTeamsUpdateProps); + assert.isFunction(isTeamsUpdateProps); }); it('should return false when provided anything that is not an TeamsUpdateProps', () => { - chai.assert.isFalse(isTeamsUpdateProps(undefined)); - chai.assert.isFalse(isTeamsUpdateProps(null)); - chai.assert.isFalse(isTeamsUpdateProps('')); - chai.assert.isFalse(isTeamsUpdateProps(123)); - chai.assert.isFalse(isTeamsUpdateProps({})); - chai.assert.isFalse(isTeamsUpdateProps([])); - chai.assert.isFalse(isTeamsUpdateProps(new Date())); - chai.assert.isFalse(isTeamsUpdateProps(new Error())); + assert.isFalse(isTeamsUpdateProps(undefined)); + assert.isFalse(isTeamsUpdateProps(null)); + assert.isFalse(isTeamsUpdateProps('')); + assert.isFalse(isTeamsUpdateProps(123)); + assert.isFalse(isTeamsUpdateProps({})); + assert.isFalse(isTeamsUpdateProps([])); + assert.isFalse(isTeamsUpdateProps(new Date())); + assert.isFalse(isTeamsUpdateProps(new Error())); }); it('should return false when only teamName is provided to TeamsUpdateProps', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamName: 'teamName', })); }); it('should return false when only teamId is provided to TeamsUpdateProps', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamId: 'teamId', })); }); it('should return false when teamName and data are provided to TeamsUpdateProps but data is an empty object', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamName: 'teamName', data: {}, })); }); it('should return false when teamId and data are provided to TeamsUpdateProps but data is an empty object', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamId: 'teamId', data: {}, })); }); it('should return false when teamName and data are provided to TeamsUpdateProps but data is not an object', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamName: 'teamName', data: 'data', })); }); it('should return false when teamId and data are provided to TeamsUpdateProps but data is not an object', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamId: 'teamId', data: 'data', })); }); it('should return true when teamName and data.name are provided to TeamsUpdateProps', () => { - chai.assert.isTrue(isTeamsUpdateProps({ + assert.isTrue(isTeamsUpdateProps({ teamName: 'teamName', data: { name: 'name', @@ -68,7 +67,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return true when teamId and data.name are provided to TeamsUpdateProps', () => { - chai.assert.isTrue(isTeamsUpdateProps({ + assert.isTrue(isTeamsUpdateProps({ teamId: 'teamId', data: { name: 'name', @@ -77,7 +76,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return true when teamName and data.type are provided to TeamsUpdateProps', () => { - chai.assert.isTrue(isTeamsUpdateProps({ + assert.isTrue(isTeamsUpdateProps({ teamName: 'teamName', data: { type: 0, @@ -86,7 +85,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return true when teamId and data.type are provided to TeamsUpdateProps', () => { - chai.assert.isTrue(isTeamsUpdateProps({ + assert.isTrue(isTeamsUpdateProps({ teamId: 'teamId', data: { type: 0, @@ -95,7 +94,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return true when teamName and data.name and data.type are provided to TeamsUpdateProps', () => { - chai.assert.isTrue(isTeamsUpdateProps({ + assert.isTrue(isTeamsUpdateProps({ teamName: 'teamName', data: { name: 'name', @@ -105,7 +104,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return true when teamId and data.name and data.type are provided to TeamsUpdateProps', () => { - chai.assert.isTrue(isTeamsUpdateProps({ + assert.isTrue(isTeamsUpdateProps({ teamId: 'teamId', data: { name: 'name', @@ -115,7 +114,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return false when teamName, data.name, data.type are some more extra data are provided to TeamsUpdateProps', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamName: 'teamName', data: { name: 'name', @@ -126,7 +125,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return false when teamId, data.name, data.type are some more extra data are provided to TeamsUpdateProps', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamId: 'teamId', data: { name: 'name', @@ -137,7 +136,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return false when teamName, data.name, data.type are some more extra parameter are provided to TeamsUpdateProps', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamName: 'teamName', extra: 'extra', data: { @@ -148,7 +147,7 @@ describe('TeamsUpdateMemberProps (definition/rest/v1)', () => { }); it('should return false when teamId, data.name, data.type are some more extra parameter are provided to TeamsUpdateProps', () => { - chai.assert.isFalse(isTeamsUpdateProps({ + assert.isFalse(isTeamsUpdateProps({ teamId: 'teamId', extra: 'extra', data: { From 4d3ce79dc3e3269b60ed94d484a179b3c3dc5e69 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Tue, 16 Nov 2021 11:01:51 -0300 Subject: [PATCH 23/23] Adjust comment at configuration file --- .mocharc.definition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mocharc.definition.js b/.mocharc.definition.js index f7036b4d28f7..efffe16964d5 100644 --- a/.mocharc.definition.js +++ b/.mocharc.definition.js @@ -1,7 +1,7 @@ 'use strict'; /** - * Mocha configuration for client-side unit and integration tests. + * Mocha configuration for unit tests for type guards. */ const base = require('./.mocharc.base.json');