Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/loud-chairs-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/i18n': patch
'@rocket.chat/meteor': patch
---

Fixes some warnings on delete user modal when deleting a user who has owner role in some rooms.
92 changes: 49 additions & 43 deletions apps/meteor/client/components/ConfirmOwnerChangeModal.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Box } from '@rocket.chat/fuselage';
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
import type { ComponentPropsWithoutRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';

import GenericModal from './GenericModal';
import RawText from './RawText';

type ConfirmOwnerChangeModalProps = {
shouldChangeOwner: string[];
Expand All @@ -19,58 +19,64 @@ const ConfirmOwnerChangeModal = ({
onConfirm,
onCancel,
}: ConfirmOwnerChangeModalProps) => {
const { t } = useTranslation();
const getChangeOwnerRooms = useEffectEvent(() => {
if (shouldChangeOwner.length === 0) {
return '';
}

let changeOwnerRooms = '';
if (shouldChangeOwner.length > 0) {
if (shouldChangeOwner.length === 1) {
changeOwnerRooms = t('A_new_owner_will_be_assigned_automatically_to_the__roomName__room', {
roomName: shouldChangeOwner.pop(),
});
} else if (shouldChangeOwner.length <= 5) {
changeOwnerRooms = t('A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__', {
count: shouldChangeOwner.length,
rooms: shouldChangeOwner.join(', '),
});
} else {
changeOwnerRooms = t('A_new_owner_will_be_assigned_automatically_to__count__rooms', {
count: shouldChangeOwner.length,
});
return (
<Trans
i18nKey='A_new_owner_will_be_assigned_automatically_to_the__roomName__room'
values={{ roomName: shouldChangeOwner[0] }}
components={{ bold: <Box is='span' fontWeight='bold' /> }}
/>
);
}
if (shouldChangeOwner.length <= 5) {
return (
<Trans
i18nKey='A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__'
values={{ count: shouldChangeOwner.length, rooms: shouldChangeOwner.join(', ') }}
components={{ br: <br />, bold: <Box is='span' fontWeight='bold' /> }}
/>
);
}
return <Trans i18nKey='A_new_owner_will_be_assigned_automatically_to__count__rooms' values={{ count: shouldChangeOwner.length }} />;
});

const getRemovedRooms = useEffectEvent(() => {
if (shouldBeRemoved.length === 0) {
return '';
}
}

let removedRooms = '';
if (shouldBeRemoved.length > 0) {
if (shouldBeRemoved.length === 1) {
removedRooms = t('The_empty_room__roomName__will_be_removed_automatically', {
roomName: shouldBeRemoved.pop(),
});
} else if (shouldBeRemoved.length <= 5) {
removedRooms = t('__count__empty_rooms_will_be_removed_automatically__rooms__', {
count: shouldBeRemoved.length,
rooms: shouldBeRemoved.join(', '),
});
} else {
removedRooms = t('__count__empty_rooms_will_be_removed_automatically', {
count: shouldBeRemoved.length,
});
return (
<Trans
i18nKey='The_empty_room__roomName__will_be_removed_automatically'
values={{ roomName: shouldBeRemoved[0] }}
components={{ bold: <Box is='span' fontWeight='bold' /> }}
/>
);
}
if (shouldBeRemoved.length <= 5) {
return (
<Trans
i18nKey='__count__empty_rooms_will_be_removed_automatically__rooms__'
values={{ count: shouldBeRemoved.length, rooms: shouldBeRemoved.join(', ') }}
components={{ br: <br />, bold: <Box is='span' fontWeight='bold' /> }}
/>
);
}
}
return <Trans i18nKey='__count__empty_rooms_will_be_removed_automatically' values={{ count: shouldBeRemoved.length }} />;
});

return (
<GenericModal variant='danger' onClose={onCancel} onCancel={onCancel} confirmText={confirmText} onConfirm={onConfirm}>
{contentTitle}

{changeOwnerRooms && (
<Box marginBlock={16}>
<RawText>{changeOwnerRooms}</RawText>
</Box>
)}
{removedRooms && (
<Box marginBlock={16}>
<RawText>{removedRooms}</RawText>
</Box>
)}
{shouldChangeOwner && <Box marginBlock={16}>{getChangeOwnerRooms()}</Box>}
{shouldBeRemoved && <Box marginBlock={16}>{getRemovedRooms()}</Box>}
</GenericModal>
);
};
Expand Down
82 changes: 82 additions & 0 deletions apps/meteor/tests/e2e/administration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { faker } from '@faker-js/faker';
import type { IUser } from '@rocket.chat/apps-engine/definition/users';

import { IS_EE } from './config/constants';
import { Users } from './fixtures/userStates';
Expand Down Expand Up @@ -111,6 +112,87 @@ test.describe.parallel('administration', () => {
await expect(poAdmin.tabs.users.inputUserName).toHaveValue(username);
await expect(poAdmin.tabs.users.joinDefaultChannels).not.toBeVisible();
});

test.describe('Delete user', () => {
const nonEmptyChannelName = faker.string.uuid();
const emptyChannelName = faker.string.uuid();
let ownerUser: IUser;
let user: IUser;

test.beforeAll(async ({ api }) => {
const createUserResponse = await api.post('/users.create', {
email: faker.internet.email(),
name: faker.person.fullName(),
password: faker.internet.password(),
username: faker.internet.userName(),
});

expect(createUserResponse.status()).toBe(200);
Comment thread
yash-rajpal marked this conversation as resolved.
Outdated
user = (await createUserResponse.json()).user;

const createOwnerUserResponse = await api.post('/users.create', {
email: faker.internet.email(),
name: faker.person.fullName(),
password: faker.internet.password(),
username: faker.internet.userName(),
});

expect(createOwnerUserResponse.status()).toBe(200);
Comment thread
yash-rajpal marked this conversation as resolved.
Outdated
ownerUser = (await createOwnerUserResponse.json()).user;

// TODO: refactor createChannel utility in order to get channel data when creating
const response = await api.post('/channels.create', { name: nonEmptyChannelName, members: [ownerUser.username] });
const { channel: nonEmptyChannel } = await response.json();

await api.post('/channels.addOwner', { roomId: nonEmptyChannel._id, username: ownerUser.username });
await api.post('/channels.removeOwner', { roomId: nonEmptyChannel._id, userId: Users.admin.data._id });

// TODO: refactor createChannel utility in order to get channel data when creating
const res = await api.post('/groups.create', { name: emptyChannelName, members: [ownerUser.username] });
const { group: emptyRoom } = await res.json();

await api.post('/groups.addOwner', { roomId: emptyRoom._id, username: ownerUser.username });
await api.post('/groups.leave', { roomId: emptyRoom._id });
});

test('expect to show owner change modal, when deleting last owner of any room', async ({ page }) => {
await poAdmin.inputSearchUsers.type(ownerUser.username);
await poAdmin.getUserRow(ownerUser.username).click();
await poAdmin.tabs.users.btnMoreActions.click();
await poAdmin.tabs.users.btnDeleteUser.click();

await expect(page.getByRole('dialog', { name: 'Are you sure?' })).toBeVisible();

await page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click();

await expect(page.getByRole('dialog', { name: 'Are you sure?' })).toContainText(
`A new owner will be assigned automatically to the ${nonEmptyChannelName} room.`,
);
await expect(page.getByRole('dialog', { name: 'Are you sure?' })).toContainText(
`The empty room ${emptyChannelName} will be removed automatically.`,
);
await expect(page.getByRole('dialog').getByRole('button', { name: 'Delete' })).toBeVisible();

await page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click();

await expect(poUtils.toastBarSuccess).toBeVisible();
await expect(page.getByRole('heading', { name: 'No users' })).toBeVisible();
});

test('expect to delete user', async ({ page }) => {
await poAdmin.inputSearchUsers.type(user.username);
await poAdmin.getUserRow(user.username).click();
await poAdmin.tabs.users.btnMoreActions.click();
await poAdmin.tabs.users.btnDeleteUser.click();

await expect(page.getByRole('dialog', { name: 'Are you sure?' })).toBeVisible();

await page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click();

await expect(poUtils.toastBarSuccess).toBeVisible();
await expect(page.getByRole('heading', { name: 'No users' })).toBeVisible();
});
});
});

