mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-13 01:14:59 +03:00
5e15120cec
* feat(happ): make ad blocking optional in routing presets Add an independent AdBlock toggle for Iran, China, and global presets, applied only when generating routing rules. Update the China preset to Bypass-CN and cover preset behavior and localized controls. * feat: add visual routing editor with JSON support and localization updates - Implemented a new modal for editing routing profiles with basic and advanced tabs. - Added functionality to load, parse, and generate routing profiles in JSON format. - Enhanced user experience with validation and error handling for JSON input. - Updated translations for Russian, Turkish, Ukrainian, Vietnamese, Chinese (Simplified and Traditional) to include new routing editor terms. - Created helper functions for managing routing profiles and generating deep links. - Added unit tests for routing editor functionalities and JSON handling. * feat: update routing editor to preserve null lists in profiles and enhance validation messages
134 lines
4.6 KiB
TypeScript
134 lines
4.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import { buildHappPresetDeeplink, toBase64Utf8 } from '@/pages/settings/happPresets';
|
|
|
|
describe('Happ presets and helpers', () => {
|
|
it('generates valid happ://routing/off for off preset', () => {
|
|
const link = buildHappPresetDeeplink('off');
|
|
expect(link).toBe('happ://routing/off');
|
|
});
|
|
|
|
it('generates valid base64 payload for iran-bypass preset', () => {
|
|
const link = buildHappPresetDeeplink('iran-bypass');
|
|
expect(link.startsWith('happ://routing/onadd/')).toBe(true);
|
|
|
|
const b64 = link.replace('happ://routing/onadd/', '');
|
|
const jsonStr = atob(b64);
|
|
const parsed = JSON.parse(jsonStr);
|
|
|
|
expect(parsed.Name).toBe('Iran Bypass');
|
|
expect(parsed.GlobalProxy).toBe('true');
|
|
expect(parsed.DirectSites).toContain('domain:ir');
|
|
expect(parsed.DirectIp).toContain('geoip:ir');
|
|
expect(parsed.BlockSites).toEqual([]);
|
|
});
|
|
|
|
it('generates valid base64 payload for china-direct preset', () => {
|
|
const link = buildHappPresetDeeplink('china-direct');
|
|
expect(link.startsWith('happ://routing/onadd/')).toBe(true);
|
|
|
|
const b64 = link.replace('happ://routing/onadd/', '');
|
|
const jsonStr = atob(b64);
|
|
const parsed = JSON.parse(jsonStr);
|
|
|
|
expect(parsed).toEqual({
|
|
Name: 'Bypass-CN',
|
|
GlobalProxy: 'true',
|
|
RouteOrder: 'block-proxy-direct',
|
|
RemoteDNSType: 'DoH',
|
|
RemoteDNSDomain: 'https://cloudflare-dns.com/dns-query',
|
|
RemoteDNSIP: '1.1.1.1',
|
|
DomesticDNSType: 'DoH',
|
|
DomesticDNSDomain: 'https://dns.alidns.com/dns-query',
|
|
DomesticDNSIP: '223.5.5.5',
|
|
DnsHosts: {
|
|
'cloudflare-dns.com': '1.1.1.1',
|
|
'dns.alidns.com': '223.5.5.5',
|
|
},
|
|
DirectSites: ['geosite:private', 'geosite:cn', 'geosite:geolocation-cn'],
|
|
DirectIp: [
|
|
'geoip:cn',
|
|
'geoip:private',
|
|
'127.0.0.0/8',
|
|
'10.0.0.0/8',
|
|
'172.16.0.0/12',
|
|
'192.168.0.0/16',
|
|
'169.254.0.0/16',
|
|
'224.0.0.0/4',
|
|
'255.255.255.255',
|
|
],
|
|
ProxySites: [],
|
|
ProxyIp: [],
|
|
BlockSites: [],
|
|
BlockIp: [],
|
|
DomainStrategy: 'IPIfNonMatch',
|
|
FakeDNS: 'false',
|
|
UseChunkFiles: 'true',
|
|
});
|
|
});
|
|
|
|
it.each(['iran-bypass', 'china-direct', 'global', 'lan-bypass'])(
|
|
'adds ad blocking only when opted in for %s without changing its routing',
|
|
(preset) => {
|
|
const decode = (link: string) => JSON.parse(atob(link.replace('happ://routing/onadd/', '')));
|
|
const base = decode(buildHappPresetDeeplink(preset));
|
|
const withAds = decode(buildHappPresetDeeplink(preset, true));
|
|
const withoutAds = decode(buildHappPresetDeeplink(preset, false));
|
|
|
|
expect(base.BlockSites).toEqual([]);
|
|
expect(withAds.BlockSites).toEqual(['geosite:category-ads-all']);
|
|
expect({ ...withAds, BlockSites: [] }).toEqual(base);
|
|
expect(withoutAds).toEqual(base);
|
|
},
|
|
);
|
|
|
|
it('keeps routing disabled even when ad blocking is selected', () => {
|
|
expect(buildHappPresetDeeplink('off', true)).toBe('happ://routing/off');
|
|
});
|
|
|
|
it.each(['adblock', 'unknown'])(
|
|
'does not generate a profile for unsupported preset %s',
|
|
(preset) => {
|
|
expect(buildHappPresetDeeplink(preset)).toBe('');
|
|
},
|
|
);
|
|
|
|
it('keeps the global preset without direct exceptions', () => {
|
|
const link = buildHappPresetDeeplink('global');
|
|
const b64 = link.replace('happ://routing/onadd/', '');
|
|
const jsonStr = atob(b64);
|
|
const parsed = JSON.parse(jsonStr);
|
|
|
|
expect(parsed.Name).toBe('Global Proxy');
|
|
expect(parsed.GlobalProxy).toBe('true');
|
|
expect(parsed.DirectSites).toEqual([]);
|
|
expect(parsed.DirectIp).toEqual([]);
|
|
expect(parsed.DomainStrategy).toBe('AsIs');
|
|
});
|
|
|
|
it('generates a separate LAN bypass preset with proxy as the default', () => {
|
|
const link = buildHappPresetDeeplink('lan-bypass');
|
|
const prefix = 'happ://routing/onadd/';
|
|
expect(link.startsWith(prefix)).toBe(true);
|
|
const parsed = JSON.parse(atob(link.slice(prefix.length)));
|
|
|
|
expect(parsed.Name).toBe('Global Bypass Local Network');
|
|
expect(parsed.GlobalProxy).toBe('true');
|
|
expect(parsed.DirectSites).toEqual(['geosite:private']);
|
|
expect(parsed.DirectIp).toEqual(['geoip:private']);
|
|
expect(parsed.BlockSites).toEqual([]);
|
|
expect(parsed.DomainStrategy).toBe('AsIs');
|
|
});
|
|
|
|
it('encodes unicode properly via toBase64Utf8', () => {
|
|
const text = 'فیلترشکن و روتینگ';
|
|
const b64 = toBase64Utf8(text);
|
|
const decoded = decodeURIComponent(
|
|
Array.prototype.map
|
|
.call(atob(b64), (c: string) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
|
|
.join(''),
|
|
);
|
|
expect(decoded).toBe(text);
|
|
});
|
|
});
|