fix: Auto-translate doesn't work on E2E rooms (#30369)

Co-authored-by: Guilherme Jun Grillo <48109548+guijun13@users.noreply.github.com>
Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com>
pull/30617/head^2
Matheus Barbosa Silva 2 years ago committed by GitHub
parent 03315b2f68
commit 223dce18a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .changeset/popular-actors-cheat.md
  2. 9
      apps/meteor/app/autotranslate/server/methods/saveSettings.ts
  3. 6
      apps/meteor/app/channel-settings/server/functions/saveRoomEncrypted.ts
  4. 22
      apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts
  5. 19
      apps/meteor/client/views/room/Header/icons/Encrypted.tsx
  6. 18
      apps/meteor/client/views/room/contextualBar/AutoTranslate/AutoTranslate.tsx
  7. 20
      apps/meteor/client/views/room/contextualBar/AutoTranslate/AutoTranslateWithData.tsx
  8. 16
      apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
  9. 7
      apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
  10. 10
      apps/meteor/server/models/raw/Rooms.ts
  11. 8
      apps/meteor/server/models/raw/Subscriptions.ts
  12. 7
      apps/meteor/tests/e2e/e2e-encryption.spec.ts
  13. 6
      apps/meteor/tests/e2e/page-objects/home-channel.ts
  14. 31
      apps/meteor/tests/end-to-end/api/00-autotranslate.js
  15. 2
      packages/model-typings/src/models/IRoomsModel.ts
  16. 1
      packages/model-typings/src/models/ISubscriptionsModel.ts

@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/model-typings": patch
---
Do not allow auto-translation to be enabled in E2E rooms

