subscription-general-tab.test.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { fireEvent, screen } from '@testing-library/react';
  2. import { MemoryRouter, useLocation } from 'react-router';
  3. import { describe, expect, it, vi } from 'vitest';
  4. import { AllSetting } from '@/models/setting';
  5. import SubscriptionGeneralTab from '@/pages/settings/SubscriptionGeneralTab';
  6. import { renderWithProviders } from './test-utils';
  7. function LocationProbe() {
  8. const location = useLocation();
  9. return (
  10. <output data-testid="location">
  11. {location.pathname}
  12. {location.search}
  13. {location.hash}
  14. </output>
  15. );
  16. }
  17. describe('SubscriptionGeneralTab', () => {
  18. it('keeps the stored subscription port when the field is cleared', () => {
  19. const updateSetting = vi.fn();
  20. renderWithProviders(
  21. <MemoryRouter initialEntries={['/settings#subscription']}>
  22. <SubscriptionGeneralTab
  23. allSetting={new AllSetting({ subPort: 2096 })}
  24. updateSetting={updateSetting}
  25. />
  26. </MemoryRouter>,
  27. );
  28. const portInput = screen.getByDisplayValue('2096');
  29. fireEvent.change(portInput, { target: { value: '' } });
  30. fireEvent.blur(portInput);
  31. expect(updateSetting).not.toHaveBeenCalled();
  32. expect((portInput as HTMLInputElement).value).toBe('2096');
  33. });
  34. it('forwards typed subscription ports unchanged', () => {
  35. const updateSetting = vi.fn();
  36. renderWithProviders(
  37. <MemoryRouter initialEntries={['/settings#subscription']}>
  38. <SubscriptionGeneralTab
  39. allSetting={new AllSetting({ subPort: 2096 })}
  40. updateSetting={updateSetting}
  41. />
  42. </MemoryRouter>,
  43. );
  44. fireEvent.change(screen.getByDisplayValue('2096'), { target: { value: '8443' } });
  45. expect(updateSetting).toHaveBeenCalledWith({ subPort: 8443 });
  46. });
  47. it('uses router navigation to open subscription format settings', () => {
  48. const allSetting = new AllSetting({ subClashEnable: true });
  49. renderWithProviders(
  50. <MemoryRouter initialEntries={['/settings#subscription']}>
  51. <SubscriptionGeneralTab allSetting={allSetting} updateSetting={vi.fn()} />
  52. <LocationProbe />
  53. </MemoryRouter>,
  54. );
  55. fireEvent.click(screen.getByRole('button', { name: 'Open Sub Formats' }));
  56. expect(screen.getByTestId('location').textContent).toBe('/settings#subscription-formats');
  57. });
  58. it.each([false, true])(
  59. 'updates the Happ link gate from its own tab when stored as %s',
  60. (enabled) => {
  61. const updateSetting = vi.fn();
  62. renderWithProviders(
  63. <MemoryRouter initialEntries={['/settings#subscription']}>
  64. <SubscriptionGeneralTab
  65. allSetting={new AllSetting({ happLinkEnable: enabled })}
  66. updateSetting={updateSetting}
  67. />
  68. </MemoryRouter>,
  69. );
  70. fireEvent.click(screen.getByRole('tab', { name: /Happ/ }));
  71. expect(
  72. screen.getByRole('tab', { name: /Routing & Rules/ }).getAttribute('aria-selected'),
  73. ).toBe('true');
  74. expect(screen.queryByRole('switch', { name: 'Encrypted subscription links' })).toBeNull();
  75. fireEvent.click(screen.getByRole('tab', { name: /Subscription Links/ }));
  76. const linkSwitch = screen.getByRole('switch', { name: 'Encrypted subscription links' });
  77. expect(linkSwitch.getAttribute('aria-checked')).toBe(String(enabled));
  78. expect(updateSetting).not.toHaveBeenCalled();
  79. fireEvent.click(linkSwitch);
  80. expect(updateSetting).toHaveBeenCalledExactlyOnceWith({ happLinkEnable: !enabled });
  81. },
  82. );
  83. it('opens the Happ link tab from the QR settings deep link without enabling generation', () => {
  84. const updateSetting = vi.fn();
  85. renderWithProviders(
  86. <MemoryRouter initialEntries={['/settings?subscriptionTab=happ&happTab=links#subscription']}>
  87. <SubscriptionGeneralTab
  88. allSetting={new AllSetting({ happLinkEnable: false })}
  89. updateSetting={updateSetting}
  90. />
  91. <LocationProbe />
  92. </MemoryRouter>,
  93. );
  94. expect(screen.getByRole('tab', { name: /Happ/ }).getAttribute('aria-selected')).toBe('true');
  95. expect(
  96. screen.getByRole('tab', { name: /Subscription Links/ }).getAttribute('aria-selected'),
  97. ).toBe('true');
  98. expect(
  99. screen
  100. .getByRole('switch', { name: 'Encrypted subscription links' })
  101. .getAttribute('aria-checked'),
  102. ).toBe('false');
  103. expect(screen.getByTestId('location').textContent).toBe(
  104. '/settings?subscriptionTab=happ&happTab=links#subscription',
  105. );
  106. expect(updateSetting).not.toHaveBeenCalled();
  107. });
  108. it.each(['', '&happTab=unknown'])(
  109. 'keeps the routing default for a general Happ deep link %s',
  110. (query) => {
  111. const updateSetting = vi.fn();
  112. renderWithProviders(
  113. <MemoryRouter initialEntries={['/settings?subscriptionTab=happ' + query + '#subscription']}>
  114. <SubscriptionGeneralTab allSetting={new AllSetting()} updateSetting={updateSetting} />
  115. </MemoryRouter>,
  116. );
  117. expect(screen.getByRole('tab', { name: /Happ/ }).getAttribute('aria-selected')).toBe('true');
  118. expect(
  119. screen.getByRole('tab', { name: /Routing & Rules/ }).getAttribute('aria-selected'),
  120. ).toBe('true');
  121. expect(screen.queryByRole('switch', { name: 'Encrypted subscription links' })).toBeNull();
  122. expect(updateSetting).not.toHaveBeenCalled();
  123. },
  124. );
  125. });