mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 07:37:15 +00:00
fix(frontend): preserve edited server drafts (#6156)
* fix(frontend): preserve edited server drafts * fix(frontend): retain Xray server projections * fix(frontend): keep draft controls internal * fix(frontend): rehydrate saved redacted settings * fix(frontend): order saved draft hydration * fix(frontend): preserve draft baselines on security saves --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { useAllSettings } from '@/api/queries/useAllSettings';
|
||||
import { keys } from '@/api/queryKeys';
|
||||
import { makeTestQueryClient } from '@/test/test-utils';
|
||||
import { HttpUtil, Msg } from '@/utils';
|
||||
|
||||
@@ -25,4 +26,80 @@ describe('useAllSettings', () => {
|
||||
await waitFor(() => expect(result.current.fetched).toBe(true));
|
||||
expect(result.current.allSetting.subJsonUserAgentRegex).toBe(subJsonUserAgentRegex);
|
||||
});
|
||||
|
||||
it('keeps an edited setting when a refetch returns older server data', async () => {
|
||||
const values = [
|
||||
{ webPort: 2053 },
|
||||
{ webPort: 2054 },
|
||||
];
|
||||
let index = 0;
|
||||
vi.spyOn(HttpUtil, 'post').mockImplementation(async () => new Msg(true, '', values[index++]));
|
||||
const queryClient = makeTestQueryClient();
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useAllSettings(), { wrapper });
|
||||
|
||||
await waitFor(() => expect(result.current.fetched).toBe(true));
|
||||
act(() => result.current.updateSetting({ webPort: 3000 }));
|
||||
await queryClient.invalidateQueries({ queryKey: keys.settings.all() });
|
||||
|
||||
await waitFor(() => expect(HttpUtil.post).toHaveBeenCalledTimes(2));
|
||||
expect(result.current.allSetting.webPort).toBe(3000);
|
||||
expect(result.current.saveDisabled).toBe(false);
|
||||
});
|
||||
|
||||
it('hydrates redacted secrets after a successful save', async () => {
|
||||
let fetchCount = 0;
|
||||
vi.spyOn(HttpUtil, 'post').mockImplementation(async (url) => {
|
||||
if (url === '/panel/api/setting/all') {
|
||||
fetchCount += 1;
|
||||
return new Msg(true, '', fetchCount === 1 ? { hasTgBotToken: false } : { hasTgBotToken: true, tgBotToken: '' });
|
||||
}
|
||||
return new Msg(true, '');
|
||||
});
|
||||
const queryClient = makeTestQueryClient();
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useAllSettings(), { wrapper });
|
||||
|
||||
await waitFor(() => expect(result.current.fetched).toBe(true));
|
||||
act(() => result.current.updateSetting({ tgBotToken: 'secret' }));
|
||||
await act(async () => {
|
||||
await result.current.saveAll();
|
||||
});
|
||||
|
||||
await waitFor(() => expect(HttpUtil.post).toHaveBeenCalledTimes(3));
|
||||
expect(result.current.allSetting.tgBotToken).toBe('');
|
||||
expect(result.current.allSetting.hasTgBotToken).toBe(true);
|
||||
expect(result.current.saveDisabled).toBe(true);
|
||||
});
|
||||
|
||||
it('establishes a saved baseline for a full-payload security save', async () => {
|
||||
let fetchCount = 0;
|
||||
vi.spyOn(HttpUtil, 'post').mockImplementation(async (url) => {
|
||||
if (url === '/panel/api/setting/all') {
|
||||
fetchCount += 1;
|
||||
return new Msg(true, '', fetchCount === 1 ? { hasTgBotToken: false } : { hasTgBotToken: true, tgBotToken: '' });
|
||||
}
|
||||
return new Msg(true, '');
|
||||
});
|
||||
const queryClient = makeTestQueryClient();
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useAllSettings(), { wrapper });
|
||||
|
||||
await waitFor(() => expect(result.current.fetched).toBe(true));
|
||||
act(() => result.current.updateSetting({ tgBotToken: 'secret' }));
|
||||
await act(async () => {
|
||||
await result.current.savePayload({ ...result.current.allSetting, twoFactorEnable: false, twoFactorToken: '' });
|
||||
});
|
||||
|
||||
await waitFor(() => expect(HttpUtil.post).toHaveBeenCalledTimes(3));
|
||||
expect(result.current.allSetting.tgBotToken).toBe('');
|
||||
expect(result.current.allSetting.hasTgBotToken).toBe(true);
|
||||
expect(result.current.saveDisabled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user