test.describe('Rooms', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ export class AdminFlextabUsers {
return this.page.locator('role=button[name="Add user"]');
}

get btnMoreActions(): Locator {
return this.page.locator('role=button[name="More"]');
}

get btnDeleteUser(): Locator {
return this.page.locator('role=menuitem[name="Delete"]');
}

get btnInvite(): Locator {
return this.page.locator('role=button[name="Invite"]');
}
Expand Down
10 changes: 5 additions & 5 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"500": "Internal Server Error",
"__agents__agents_and__count__conversations__period__": "{{agents}} agents and {{count}} conversations, {{period}}",
"__count__empty_rooms_will_be_removed_automatically": "{{count}} empty rooms will be removed automatically.",
"__count__empty_rooms_will_be_removed_automatically__rooms__": "{{count}} empty rooms will be removed automatically:<br/> {{rooms}}.",
"__count__empty_rooms_will_be_removed_automatically__rooms__": "<bold>{{count}}</bold> empty rooms will be removed automatically:<br/> {{rooms}}.",
"__count__message_pruned_one": "{{count}} message pruned",
"__count__message_pruned_other": "{{count}} messages pruned",
"__username__is_no_longer__role__defined_by__user_by_": "{{username}} is no longer {{role}} by {{user_by}}",
Expand Down Expand Up @@ -56,9 +56,9 @@
"A_cloud-based_platform_for_those_needing_a_plug-and-play_app": "A cloud-based platform for those needing a plug-and-play app.",
"Accessing_permissions": "Accessing permissions",
"A_new_owner_will_be_assigned_automatically_to__count__rooms": "A new owner will be assigned automatically to <span style=\"font-weight: bold;\">{{count}}</span> rooms.",
"A_new_owner_will_be_assigned_automatically_to_the__roomName__room": "A new owner will be assigned automatically to the <span style=\"font-weight: bold;\">{{roomName}}</span> room.",
"A_new_owner_will_be_assigned_automatically_to_the__roomName__room": "A new owner will be assigned automatically to the <bold>{{roomName}}</bold> room.",
"Account_SID": "Account SID",
"A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__": "A new owner will be assigned automatically to those <span style=\"font-weight: bold;\">{{count}}</span> rooms:<br/> {{rooms}}.",
"A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__": "A new owner will be assigned automatically to those <bold>{{count}}</bold> rooms:<br/> {{rooms}}.",
"A_secure_and_highly_private_self-managed_solution_for_conference_calls": "A secure and highly private self-managed solution for conference calls.",
"Accounts": "Accounts",
"A_workspace_admin_needs_to_install_and_configure_a_conference_call_app": "A workspace admin needs to install and configure a conference call app.",
Expand Down Expand Up @@ -5890,7 +5890,7 @@
"Texts": "Texts",
"Text": "Text",
"The_application_will_be_able_to": "<1>{{appName}}</1> will be able to:",
"The_empty_room__roomName__will_be_removed_automatically": "The empty room <span style=\"font-weight: bold;\">{{roomName}}</span> will be removed automatically.",
"The_empty_room__roomName__will_be_removed_automatically": "The empty room <bold>{{roomName}}</bold> will be removed automatically.",
"The_mobile_notifications_were_disabled_to_all_users_go_to_Admin_Push_to_enable_the_Push_Gateway_again": "The mobile notifications were disabled to all users, go to \"Admin > Push\" to enable the Push Gateway again",
"The_necessary_browser_permissions_for_location_sharing_are_not_granted": "The necessary browser permissions for location sharing are not granted",
"The_selected_user_is_not_a_monitor": "The selected user is not a monitor",
Expand Down Expand Up @@ -6022,7 +6022,7 @@
"Unique_ID_change_detected": "Unique ID change detected",
"Unknown_Import_State": "Unknown Import State",
"Unknown_User": "Unknown User",
"Unknown_contact_callout_description": "Unknown contact. This contact is not on the contact list.",
"Unknown_contact_callout_description": "Unknown contact. This contact is not on the contact list.",
"Unlimited": "Unlimited",
"Unmute": "Unmute",
"unpinning-not-allowed": "Unpinning is not allowed",
Expand Down