feat(sub): opt-in dns and sniffer from remote Clash rules (#6868)

Public Mihomo templates ship rules, rule-providers and proxy-groups together
with a dns block those rules depend on (fake-ip ranges, nameserver policy).
Pasting a template inline keeps its dns, but pointing the global routing rules
at its URL dropped it, so URL users had to give up auto-refresh to keep it.

A new switch, subClashRemoteDNS (off by default), also takes the remote dns and
sniffer maps. It is opt-in because a remote dns block decides where clients send
every lookup. A malformed dns or sniffer value is skipped, not the whole template.
The component-test teardown now outlasts AntD Form's 10ms error debounce, which
otherwise fired after jsdom was gone.
This commit is contained in:
Duxxie
2026-10-24 05:21:04 +03:00
committed by GitHub
parent 3acdfd9dea
commit c3394fefed
44 changed files with 355 additions and 46 deletions
+6
View File
@@ -95,6 +95,12 @@ afterEach(async () => {
for (let i = 0; i < 3; i += 1) {
await new Promise((resolve) => setTimeout(resolve, 0));
}
/*
* AntD Form debounces clearing its error list by 10ms (useDebounce via
* rc-util useDelayState), and that timer survives unmount. Wait past it so
* it fires while `window` still exists.
*/
await new Promise((resolve) => setTimeout(resolve, 20));
});
import { HttpUtil, Msg } from '@/utils';
@@ -167,6 +167,29 @@ describe('SubscriptionGeneralTab', () => {
},
);
it.each([false, true])(
'updates the Clash remote DNS opt-in, not the routing switch, when stored as %s',
(enabled) => {
const updateSetting = vi.fn();
renderWithProviders(
<MemoryRouter initialEntries={['/settings#subscription']}>
<SubscriptionGeneralTab
allSetting={new AllSetting({ subClashEnableRouting: true, subClashRemoteDNS: enabled })}
updateSetting={updateSetting}
/>
</MemoryRouter>,
);
fireEvent.click(screen.getByRole('tab', { name: /Clash/ }));
const remoteDNSSwitch = screen.getByRole('switch', { name: 'Use DNS from remote rules' });
expect(remoteDNSSwitch.getAttribute('aria-checked')).toBe(String(enabled));
fireEvent.click(remoteDNSSwitch);
expect(updateSetting).toHaveBeenCalledExactlyOnceWith({ subClashRemoteDNS: !enabled });
},
);
it('shows the Happ link gate from the QR settings deep link without enabling generation', () => {
const updateSetting = vi.fn();