mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-14 00:55:01 +03:00
feat(frontend): confirm before stopping Xray and offer Start when it is stopped (#7104)
Stop sat next to Restart on the dashboard and fired on the first click. A mis-click cut every proxy connection, and when the panel itself is reached through that proxy the only way back was SSH (#7103). Stop now opens a confirmation with Cancel focused. When Xray is stopped the first button reads Start instead of Restart and Stop is disabled; in the crashed state Stop stays enabled, because setting the manual-stop flag is what halts the crash-recovery restart loop. Closes #7103
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { Status } from '@/models/status';
|
||||
import OverviewActionBar from '@/pages/index/OverviewActionBar';
|
||||
|
||||
import { renderWithProviders } from './test-utils';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
function renderBar(
|
||||
state: string,
|
||||
{
|
||||
onStopXray = noop,
|
||||
onRestartXray = noop,
|
||||
}: { onStopXray?: () => void; onRestartXray?: () => void } = {},
|
||||
) {
|
||||
const status = new Status();
|
||||
status.xray.state = state;
|
||||
renderWithProviders(
|
||||
<OverviewActionBar
|
||||
status={status}
|
||||
isMobile={false}
|
||||
accessLogEnable={false}
|
||||
panelVersion="v3.10.1"
|
||||
latestVersion="v3.10.1"
|
||||
updateAvailable={false}
|
||||
onStopXray={onStopXray}
|
||||
onRestartXray={onRestartXray}
|
||||
onOpenLogs={noop}
|
||||
onOpenXrayLogs={noop}
|
||||
onOpenAmneziaWGLogs={noop}
|
||||
onOpenConfig={noop}
|
||||
onOpenBackup={noop}
|
||||
onOpenSystemHistory={noop}
|
||||
onOpenXrayMetrics={noop}
|
||||
onOpenPanelUpdate={noop}
|
||||
onOpenVersionSwitch={noop}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
// Stop used to fire on the first click, so a mis-click killed every proxy connection (#7103).
|
||||
describe('OverviewActionBar stop confirmation', () => {
|
||||
it('does not stop Xray until the dialog is confirmed', async () => {
|
||||
const onStopXray = vi.fn();
|
||||
renderBar('running', { onStopXray });
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Stop' }));
|
||||
expect(await screen.findAllByText('Stop Xray?')).not.toHaveLength(0);
|
||||
expect(onStopXray).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
||||
expect(onStopXray).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Stop' }));
|
||||
await waitFor(() =>
|
||||
expect(screen.getAllByRole('button', { name: 'Stop Xray' })).not.toHaveLength(0),
|
||||
);
|
||||
fireEvent.click(screen.getAllByRole('button', { name: 'Stop Xray' }).at(-1)!);
|
||||
await waitFor(() => expect(onStopXray).toHaveBeenCalledOnce());
|
||||
});
|
||||
});
|
||||
|
||||
// Stop on a stopped core only errors and sets the manual-stop flag. In the crash
|
||||
// state Stop is the one dashboard control that halts the restart loop.
|
||||
describe('OverviewActionBar start / disabled stop', () => {
|
||||
it('offers Start and disables Stop when the core is stopped', () => {
|
||||
const onRestartXray = vi.fn();
|
||||
renderBar('stop', { onRestartXray });
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Restart' })).toBeNull();
|
||||
expect((screen.getByRole('button', { name: 'Stop' }) as HTMLButtonElement).disabled).toBe(true);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Start' }));
|
||||
expect(onRestartXray).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('keeps Restart and leaves Stop usable when the core errored', async () => {
|
||||
const onStopXray = vi.fn();
|
||||
renderBar('error', { onStopXray });
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Restart' })).toBeTruthy();
|
||||
const stop = screen.getByRole('button', { name: 'Stop' }) as HTMLButtonElement;
|
||||
expect(stop.disabled).toBe(false);
|
||||
fireEvent.click(stop);
|
||||
fireEvent.click(await screen.findByRole('button', { name: 'Stop Xray' }));
|
||||
await waitFor(() => expect(onStopXray).toHaveBeenCalledOnce());
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user