Skip to content

Commit

Permalink
fix: UsersInRole not displaying all available rooms for admin (#32320)
Browse files Browse the repository at this point in the history
  • Loading branch information
dougfabris committed May 16, 2024
1 parent 776b3c3 commit c29c69e
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 91 deletions.
16 changes: 0 additions & 16 deletions apps/meteor/client/components/RoomAutoComplete/Avatar.tsx

This file was deleted.

45 changes: 30 additions & 15 deletions apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,49 @@ import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import { RoomAvatar } from '@rocket.chat/ui-avatar';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { ReactElement, ComponentProps } from 'react';
import type { ComponentProps } from 'react';
import React, { memo, useMemo, useState } from 'react';

import Avatar from './Avatar';

const generateQuery = (
term = '',
): {
selector: string;
} => ({ selector: JSON.stringify({ name: term }) });

type RoomAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
type RoomAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'> & { scope?: 'admin' | 'regular' };

const AVATAR_SIZE = 'x20';

const RoomAutoComplete = ({ value, onChange, ...props }: RoomAutoCompleteProps): ReactElement => {
const ROOM_AUTOCOMPLETE_PARAMS = {
admin: {
endpoint: '/v1/rooms.autocomplete.adminRooms',
key: 'roomsAutoCompleteAdmin',
},
regular: {
endpoint: '/v1/rooms.autocomplete.channelAndPrivate',
key: 'roomsAutoCompleteRegular',
},
} as const;

const RoomAutoComplete = ({ value, onChange, scope = 'regular', ...props }: RoomAutoCompleteProps) => {
const [filter, setFilter] = useState('');
const filterDebounced = useDebouncedValue(filter, 300);
const autocomplete = useEndpoint('GET', '/v1/rooms.autocomplete.channelAndPrivate');
const roomsAutoCompleteEndpoint = useEndpoint('GET', ROOM_AUTOCOMPLETE_PARAMS[scope].endpoint);

const result = useQuery(['rooms.autocomplete.channelAndPrivate', filterDebounced], () => autocomplete(generateQuery(filterDebounced)), {
keepPreviousData: true,
});
const result = useQuery(
[ROOM_AUTOCOMPLETE_PARAMS[scope].key, filterDebounced],
() => roomsAutoCompleteEndpoint(generateQuery(filterDebounced)),
{
keepPreviousData: true,
},
);

const options = useMemo(
() =>
result.isSuccess
? result.data.items.map(({ name, _id, avatarETag, t }) => ({
? result.data.items.map(({ name, fname, _id, avatarETag, t }) => ({
value: _id,
label: { name, avatarETag, type: t },
label: { name: fname || name, avatarETag, type: t },
}))
: [],
[result.data?.items, result.isSuccess],
Expand All @@ -43,18 +58,18 @@ const RoomAutoComplete = ({ value, onChange, ...props }: RoomAutoCompleteProps):
onChange={onChange}
filter={filter}
setFilter={setFilter}
renderSelected={({ selected: { value, label } }): ReactElement => (
renderSelected={({ selected: { value, label } }) => (
<>
<Box margin='none' mi={2}>
<RoomAvatar size='x20' room={{ type: label?.type || 'c', _id: value, ...label }} />
<RoomAvatar size={AVATAR_SIZE} room={{ type: label?.type || 'c', _id: value, ...label }} />
</Box>
<Box margin='none' mi={2}>
{label?.name}
</Box>
</>
)}
renderItem={({ value, label, ...props }): ReactElement => (
<Option key={value} {...props} label={label.name} avatar={<Avatar value={value} {...label} />} />
renderItem={({ value, label, ...props }) => (
<Option {...props} label={label.name} avatar={<RoomAvatar size={AVATAR_SIZE} room={{ _id: value, ...label }} />} />
)}
options={options}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const UsersInRolePage = ({ role }: { role: IRole }): ReactElement => {
control={control}
name='rid'
render={({ field: { onChange, value } }): ReactElement => (
<RoomAutoComplete value={value} onChange={onChange} placeholder={t('User')} />
<RoomAutoComplete scope='admin' value={value} onChange={onChange} placeholder={t('Room')} />
)}
/>
</FieldRow>
Expand Down

This file was deleted.

12 changes: 6 additions & 6 deletions apps/meteor/ee/client/views/audit/components/tabs/RoomsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { Field, FieldLabel, FieldRow, FieldError } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React from 'react';
import type { UseFormReturn } from 'react-hook-form';
import { useController } from 'react-hook-form';

import RoomAutoComplete from '../../../../../../client/components/RoomAutoComplete';
import type { AuditFields } from '../../hooks/useAuditForm';
import RoomAutoComplete from '../forms/RoomAutoComplete';

type RoomsTabProps = {
form: UseFormReturn<AuditFields>;
};

const RoomsTab = ({ form: { control } }: RoomsTabProps): ReactElement => {
const { field: ridField, fieldState: ridFieldState } = useController({ name: 'rid', control, rules: { required: true } });

const RoomsTab = ({ form: { control } }: RoomsTabProps) => {
const t = useTranslation();

const { field: ridField, fieldState: ridFieldState } = useController({ name: 'rid', control, rules: { required: true } });

return (
<Field flexShrink={1}>
<FieldLabel>{t('Channel_name')}</FieldLabel> {/* TODO: should it be `Room_name`? */}
<FieldLabel>{t('Channel_name')}</FieldLabel>
<FieldRow>
<RoomAutoComplete
scope='admin'
value={ridField.value}
error={!!ridFieldState.error}
placeholder={t('Channel_Name_Placeholder')}
Expand Down
21 changes: 21 additions & 0 deletions apps/meteor/tests/e2e/administration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,27 @@ test.describe.parallel('administration', () => {
await poAdmin.btnCreateRole.click();
await page.waitForSelector('role=dialog[name="Custom roles"]');
});

test.describe('Users in role', () => {
const channelName = faker.string.uuid();
test.beforeAll(async ({ api }) => {
// TODO: refactor createChannel utility in order to get channel data when creating
const response = await api.post('/channels.create', { name: channelName, members: ['user1'] });
const { channel } = await response.json();

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

test('admin should be able to get the owners of a room that wasnt created by him', async ({ page }) => {
await poAdmin.openRoleByName('Owner').click();
await poAdmin.btnUsersInRole.click();
await poAdmin.inputRoom.fill(channelName);
await page.getByRole('option', { name: channelName }).click();

await expect(poAdmin.getUserRowByUsername('user1')).toBeVisible();
})
})
});

test.describe('Mailer', () => {
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/tests/e2e/fixtures/userStates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BASE_URL } from '../config/constants';

export type IUserState = {
data: {
_id: string;
username: string;
loginToken: string;
loginExpire: Date;
Expand All @@ -27,6 +28,7 @@ function generateContext(username: string): IUserState {

return {
data: {
_id: username,
username,
loginToken: token.token,
loginExpire: token.when,
Expand Down
20 changes: 18 additions & 2 deletions apps/meteor/tests/e2e/page-objects/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,27 @@ export class Admin {
return this.page.locator('//label[@title="Assets_logo"]/following-sibling::span >> role=button[name="Delete"]');
}

get inputAssetsLogo(): Locator {
return this.page.locator('//label[@title="Assets_logo"]/following-sibling::span >> input[type="file"]');
}

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

get inputAssetsLogo(): Locator {
return this.page.locator('//label[@title="Assets_logo"]/following-sibling::span >> input[type="file"]');
openRoleByName(name: string): Locator {
return this.page.getByRole('table').getByRole('button', { name });
}

get btnUsersInRole(): Locator {
return this.page.getByRole('dialog').getByRole('button', { name: 'Users in role' });
}

get inputRoom(): Locator {
return this.page.locator('input[placeholder="Room"]');
}

getUserRowByUsername(username: string): Locator {
return this.page.locator('tr', { hasText: username })
}
}

0 comments on commit c29c69e

Please sign in to comment.