import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, it, vi } from 'vitest';
import { App } from '../app';
import { ServerProvider } from '../context/server-context';
import { ApiClient } from '../lib/api';
import { I18nProvider } from '../lib/i18n';
describe('player action confirmation', () => {
it('uses the server-issued preview token for the second-stage mutation', async () => {
localStorage.setItem('xfesm.locale', 'en-US');
location.hash = '/players';
const client = new ApiClient();
vi.spyOn(client, 'session').mockResolvedValue({
authenticated: true,
csrfToken: 'csrf',
actor: { id: 'owner-id', displayName: 'owner', roles: ['owner'], permissions: [], totpVerified: true },
});
vi.spyOn(client, 'status').mockResolvedValue({ sampledAt: '2026-01-01T00:00:00Z', uptimeSeconds: 1, playersOnline: 1 });
vi.spyOn(client, 'features').mockResolvedValue({ consoleEnabled: false, moderationEnabled: false, claimsEnabled: false, worldTrackingEnabled: false, rollbackEnabled: false });
vi.spyOn(client, 'subscribe').mockReturnValue(() => undefined);
vi.spyOn(client, 'players').mockResolvedValue({
items: [{ uuid: '11111111-1111-1111-1111-111111111111', name: 'Alex', online: true }],
});
const action = vi.spyOn(client, 'playerAction')
.mockResolvedValueOnce({
id: 'preview-operation',
kind: 'player.kick',
state: 'PREVIEW_REQUIRED',
previewToken: 'server-signed-preview',
expiresAt: '2026-01-01T00:10:00Z',
targets: [{ uuid: '11111111-1111-1111-1111-111111111111', name: 'Alex' }],
})
.mockResolvedValueOnce({ id: 'accepted-operation', kind: 'player.kick', state: 'QUEUED' });
const user = userEvent.setup();
render(<I18nProvider><ServerProvider client={client}><App /></ServerProvider></I18nProvider>);
await user.click(await screen.findByRole('button', { name: 'Manage' }));
expect(screen.getByRole('dialog').parentElement?.parentElement).toBe(document.body);
await user.selectOptions(screen.getByLabelText('Action type'), 'kick');
await user.type(screen.getByLabelText(/Audit reason/), 'Rule violation');
await user.click(screen.getByRole('button', { name: 'Submit action' }));
expect(await screen.findByText(/Alex · 11111111/)).toBeInTheDocument();
expect(action).toHaveBeenNthCalledWith(1, '11111111-1111-1111-1111-111111111111', 'kick', {
reason: 'Rule violation',
value: undefined,
confirmedTargetUuid: '11111111-1111-1111-1111-111111111111',
previewToken: undefined,
});
await user.click(screen.getByRole('button', { name: 'Confirm' }));
await waitFor(() => expect(action).toHaveBeenCalledTimes(2));
expect(action).toHaveBeenNthCalledWith(2, '11111111-1111-1111-1111-111111111111', 'kick', {
reason: 'Rule violation',
value: undefined,
confirmedTargetUuid: '11111111-1111-1111-1111-111111111111',
previewToken: 'server-signed-preview',
});
});
it('sends the supported whitelist mode through the destructive server preview', async () => {
localStorage.setItem('xfesm.locale', 'en-US');
location.hash = '/players';
const client = new ApiClient();
vi.spyOn(client, 'session').mockResolvedValue({ authenticated: true, csrfToken: 'csrf', actor: { id: 'owner-id', displayName: 'owner', roles: ['owner'], permissions: [], totpVerified: true } });
vi.spyOn(client, 'status').mockResolvedValue({ sampledAt: '2026-01-01T00:00:00Z', uptimeSeconds: 1, playersOnline: 1 });
vi.spyOn(client, 'features').mockResolvedValue({ consoleEnabled: false, moderationEnabled: false, claimsEnabled: false, worldTrackingEnabled: false, rollbackEnabled: false });
vi.spyOn(client, 'subscribe').mockReturnValue(() => undefined);
vi.spyOn(client, 'players').mockResolvedValue({ items: [{ uuid: '11111111-1111-1111-1111-111111111111', name: 'Alex', online: true }] });
const action = vi.spyOn(client, 'playerAction').mockResolvedValue({ id: 'preview', kind: 'whitelist', state: 'PREVIEW_REQUIRED', previewToken: 'token' });
const user = userEvent.setup();
render(<I18nProvider><ServerProvider client={client}><App /></ServerProvider></I18nProvider>);
await user.click(await screen.findByRole('button', { name: 'Manage' }));
await user.selectOptions(screen.getByLabelText('Action type'), 'whitelist');
await user.selectOptions(screen.getByLabelText('Whitelist operation'), 'remove');
await user.type(screen.getByLabelText(/Audit reason/), 'Access revoked');
await user.click(screen.getByRole('button', { name: 'Submit action' }));
await waitFor(() => expect(action).toHaveBeenCalledOnce());
expect(action).toHaveBeenCalledWith('11111111-1111-1111-1111-111111111111', 'whitelist', {
reason: 'Access revoked',
value: 'remove',
confirmedTargetUuid: '11111111-1111-1111-1111-111111111111',
previewToken: undefined,
});
expect(await screen.findByRole('button', { name: 'Confirm' })).toBeInTheDocument();
});
});
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, it, vi } from 'vitest';
import { App } from '../app';
import { ServerProvider } from '../context/server-context';
import { ApiClient } from '../lib/api';
import { I18nProvider } from '../lib/i18n';
describe('player action confirmation', () => {
it('uses the server-issued preview token for the second-stage mutation', async () => {
localStorage.setItem('xfesm.locale', 'en-US');
location.hash = '/players';
const client = new ApiClient();
vi.spyOn(client, 'session').mockResolvedValue({
authenticated: true,
csrfToken: 'csrf',
actor: { id: 'owner-id', displayName: 'owner', roles: ['owner'], permissions: [], totpVerified: true },
});
vi.spyOn(client, 'status').mockResolvedValue({ sampledAt: '2026-01-01T00:00:00Z', uptimeSeconds: 1, playersOnline: 1 });
vi.spyOn(client, 'features').mockResolvedValue({ consoleEnabled: false, moderationEnabled: false, claimsEnabled: false, worldTrackingEnabled: false, rollbackEnabled: false });
vi.spyOn(client, 'subscribe').mockReturnValue(() => undefined);
vi.spyOn(client, 'players').mockResolvedValue({
items: [{ uuid: '11111111-1111-1111-1111-111111111111', name: 'Alex', online: true }],
});
const action = vi.spyOn(client, 'playerAction')
.mockResolvedValueOnce({
id: 'preview-operation',
kind: 'player.kick',
state: 'PREVIEW_REQUIRED',
previewToken: 'server-signed-preview',
expiresAt: '2026-01-01T00:10:00Z',
targets: [{ uuid: '11111111-1111-1111-1111-111111111111', name: 'Alex' }],
})
.mockResolvedValueOnce({ id: 'accepted-operation', kind: 'player.kick', state: 'QUEUED' });
const user = userEvent.setup();
render(<I18nProvider><ServerProvider client={client}><App /></ServerProvider></I18nProvider>);
await user.click(await screen.findByRole('button', { name: 'Manage' }));
expect(screen.getByRole('dialog').parentElement?.parentElement).toBe(document.body);
await user.selectOptions(screen.getByLabelText('Action type'), 'kick');
await user.type(screen.getByLabelText(/Audit reason/), 'Rule violation');
await user.click(screen.getByRole('button', { name: 'Submit action' }));
expect(await screen.findByText(/Alex · 11111111/)).toBeInTheDocument();
expect(action).toHaveBeenNthCalledWith(1, '11111111-1111-1111-1111-111111111111', 'kick', {
reason: 'Rule violation',
value: undefined,
confirmedTargetUuid: '11111111-1111-1111-1111-111111111111',
previewToken: undefined,
});
await user.click(screen.getByRole('button', { name: 'Confirm' }));
await waitFor(() => expect(action).toHaveBeenCalledTimes(2));
expect(action).toHaveBeenNthCalledWith(2, '11111111-1111-1111-1111-111111111111', 'kick', {
reason: 'Rule violation',
value: undefined,
confirmedTargetUuid: '11111111-1111-1111-1111-111111111111',
previewToken: 'server-signed-preview',
});
});
it('sends the supported whitelist mode through the destructive server preview', async () => {
localStorage.setItem('xfesm.locale', 'en-US');
location.hash = '/players';
const client = new ApiClient();
vi.spyOn(client, 'session').mockResolvedValue({ authenticated: true, csrfToken: 'csrf', actor: { id: 'owner-id', displayName: 'owner', roles: ['owner'], permissions: [], totpVerified: true } });
vi.spyOn(client, 'status').mockResolvedValue({ sampledAt: '2026-01-01T00:00:00Z', uptimeSeconds: 1, playersOnline: 1 });
vi.spyOn(client, 'features').mockResolvedValue({ consoleEnabled: false, moderationEnabled: false, claimsEnabled: false, worldTrackingEnabled: false, rollbackEnabled: false });
vi.spyOn(client, 'subscribe').mockReturnValue(() => undefined);
vi.spyOn(client, 'players').mockResolvedValue({ items: [{ uuid: '11111111-1111-1111-1111-111111111111', name: 'Alex', online: true }] });
const action = vi.spyOn(client, 'playerAction').mockResolvedValue({ id: 'preview', kind: 'whitelist', state: 'PREVIEW_REQUIRED', previewToken: 'token' });
const user = userEvent.setup();
render(<I18nProvider><ServerProvider client={client}><App /></ServerProvider></I18nProvider>);
await user.click(await screen.findByRole('button', { name: 'Manage' }));
await user.selectOptions(screen.getByLabelText('Action type'), 'whitelist');
await user.selectOptions(screen.getByLabelText('Whitelist operation'), 'remove');
await user.type(screen.getByLabelText(/Audit reason/), 'Access revoked');
await user.click(screen.getByRole('button', { name: 'Submit action' }));
await waitFor(() => expect(action).toHaveBeenCalledOnce());
expect(action).toHaveBeenCalledWith('11111111-1111-1111-1111-111111111111', 'whitelist', {
reason: 'Access revoked',
value: 'remove',
confirmedTargetUuid: '11111111-1111-1111-1111-111111111111',
previewToken: undefined,
});
expect(await screen.findByRole('button', { name: 'Confirm' })).toBeInTheDocument();
});
});