@ -1,4 +1,4 @@
import { Subscriptions } from '@rocket.chat/models';
import { Subscriptions, Rooms } from '@rocket.chat/models';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
@ -46,6 +46,13 @@ Meteor.methods<ServerMethods>({
switch (field) {
case 'autoTranslate':
const room = await Rooms.findE2ERoomById(rid, { projection: { _id: 1 } });
if (room && value === '1') {
throw new Meteor.Error('error-e2e-enabled', 'Enabling auto-translation in E2E encrypted rooms is not allowed', {
method: 'saveAutoTranslateSettings',
});
}
await Subscriptions.updateAutoTranslateById(subscription._id, value === '1');
if (!subscription.autoTranslateLanguage && options.defaultLanguage) {
await Subscriptions.updateAutoTranslateLanguageById(subscription._id, options.defaultLanguage);

@ -1,7 +1,7 @@
import { Message } from '@rocket.chat/core-services';
import type { IUser } from '@rocket.chat/core-typings';
import { isRegisterUser } from '@rocket.chat/core-typings';
import { Rooms } from '@rocket.chat/models';
import { Rooms, Subscriptions } from '@rocket.chat/models';
import { Match } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
import type { UpdateResult } from 'mongodb';
@ -25,5 +25,9 @@ export const saveRoomEncrypted = async function (rid: string, encrypted: boolean
await Message.saveSystemMessage(type, rid, user.username, user);
}
if (encrypted) {
await Subscriptions.disableAutoTranslateByRoomId(rid);
}
return update;
};

@ -5,13 +5,15 @@ import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { e2e } from '../../../app/e2e/client/rocketchat.e2e';
import { useRoom } from '../../views/room/contexts/RoomContext';
import { dispatchToastMessage } from '../../lib/toast';
import { useRoom, useRoomSubscription } from '../../views/room/contexts/RoomContext';
import type { RoomToolboxActionConfig } from '../../views/room/contexts/RoomToolboxContext';
import { useReactiveValue } from '../useReactiveValue';
export const useE2EERoomAction = () => {
const enabled = useSetting('E2E_Enable', false);
const room = useRoom();
const subscription = useRoomSubscription();
const readyToEncrypt = useReactiveValue(useCallback(() => e2e.isReady(), [])) || room.encrypted;
const permittedToToggleEncryption = usePermission('toggle-room-e2e-encryption', room._id);
const permittedToEditRoom = usePermission('edit-room', room._id);
@ -21,8 +23,22 @@ export const useE2EERoomAction = () => {
const toggleE2E = useEndpoint('POST', '/v1/rooms.saveRoomSettings');
const action = useMutableCallback(() => {
void toggleE2E({ rid: room._id, encrypted: !room.encrypted });
const action = useMutableCallback(async () => {
const { success } = await toggleE2E({ rid: room._id, encrypted: !room.encrypted });
if (!success) {
return;
}
dispatchToastMessage({
type: 'success',
message: room.encrypted
? t('E2E_Encryption_disabled_for_room', { roomName: room.name })
: t('E2E_Encryption_enabled_for_room', { roomName: room.name }),
});
if (subscription?.autoTranslate) {
dispatchToastMessage({ type: 'success', message: t('AutoTranslate_Disabled_for_room', { roomName: room.name }) });
}
});
const enabledOnRoom = !!room.encrypted;

@ -2,20 +2,31 @@ import type { IRoom } from '@rocket.chat/core-typings';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import colors from '@rocket.chat/fuselage-tokens/colors';
import { HeaderState } from '@rocket.chat/ui-client';
import { useSetting, usePermission, useMethod, useTranslation } from '@rocket.chat/ui-contexts';
import { useSetting, usePermission, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import React, { memo } from 'react';
import { dispatchToastMessage } from '../../../../lib/toast';
const Encrypted = ({ room }: { room: IRoom }) => {
const t = useTranslation();
const e2eEnabled = useSetting('E2E_Enable');
const toggleE2E = useMethod('saveRoomSettings');
const toggleE2E = useEndpoint('POST', '/v1/rooms.saveRoomSettings');
const canToggleE2E = usePermission('toggle-room-e2e-encryption');
const encryptedLabel = canToggleE2E ? t('Encrypted_key_title') : t('Encrypted');
const handleE2EClick = useMutableCallback(() => {
const handleE2EClick = useMutableCallback(async () => {
if (!canToggleE2E) {
return;
}
toggleE2E(room._id, 'encrypted', !room?.encrypted);
const { success } = await toggleE2E({ rid: room._id, encrypted: !room.encrypted });
if (!success) {
return;
}
dispatchToastMessage({
type: 'success',
message: t('E2E_Encryption_disabled_for_room', { roomName: room.name }),
});
});
return e2eEnabled && room?.encrypted ? (
<HeaderState title={encryptedLabel} icon='key' onClick={handleE2EClick} color={colors.s500} tiny />

@ -1,4 +1,4 @@
import { FieldGroup, Field, FieldLabel, FieldRow, ToggleSwitch, Select } from '@rocket.chat/fuselage';
import { Callout, FieldGroup, Field, FieldLabel, FieldRow, ToggleSwitch, Select } from '@rocket.chat/fuselage';
import type { SelectOption } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement, ChangeEvent } from 'react';
@ -11,6 +11,7 @@ import {
ContextualbarIcon,
ContextualbarContent,
} from '../../../../components/Contextualbar';
import { useRoom } from '../../contexts/RoomContext';
type AutoTranslateProps = {
language: string;
@ -30,6 +31,7 @@ const AutoTranslate = ({
handleClose,
}: AutoTranslateProps): ReactElement => {
const t = useTranslation();
const room = useRoom();
return (
<>
@ -40,14 +42,24 @@ const AutoTranslate = ({
</ContextualbarHeader>
<ContextualbarContent pbs={24}>
<FieldGroup>
{room.encrypted && (
<Callout title={t('Automatic_translation_not_available')} type='warning'>
{t('Automatic_translation_not_available_info')}
</Callout>
)}
<Field>
<FieldRow>
<ToggleSwitch id='automatic-translation' onChange={handleSwitch} defaultChecked={translateEnable} />
<ToggleSwitch
id='automatic-translation'
onChange={handleSwitch}
defaultChecked={translateEnable}
disabled={room.encrypted && !translateEnable}
/>
<FieldLabel htmlFor='automatic-translation'>{t('Automatic_Translation')}</FieldLabel>
</FieldRow>
</Field>
<Field>
<FieldLabel htmlFor='language'>{t('Language')}</FieldLabel>
<FieldLabel htmlFor='translate-to'>{t('Translate_to')}</FieldLabel>
<FieldRow verticalAlign='middle'>
<Select
id='language'

@ -2,9 +2,11 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useLanguage } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { useMemo, useEffect, useState, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useEndpointAction } from '../../../../hooks/useEndpointAction';
import { useEndpointData } from '../../../../hooks/useEndpointData';
import { dispatchToastMessage } from '../../../../lib/toast';
import { useRoom, useRoomSubscription } from '../../contexts/RoomContext';
import { useRoomToolbox } from '../../contexts/RoomToolboxContext';
import AutoTranslate from './AutoTranslate';
@ -16,10 +18,12 @@ const AutoTranslateWithData = (): ReactElement => {
const userLanguage = useLanguage();
const [currentLanguage, setCurrentLanguage] = useState(subscription?.autoTranslateLanguage ?? '');
const saveSettings = useEndpointAction('POST', '/v1/autotranslate.saveSettings');
const { t } = useTranslation();
const { value: translateData } = useEndpointData('/v1/autotranslate.getSupportedLanguages', {
params: useMemo(() => ({ targetLanguage: userLanguage }), [userLanguage]),
});
const languagesDict = translateData ? Object.fromEntries(translateData.languages.map((lang) => [lang.language, lang.name])) : {};
const handleChangeLanguage = useMutableCallback((value) => {
setCurrentLanguage(value);
@ -29,6 +33,10 @@ const AutoTranslateWithData = (): ReactElement => {
field: 'autoTranslateLanguage',
value,
});
dispatchToastMessage({
type: 'success',
message: t('AutoTranslate_language_set_to', { language: languagesDict[value] }),
});
});
const handleSwitch = useMutableCallback((event) => {
@ -37,6 +45,18 @@ const AutoTranslateWithData = (): ReactElement => {
field: 'autoTranslate',
value: event.target.checked,
});
dispatchToastMessage({
type: 'success',
message: event.target.checked
? t('AutoTranslate_Enabled_for_room', { roomName: room.name })
: t('AutoTranslate_Disabled_for_room', { roomName: room.name }),
});
if (event.target.checked && currentLanguage) {
dispatchToastMessage({
type: 'success',
message: t('AutoTranslate_language_set_to', { language: languagesDict[currentLanguage] }),
});
}
});
useEffect(() => {

@ -705,6 +705,8 @@
"Authorization_URL": "Authorization URL",
"Authorize": "Authorize",
"Authorize_access_to_your_account": "Authorize access to your account",
"Automatic_translation_not_available": "Automatic translation not available",
"Automatic_translation_not_available_info": "This room has E2E encryption enabled, translation cannot work with encrypted messages",
"Auto_Load_Images": "Auto Load Images",
"Auto_Selection": "Auto Selection",
"Auto_Translate": "Auto-Translate",
@ -715,11 +717,14 @@
"AutoTranslate_APIKey": "API Key",
"AutoTranslate_Change_Language_Description": "Changing the auto-translate language does not translate previous messages.",
"AutoTranslate_DeepL": "DeepL",
"AutoTranslate_Disabled_for_room": "Auto-translate disabled for #{{roomName}}",
"AutoTranslate_Enabled": "Enable Auto-Translate",
"AutoTranslate_Enabled_Description": "Enabling auto-translation will allow people with the `auto-translate` permission to have all messages automatically translated into their selected language. Fees may apply.",
"AutoTranslate_Enabled_for_room": "Auto-translate enabled for #{{roomName}}",
"AutoTranslate_AutoEnableOnJoinRoom": "Auto-Translate for non-default language members",
"AutoTranslate_AutoEnableOnJoinRoom_Description": "If enabled, whenever a user with a language preference different than the workspace default joins a room, it will be automatically translated for them.",
"AutoTranslate_Google": "Google",
"AutoTranslate_language_set_to": "Auto-translate language set to {{language}}",
"AutoTranslate_Microsoft": "Microsoft",
"AutoTranslate_Microsoft_API_Key": "Ocp-Apim-Subscription-Key",
"AutoTranslate_ServiceProvider": "Service Provider",
@ -1676,7 +1681,7 @@
"Discussion": "Discussion",
"Discussion_Description": "Discussions are an additional way to organize conversations that allows inviting users from outside channels to participate in specific conversations.",
"Discussion_description": "Help keep an overview of what's going on! By creating a discussion, a sub-channel of the one you selected is created and both are linked.",
"Discussion_first_message_disabled_due_to_e2e": "You can start sending End-to-End encrypted messages in this discussion after its creation.",
"Discussion_first_message_disabled_due_to_e2e": "You can start sending End-to-end encrypted messages in this discussion after its creation.",
"Discussion_first_message_title": "Your message",
"Discussion_name": "Discussion name",
"Discussion_start": "Start a Discussion",
@ -1739,6 +1744,8 @@
"Markdown_Marked_Tables": "Enable Marked Tables",
"duplicated-account": "Duplicated account",
"E2E Encryption": "E2E Encryption",
"E2E_Encryption_enabled_for_room": "End-to-end encryption enabled for #{{roomName}}",
"E2E_Encryption_disabled_for_room": "End-to-end encryption disabled for #{{roomName}}",
"Markdown_Parser": "Markdown Parser",
"Markdown_SupportSchemesForLink": "Markdown Support Schemes for Link",
"E2E Encryption_Description": "Keep conversations private, ensuring only the sender and intended recipients are able to read them.",
@ -1751,7 +1758,7 @@
"E2E_Enabled_Default_DirectRooms": "Enable encryption for Direct Rooms by default",
"E2E_Enabled_Default_PrivateRooms": "Enable encryption for Private Rooms by default",
"E2E_Encryption_Password_Change": "Change Encryption Password",
"E2E_Encryption_Password_Explanation": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.<br/><br/>This is end to end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store your password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on.",
"E2E_Encryption_Password_Explanation": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.<br/><br/>This is end-to-end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store your password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on.",
"E2E_key_reset_email": "E2E Key Reset Notification",
"E2E_message_encrypted_placeholder": "This message is end-to-end encrypted. To view it, you must enter your encryption key in your account settings.",
"E2E_password_request_text": "To access your encrypted private groups and direct messages, enter your encryption password. <br/>You need to enter this password to encode/decode your messages on every client you use, since the key is not stored on the server.",
@ -1876,7 +1883,7 @@
"Enable_unlimited_apps": "Enable unlimited apps",
"Enabled": "Enabled",
"Encrypted": "Encrypted",
"Encrypted_channel_Description": "End to end encrypted channel. Search will not work with encrypted channels and notifications may not show the messages content.",
"Encrypted_channel_Description": "End-to-end encrypted channel. Search will not work with encrypted channels and notifications may not show the messages content.",
"Encrypted_key_title": "Click here to disable end-to-end encryption for this channel (requires e2ee-permission)",
"Encrypted_message": "Encrypted message",
"Encrypted_setting_changed_successfully": "Encrypted setting changed successfully",
@ -4986,7 +4993,7 @@
"Teams_New_Description_Label": "Topic",
"Teams_New_Description_Placeholder": "What is this team about",
"Teams_New_Encrypted_Description_Disabled": "Only available for private team",
"Teams_New_Encrypted_Description_Enabled": "End to end encrypted team. Search will not work with encrypted Teams and notifications may not show the messages content.",
"Teams_New_Encrypted_Description_Enabled": "End-to-end encrypted team. Search will not work with encrypted Teams and notifications may not show the messages content.",
"Teams_New_Encrypted_Label": "Encrypted",
"Teams_New_Private_Description_Disabled": "When disabled, anyone can join the team",
"Teams_New_Private_Description_Enabled": "Only invited people can join",
@ -5185,6 +5192,7 @@
"Transferred": "Transferred",
"Translate": "Translate",
"Translated": "Translated",
"Translate_to": "Translate to",
"Translations": "Translations",
"Travel_and_Places": "Travel & Places",
"Trigger_removed": "Trigger removed",

@ -621,6 +621,8 @@
"Author_Site": "Página do autor",
"Authorization_URL": "URL de autorização",
"Authorize": "Autorizar",
"Automatic_translation_not_available": "Tradução automática indisponível",
"Automatic_translation_not_available_info": "Esta sala tem criptografia E2E ativada, a tradução não pode funcionar com mensagens criptografadas",
"Auto_Load_Images": "Carregar imagens automaticamente",
"Auto_Selection": "Seleção automática",
"Auto_Translate": "Traduzir automaticamente",
@ -631,9 +633,12 @@
"AutoTranslate_APIKey": "Key da API",
"AutoTranslate_Change_Language_Description": "Alterar o idioma de tradução automática não traduz mensagens anteriores.",
"AutoTranslate_DeepL": "DeepL",
"AutoTranslate_Disabled_for_room": "Tradução automática desabilitada para #{{roomName}}",
"AutoTranslate_Enabled": "Habilitar tradução automática",
"AutoTranslate_Enabled_Description": "Habilitar a tradução automática implicará em permitir que as pessoas com a permissão `auto-translate` tenham todas as suas mensagens automaticamente traduzidas para seu idioma. Taxas podem ser cobradas.",
"AutoTranslate_Enabled_for_room": "Tradução automática habilitada para #{{roomName}}",
"AutoTranslate_Google": "Google",
"AutoTranslate_language_set_to": "Linguagem para tradução automática definida como {{language}}",
"AutoTranslate_Microsoft": "Microsoft",
"AutoTranslate_Microsoft_API_Key": "Ocp-Apim-Subscription-Key",
"AutoTranslate_ServiceProvider": "Provedor de serviço",
@ -1515,6 +1520,7 @@
"Markdown_Marked_Tables": "Ativar tabelas marcadas",
"duplicated-account": "Conta duplicada",
"E2E Encryption": "Criptografia E2E",
"E2E_Encryption_enabled_for_room": "Criptografia E2E habilitada para #{{roomName}}",
"Markdown_Parser": "Parser de marcação",
"Markdown_SupportSchemesForLink": "Esquemas de links compatíveis com marcação",
"Markdown_SupportSchemesForLink_Description": "Lista de esquemas permitidos separados por vírgulas",
@ -4336,6 +4342,7 @@
"Transferred": "Transferido",
"Translate": "Traduzir",
"Translated": "Traduzido",
"Translate_to": "Traduzir para",
"Translations": "Traduções",
"Travel_and_Places": "Viagem e Locais",
"Trigger_removed": "Gatilho removido",

@ -678,6 +678,16 @@ export class RoomsRaw extends BaseRaw<IRoom> implements IRoomsModel {
);
}
findE2ERoomById(roomId: IRoom['_id'], options: FindOptions<IRoom> = {}): Promise<IRoom | null> {
return this.findOne(
{
_id: roomId,
encrypted: true,
},
options,
);
}
findRoomsInsideTeams(autoJoin = false): FindCursor<IRoom> {
return this.find({
teamId: { $exists: true },

@ -604,6 +604,14 @@ export class SubscriptionsRaw extends BaseRaw<ISubscription> implements ISubscri
return this.updateOne(query, update);
}
disableAutoTranslateByRoomId(roomId: IRoom['_id']): Promise<UpdateResult | Document> {
const query = {
rid: roomId,
};
return this.updateMany(query, { $unset: { autoTranslate: 1 } });
}
updateAutoTranslateLanguageById(_id: string, autoTranslateLanguage: string): Promise<UpdateResult> {
const query = {
_id,

@ -154,8 +154,7 @@ test.describe.serial('e2e-encryption', () => {
await expect(page).toHaveURL(`/group/${channelName}`);
await poHomeChannel.toastSuccess.locator('button >> i.rcx-icon--name-cross.rcx-icon').click();
await page.mouse.move(0, 0);
await poHomeChannel.dismissToast();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();
@ -168,6 +167,7 @@ test.describe.serial('e2e-encryption', () => {
await expect(poHomeChannel.tabs.btnDisableE2E).toBeVisible();
await poHomeChannel.tabs.btnDisableE2E.click({ force: true });
await poHomeChannel.dismissToast();
await page.waitForTimeout(1000);
await poHomeChannel.content.sendMessage('hello world not encrypted');
@ -178,6 +178,7 @@ test.describe.serial('e2e-encryption', () => {
await poHomeChannel.tabs.kebab.click({ force: true });
await expect(poHomeChannel.tabs.btnEnableE2E).toBeVisible();
await poHomeChannel.tabs.btnEnableE2E.click({ force: true });
await poHomeChannel.dismissToast();
await page.waitForTimeout(1000);
await poHomeChannel.content.sendMessage('hello world encrypted again');
@ -197,7 +198,7 @@ test.describe.serial('e2e-encryption', () => {
await expect(poHomeChannel.toastSuccess).toBeVisible();
await poHomeChannel.toastSuccess.locator('button >> i.rcx-icon--name-cross.rcx-icon').click();
await poHomeChannel.dismissToast();
await poHomeChannel.tabs.kebab.click({ force: true });
await expect(poHomeChannel.tabs.btnEnableE2E).toBeVisible();

@ -34,4 +34,10 @@ export class HomeChannel {
await expect(this.page.locator('role=main >> .rcx-skeleton')).toHaveCount(0);
await expect(this.page.locator('role=main >> role=list')).not.toHaveAttribute('aria-busy', 'true');
}
async dismissToast() {
// this is a workaround for when the toast is blocking the click of the button
await this.toastSuccess.locator('button >> i.rcx-icon--name-cross.rcx-icon').click();
await this.page.mouse.move(0, 0);
}
}

@ -4,7 +4,7 @@ import { before, describe, after, it } from 'mocha';
import { getCredentials, api, request, credentials } from '../../data/api-data.js';
import { sendSimpleMessage } from '../../data/chat.helper';
import { updatePermission, updateSetting } from '../../data/permissions.helper';
import { createRoom } from '../../data/rooms.helper';
import { createRoom, deleteRoom } from '../../data/rooms.helper';
import { password } from '../../data/user';
import { createUser, login } from '../../data/users.helper.js';
@ -71,6 +71,18 @@ describe('AutoTranslate', function () {
});
});
describe('[/autotranslate.saveSettings', () => {
let testGroupId;
before(async () => {
await updateSetting('E2E_Enable', true);
await updateSetting('E2E_Enabled_Default_PrivateRooms', true);
const res = await createRoom({ type: 'p', name: `e2etest-autotranslate-${Date.now()}` });
testGroupId = res.body.group._id;
});
after(async () => {
await updateSetting('E2E_Enabled_Default_PrivateRooms', false);
await updateSetting('E2E_Enable', false);
await deleteRoom({ type: 'p', roomId: testGroupId });
});
it('should throw an error when the "AutoTranslate_Enabled" setting is disabled', (done) => {
updateSetting('AutoTranslate_Enabled', false).then(() => {
request
@ -223,6 +235,23 @@ describe('AutoTranslate', function () {
})
.end(done);
});
it('should throw an error when E2E encryption is enabled', async () => {
await request
.post(api('autotranslate.saveSettings'))
.set(credentials)
.send({
roomId: testGroupId,
field: 'autoTranslate',
defaultLanguage: 'en',
value: true,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-e2e-enabled');
});
});
it('should return success when the setting is saved correctly', (done) => {
request
.post(api('autotranslate.saveSettings'))

@ -130,6 +130,8 @@ export interface IRoomsModel extends IBaseModel<IRoom> {
findByE2E(options?: FindOptions<IRoom>): FindCursor<IRoom>;
findE2ERoomById(roomId: IRoom['_id'], options?: FindOptions<IRoom>): Promise<IRoom | null>;
findRoomsInsideTeams(autoJoin?: boolean): FindCursor<IRoom>;
findOneDirectRoomContainingAllUserIDs(uid: IDirectMessageRoom['uids'], options?: FindOptions<IRoom>): Promise<IRoom | null>;

@ -131,6 +131,7 @@ export interface ISubscriptionsModel extends IBaseModel<ISubscription> {
findByUserId(userId: string, options?: FindOptions<ISubscription>): FindCursor<ISubscription>;
cachedFindByUserId(userId: string, options?: FindOptions<ISubscription>): FindCursor<ISubscription>;
updateAutoTranslateById(_id: string, autoTranslate: boolean): Promise<UpdateResult>;
disableAutoTranslateByRoomId(roomId: IRoom['_id']): Promise<UpdateResult | Document>;
findAlwaysNotifyDesktopUsersByRoomId(roomId: string): FindCursor<ISubscription>;
findOneByRoomNameAndUserId(roomName: string, userId: string): Promise<ISubscription | null>;

Loading…
Cancel
Save