use-xray-setting.test.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import type { ReactNode } from 'react';
  2. import { act, renderHook, waitFor } from '@testing-library/react';
  3. import { QueryClientProvider } from '@tanstack/react-query';
  4. import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
  5. import { useXraySetting } from '@/hooks/useXraySetting';
  6. import { makeTestQueryClient } from '@/test/test-utils';
  7. import { HttpUtil, Msg } from '@/utils';
  8. function xrayPayload(overrides: Record<string, unknown> = {}) {
  9. return {
  10. xraySetting: {},
  11. inboundTags: [],
  12. clientReverseTags: [],
  13. outboundTestUrl: 'https://test.example',
  14. subscriptionOutbounds: [],
  15. subscriptionOutboundTags: [],
  16. ...overrides,
  17. };
  18. }
  19. afterEach(() => {
  20. vi.restoreAllMocks();
  21. });
  22. beforeEach(() => {
  23. vi.spyOn(HttpUtil, 'get').mockResolvedValue(new Msg(true, '', []));
  24. });
  25. describe('useXraySetting', () => {
  26. it('refreshes server-derived outbounds while the editor is dirty', async () => {
  27. let payload = xrayPayload({ subscriptionOutbounds: [{ tag: 'before' }] });
  28. vi.spyOn(HttpUtil, 'post').mockImplementation(async (url) => {
  29. if (url === '/panel/api/xray/') return new Msg(true, '', JSON.stringify(payload));
  30. return new Msg(true, '');
  31. });
  32. const queryClient = makeTestQueryClient();
  33. const wrapper = ({ children }: { children: ReactNode }) => (
  34. <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  35. );
  36. const { result } = renderHook(() => useXraySetting(), { wrapper });
  37. await waitFor(() => expect(result.current.fetched).toBe(true));
  38. act(() => result.current.setXraySetting('{"outbounds":[]}'));
  39. payload = xrayPayload({ subscriptionOutbounds: [{ tag: 'after' }] });
  40. await act(async () => result.current.fetchAll());
  41. await waitFor(() => expect(result.current.subscriptionOutbounds).toEqual([{ tag: 'after' }]));
  42. expect(result.current.xraySetting).toBe('{"outbounds":[]}');
  43. });
  44. it('keeps the outbound test URL input empty when it is cleared', async () => {
  45. const payload = xrayPayload({ outboundTestUrl: 'https://www.google.com/generate_204' });
  46. vi.spyOn(HttpUtil, 'post').mockImplementation(async (url) => {
  47. if (url === '/panel/api/xray/') return new Msg(true, '', JSON.stringify(payload));
  48. return new Msg(true, '');
  49. });
  50. const queryClient = makeTestQueryClient();
  51. const wrapper = ({ children }: { children: ReactNode }) => (
  52. <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  53. );
  54. const { result } = renderHook(() => useXraySetting(), { wrapper });
  55. await waitFor(() => expect(result.current.fetched).toBe(true));
  56. act(() => result.current.setOutboundTestUrl(''));
  57. expect(result.current.outboundTestUrl).toBe('');
  58. expect(result.current.saveDisabled).toBe(true);
  59. });
  60. // The core lowercases a protocol id and a transport name before resolving
  61. // either, so a differently spelled UDP outbound must still skip the TCP dial.
  62. it.each<[string, Record<string, unknown>, string]>([
  63. ['probes a canonical UDP outbound over HTTP', { protocol: 'wireguard', tag: 'wg' }, 'http'],
  64. [
  65. 'probes a "WireGuard"-spelled outbound over HTTP',
  66. { protocol: 'WireGuard', tag: 'wg' },
  67. 'http',
  68. ],
  69. ['probes a "HyStErIa"-spelled outbound over HTTP', { protocol: 'HyStErIa', tag: 'hy' }, 'http'],
  70. [
  71. 'probes a "KCP" transport over HTTP',
  72. { protocol: 'vless', tag: 'kcp', streamSettings: { network: 'KCP' } },
  73. 'http',
  74. ],
  75. [
  76. 'probes an "mkcp" transport over HTTP',
  77. { protocol: 'vless', tag: 'mkcp', streamSettings: { network: 'mkcp' } },
  78. 'http',
  79. ],
  80. ['probes a plain vless outbound over TCP', { protocol: 'vless', tag: 'plain' }, 'tcp'],
  81. ])('%s', async (_name, outbound, want) => {
  82. const bodies: Array<Record<string, unknown>> = [];
  83. vi.spyOn(HttpUtil, 'post').mockImplementation(async (url, data) => {
  84. if (url === '/panel/api/xray/') {
  85. return new Msg(true, '', JSON.stringify(xrayPayload()));
  86. }
  87. bodies.push(data as Record<string, unknown>);
  88. return new Msg(true, '', [{ success: true, mode: 'http' }]);
  89. });
  90. const queryClient = makeTestQueryClient();
  91. const wrapper = ({ children }: { children: ReactNode }) => (
  92. <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  93. );
  94. const { result } = renderHook(() => useXraySetting(), { wrapper });
  95. await waitFor(() => expect(result.current.fetched).toBe(true));
  96. await act(async () => {
  97. await result.current.testOutbound(0, outbound, 'tcp');
  98. });
  99. expect(bodies).toHaveLength(1);
  100. expect(bodies[0].mode).toBe(want);
  101. });
  102. });