Skip to content

Commit e2ce272

Browse files
committed
AGDNS-4262
1 parent ecea3e7 commit e2ce272

13 files changed

Lines changed: 1478 additions & 14 deletions

File tree

client_v2/src/__locales/en.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@
432432
"friday": "Friday",
433433
"gateway_or_subnet_invalid": "Invalid subnet mask",
434434
"general_statistics": "General statistics",
435+
"go_back": "Go back",
435436
"greater_range_start_error": "The end of the range must be greater than the start",
436437
"home_dns_addresses": "AdGuard Home DNS addresses",
437438
"home_dns_addresses_desc": "The AdGuard Home DNS server listens on the following interfaces/ports:",
@@ -773,6 +774,36 @@
773774
"tls_certificate_expiring": "Your TLS certificate is about to expire",
774775
"tls_key_path_label": "Full path to the private key file",
775776
"tls_key_path_option": "Enter the full path to the private key file",
777+
"tls_setup_backend_error_title": "Could not enable encrypted DNS",
778+
"tls_setup_cert_description": "For DNS encryption, you need a valid TLS certificate chain for your domain — get a free one from letsencrypt.org or buy from a trusted CA. After adding the certificate and key, you'll configure the encrypted DNS server settings",
779+
"tls_setup_cert_paste_label": "Paste the certificate contents",
780+
"tls_setup_cert_path_option": "Path to file on the server",
781+
"tls_setup_cert_path_option_desc": "If the certificate is already on the device running AdGuard Home. It will stay on disk and update automatically when renewed.",
782+
"tls_setup_cert_text_option": "Certificate as text",
783+
"tls_setup_cert_text_option_desc": "If the certificate is on your computer. It will be saved to the AdGuard Home configuration file",
784+
"tls_setup_cert_title": "Add certificate",
785+
"tls_setup_config_title": "Enable encrypted DNS",
786+
"tls_setup_dot_port_label": "DNS-over-TLS port",
787+
"tls_setup_doq_port_label": "DNS-over-QUIC port",
788+
"tls_setup_error_duplicate_port": "Port %port% is used by multiple DNS protocols. It must be unique",
789+
"tls_setup_error_ed25519_key": "ED25519 keys are not supported by browsers",
790+
"tls_setup_error_empty_cert": "Certificate is empty",
791+
"tls_setup_error_no_key": "No valid private keys were found",
792+
"tls_setup_error_parse_cert": "Unable to parse the certificate",
793+
"tls_setup_error_parse_key": "Unable to parse the private key",
794+
"tls_setup_error_port_busy": "Port %port% is not available for %protocol%",
795+
"tls_setup_hint_go_back": "Go back to a previous step to fix the issue",
796+
"tls_setup_https_port_label": "HTTPS port",
797+
"tls_setup_key_description": "The private key must match the certificate from the previous step. Choose how to add it depending on where the key is stored",
798+
"tls_setup_key_paste_label": "Paste the key contents",
799+
"tls_setup_key_path_option": "Path to file on the server",
800+
"tls_setup_key_path_option_desc": "If the key is already on the device running AdGuard Home, it will stay on disk",
801+
"tls_setup_key_text_option": "Private key as text",
802+
"tls_setup_key_text_option_desc": "If the key is on your computer, paste it or upload the file — it will be saved to the AdGuard Home configuration file",
803+
"tls_setup_key_title": "Add private key",
804+
"tls_setup_server_name_label": "Server name",
805+
"tls_setup_warning_cert_untrusted": "The certificate chain cannot be verified: it may be self-signed, expired, or for a different hostname",
806+
"tls_setup_warning_no_ip": "The certificate has no IP addresses. DNS-over-TLS won't be advertised via DDR",
776807
"top_blocked_domains": "Top blocked domains",
777808
"top_clients": "Top clients",
778809
"top_upstreams": "Top upstreams",
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { render, screen, waitFor } from '@solidjs/testing-library';
3+
import userEvent from '@testing-library/user-event';
4+
5+
const mocks = vi.hoisted(() => ({
6+
tlsStatus: vi.fn(),
7+
tlsConfigure: vi.fn(),
8+
tlsValidate: vi.fn(),
9+
addErrorToast: vi.fn(),
10+
addSuccessToast: vi.fn(),
11+
redirectToCurrentProtocol: vi.fn(),
12+
}));
13+
14+
vi.mock('panel/api/generated', () => ({
15+
tlsStatus: mocks.tlsStatus,
16+
tlsConfigure: mocks.tlsConfigure,
17+
tlsValidate: mocks.tlsValidate,
18+
}));
19+
vi.mock('panel/stores/toasts', () => ({
20+
addErrorToast: mocks.addErrorToast,
21+
addSuccessToast: mocks.addSuccessToast,
22+
}));
23+
vi.mock('panel/stores/dashboard', () => ({
24+
getDnsStatus: vi.fn(),
25+
dashboardState: { httpPort: 80 },
26+
}));
27+
vi.mock('panel/helpers/helpers', () => ({
28+
redirectToCurrentProtocol: mocks.redirectToCurrentProtocol,
29+
}));
30+
31+
import { Encryption } from 'panel/components/Encryption/Encryption';
32+
33+
const statusNoCert = {
34+
enabled: false,
35+
serve_plain_dns: true,
36+
force_https: false,
37+
server_name: '',
38+
port_https: 0,
39+
port_dns_over_tls: 0,
40+
port_dns_over_quic: 0,
41+
certificate_chain: '',
42+
certificate_path: '',
43+
private_key: '',
44+
private_key_path: '',
45+
private_key_saved: false,
46+
warning_validation: '',
47+
};
48+
49+
describe('Encryption — TLS setup wizard wiring', () => {
50+
beforeEach(() => {
51+
vi.clearAllMocks();
52+
mocks.tlsStatus.mockResolvedValue(statusNoCert);
53+
});
54+
55+
it('opens the wizard via the plus button when no certificate is configured', async () => {
56+
const user = userEvent.setup();
57+
render(() => <Encryption />);
58+
59+
await waitFor(() => {
60+
expect(screen.getByText('Add TLS certificate')).toBeInTheDocument();
61+
});
62+
63+
await user.click(screen.getByText('Add TLS certificate'));
64+
65+
expect(await screen.findByText('Add certificate')).toBeInTheDocument();
66+
expect(screen.getByRole('progressbar')).toHaveAttribute('aria-valuenow', '1');
67+
});
68+
69+
it('opens the wizard when the Encrypted DNS switch is turned on without a cert', async () => {
70+
const user = userEvent.setup();
71+
render(() => <Encryption />);
72+
73+
await waitFor(() => {
74+
expect(screen.getByText('Encrypted DNS')).toBeInTheDocument();
75+
});
76+
77+
const switchInput = document.getElementById('encrypted_dns') as HTMLInputElement;
78+
expect(switchInput).not.toBeNull();
79+
await user.click(switchInput);
80+
81+
expect(await screen.findByText('Add certificate')).toBeInTheDocument();
82+
});
83+
84+
it('cancelling the wizard leaves the switch off', async () => {
85+
const user = userEvent.setup();
86+
render(() => <Encryption />);
87+
88+
await waitFor(() => {
89+
expect(screen.getByText('Encrypted DNS')).toBeInTheDocument();
90+
});
91+
92+
const switchInput = document.getElementById('encrypted_dns') as HTMLInputElement;
93+
await user.click(switchInput);
94+
95+
expect(await screen.findByText('Add certificate')).toBeInTheDocument();
96+
97+
// Cancel (secondary button in the wizard footer).
98+
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
99+
await user.click(cancelButton);
100+
101+
await waitFor(() => {
102+
expect(switchInput.checked).toBe(false);
103+
});
104+
expect(mocks.tlsConfigure).not.toHaveBeenCalled();
105+
});
106+
});

0 commit comments

Comments
 (0)