feat(web): меню модерации участника в списке сервера
Банить и исключать нужно того, кто прямо сейчас в сервере, а не только разбирать готовый список банов (AGENT.md 7.17). - `MemberModerationMenu`: тайм-аут (1/10/60/1440 минут и снятие), бан с причиной и исключение; пункты скрыты без прав KICK_MEMBERS/BAN_MEMBERS/TIMEOUT_MEMBERS, себя модерировать нельзя; - после действия обновляются списки участников и банов (без F5); - тесты: бан с причиной, тайм-аут, исключение и скрытие меню без прав.
This commit is contained in:
@@ -103,7 +103,21 @@ function routes(permissions: string[] = ['BAN_MEMBERS', 'VIEW_AUDIT_LOG']): Fetc
|
||||
},
|
||||
{ match: '/api/v1/users/@me', response: () => json({ user }) },
|
||||
{ match: '/api/v1/instance', response: () => json({ instance: { name: 'glchat-test' } }) },
|
||||
{ match: '/api/v1/guilds/g-1/members', response: () => json({ members: [] }) },
|
||||
{
|
||||
match: '/api/v1/guilds/g-1/members',
|
||||
response: () =>
|
||||
json({
|
||||
members: [
|
||||
{
|
||||
user_id: 'u-7',
|
||||
username: 'spammer',
|
||||
display_name: 'Spammer',
|
||||
status: 'offline',
|
||||
role_ids: [],
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{ match: '/api/v1/guilds/g-1/roles', response: () => json({ roles: [] }) },
|
||||
{ match: '/api/v1/guilds/g-1', response: () => json({ guild: guildDetail(permissions) }) },
|
||||
];
|
||||
@@ -183,6 +197,65 @@ describe('модерация: баны', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('модерация: меню участника', () => {
|
||||
it('банит участника с причиной из меню', async () => {
|
||||
const { dialog, fetchMock } = await openServerSettings();
|
||||
|
||||
await userEvent.click(await within(dialog).findByTestId('member-moderate-u-7'));
|
||||
await userEvent.type(screen.getByTestId('member-ban-reason-u-7'), 'реклама');
|
||||
await userEvent.click(await screen.findByTestId('member-ban-u-7'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(lastRequest(fetchMock, '/guilds/g-1/bans/u-7', 'PUT')).toBeDefined();
|
||||
});
|
||||
const body = bodyOf(lastRequest(fetchMock, '/guilds/g-1/bans/u-7', 'PUT')) as {
|
||||
reason?: string;
|
||||
};
|
||||
expect(body.reason).toBe('реклама');
|
||||
});
|
||||
|
||||
it('ставит тайм-аут участнику из меню', async () => {
|
||||
const { dialog, fetchMock } = await openServerSettings([
|
||||
'BAN_MEMBERS',
|
||||
'KICK_MEMBERS',
|
||||
'TIMEOUT_MEMBERS',
|
||||
'VIEW_AUDIT_LOG',
|
||||
]);
|
||||
await userEvent.click(await within(dialog).findByTestId('member-moderate-u-7'));
|
||||
|
||||
await userEvent.click(await screen.findByTestId('member-timeout-60'));
|
||||
await waitFor(() => {
|
||||
expect(lastRequest(fetchMock, '/guilds/g-1/members/u-7', 'PATCH')).toBeDefined();
|
||||
});
|
||||
const body = bodyOf(lastRequest(fetchMock, '/guilds/g-1/members/u-7', 'PATCH')) as {
|
||||
timeout_until?: string;
|
||||
};
|
||||
expect(new Date(body.timeout_until ?? '').getTime()).toBeGreaterThan(Date.now());
|
||||
});
|
||||
|
||||
it('исключает участника из меню', async () => {
|
||||
const { dialog, fetchMock } = await openServerSettings([
|
||||
'BAN_MEMBERS',
|
||||
'KICK_MEMBERS',
|
||||
'TIMEOUT_MEMBERS',
|
||||
'VIEW_AUDIT_LOG',
|
||||
]);
|
||||
await userEvent.click(await within(dialog).findByTestId('member-moderate-u-7'));
|
||||
|
||||
await userEvent.click(await screen.findByTestId('member-kick-u-7'));
|
||||
await waitFor(() => {
|
||||
expect(lastRequest(fetchMock, '/guilds/g-1/members/u-7', 'DELETE')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('скрывает меню без прав модерации', async () => {
|
||||
const { dialog } = await openServerSettings(['VIEW_AUDIT_LOG']);
|
||||
|
||||
await within(dialog).findByTestId('guild-members');
|
||||
expect(within(dialog).queryByTestId('member-moderate-u-7')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('модерация: журнал аудита', () => {
|
||||
it('показывает записи и отправляет фильтры в запрос', async () => {
|
||||
const { dialog, fetchMock } = await openServerSettings();
|
||||
|
||||
Reference in New Issue
Block a user