Persist configurable connectivity diagnostics
This commit is contained in:
@@ -113,7 +113,14 @@ test('typed endpoint facade preserves exact request contracts and raw payload id
|
||||
method: 'PUT', body: JSON.stringify({ mode: 'direct', expectedRevision: 9 }),
|
||||
}],
|
||||
[() => api.diagnostics.connectivity(), '/api/diagnostics/connectivity', {
|
||||
method: 'POST', body: JSON.stringify({ services: [], target: null }),
|
||||
method: 'POST', body: JSON.stringify({ target: null }),
|
||||
}],
|
||||
[() => api.diagnostics.updateSettings({ customServices: [], hiddenServiceIds: [] }, 10), '/api/diagnostics/settings', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
settings: { customServices: [], hiddenServiceIds: [] },
|
||||
expectedRevision: 10,
|
||||
}),
|
||||
}],
|
||||
[() => api.singbox.stop(), '/api/singbox/stop', { method: 'POST' }],
|
||||
[() => api.singbox.restart(), '/api/singbox/restart', { method: 'POST' }],
|
||||
|
||||
@@ -34,5 +34,6 @@ test('presentational components use only injected narrow actions', () => {
|
||||
assert.match(deviceFeature, /requestDeviceUpdate\(device\.id, patch, snapshot\.revision\)/);
|
||||
assert.match(deviceFeature, /setDevicePolicy\(device\.id, mode, snapshot\.revision\)/);
|
||||
assert.match(servers, /await pingServers\(profileId, ids\)/);
|
||||
assert.match(diagnostics, /await runConnectivityDiagnostics\(customServices, target\)/);
|
||||
assert.match(diagnostics, /await runConnectivityDiagnostics\(target\)/);
|
||||
assert.match(overview, /settings=\{state\.diagnostics\}[\s\S]*updateSettings=\{onUpdateDiagnosticsSettings\}/);
|
||||
});
|
||||
|
||||
@@ -76,17 +76,21 @@ test('unknown target and legacy-full results pass one identity-preserving parser
|
||||
{ ...valid, vpn: { ...valid.vpn, server: undefined } },
|
||||
{ ...valid, vpn: { ...valid.vpn, server: { id: 1, label: 'Server' } } },
|
||||
]) assert.throws(() => parseConnectivityResult(invalid), TypeError);
|
||||
assert.match(panel, /parseConnectivityResult\(await runConnectivityDiagnostics\(customServices, target\)\)/);
|
||||
assert.match(panel, /parseConnectivityResult\(await runConnectivityDiagnostics\(target\)\)/);
|
||||
assert.match(panel, /const legacyFullResult = partial\.direct\.ipv4\.sources\.length > 1 \|\| partial\.direct\.sites\.length > 1/);
|
||||
assert.match(panel, /next = legacyFullResult \? partial : mergeResult\(next, partial\)/);
|
||||
assert.doesNotMatch(model, /\sas\s(?:ConnectivityResult|Record<string, unknown>)/);
|
||||
});
|
||||
|
||||
test('serial probes, storage and editor behavior stay panel-owned', () => {
|
||||
test('serial probes and editor stay panel-owned while backend state owns the service set', () => {
|
||||
assert.match(panel, /const targets = onlyTarget \? \[onlyTarget\] : \[[\s\S]*CONNECTIVITY_NETWORK_SOURCE\.id[\s\S]*CONNECTIVITY_IP_SOURCES\.map[\s\S]*sites\.map/);
|
||||
assert.match(panel, /for \(const target of targets\) \{[\s\S]*setActiveTarget\(target\)[\s\S]*await runConnectivityDiagnostics\(customServices, target\)[\s\S]*if \(legacyFullResult\) break/);
|
||||
assert.match(panel, /for \(const target of targets\) \{[\s\S]*setActiveTarget\(target\)[\s\S]*await runConnectivityDiagnostics\(target\)[\s\S]*if \(legacyFullResult\) break/);
|
||||
assert.match(panel, /CUSTOM_SERVICES_KEY = 'harbor-diagnostic-services'/);
|
||||
assert.match(panel, /HIDDEN_SERVICES_KEY = 'harbor-hidden-diagnostic-services'/);
|
||||
assert.match(panel, /if \(settings\.configured\)[\s\S]*clearLegacyServices\(\)/);
|
||||
assert.match(panel, /updateSettings\(\{[\s\S]*customServices:[\s\S]*hiddenServiceIds/);
|
||||
assert.doesNotMatch(panel, /localStorage\.setItem/);
|
||||
assert.doesNotMatch(panel, /useState\(read(?:Custom|Hidden)Services\)/);
|
||||
assert.match(panel, /slice\(0, MAX_CUSTOM_DIAGNOSTIC_SERVICES\)/);
|
||||
assert.match(panel, /parsed\.protocol !== 'https:'/);
|
||||
assert.match(panel, /document\.startViewTransition\(update\)/);
|
||||
@@ -112,5 +116,7 @@ test('network identity is one stable compact row for Direct and VPN', () => {
|
||||
assert.match(panel, /data-diagnostic-target=\{CONNECTIVITY_NETWORK_SOURCE\.id\}[\s\S]*<NetworkCell[\s\S]*route="Напрямую, сеть"[\s\S]*<NetworkCell[\s\S]*route="VPN, сеть"/);
|
||||
assert.match(panel, /path\?\.network\?\.asn[\s\S]*path\?\.network\?\.provider[\s\S]*path\?\.network\?\.city[\s\S]*path\?\.network\?\.country/);
|
||||
assert.match(panel, /<Status value=\{status\} route=\{route\} \/><br \/>[\s\S]*client-diagnostics-status is-muted[\s\S]* /);
|
||||
assert.match(panel, /if \(!value\) return <Status value=\{\['is-muted', '—'\]\}[\s\S]*if \(!value\?\.address\) return <Status value=\{\['is-error', 'Нет ответа'\]\}/);
|
||||
assert.match(panel, /else if \(path\.network == null\) status = \['is-muted', '—'\][\s\S]*if \(!status && !identity && !location\) status = \['is-error', 'Нет ответа'\]/);
|
||||
assert.doesNotMatch(panel, /traceroute|tracepath/);
|
||||
});
|
||||
|
||||
@@ -211,8 +211,9 @@ test('connectivity diagnostics render stable compact tables before the first run
|
||||
assert.match(diagnostics, /document\.startViewTransition\(update\)/);
|
||||
assert.match(diagnostics, /client-diagnostics-refresh/);
|
||||
assert.match(diagnostics, /client-diagnostics-row-refresh/);
|
||||
assert.match(rule('.client-diagnostics-row-name'), /display:\s*inline-flex[\s\S]*max-width:\s*100%/);
|
||||
assert.match(rule('.client-diagnostics-row-name'), /display:\s*flex[\s\S]*width:\s*100%/);
|
||||
assert.match(styles, /\.client-diagnostics-row-refresh-wrap,[\s\S]*flex:\s*0 0 24px/);
|
||||
assert.match(rule('.client-diagnostics-row-refresh-wrap'), /margin-left:\s*auto/);
|
||||
assert.match(diagnostics, /isGateway \? 'Gateway' : 'Connect'/);
|
||||
assert.doesNotMatch(diagnostics, /Проверить ещё раз|client-diagnostics-empty|client-diagnostics-run/);
|
||||
assert.match(diagnostics, /\{Boolean\(error\) && <div className="client-diagnostics-feedback"/);
|
||||
@@ -221,7 +222,7 @@ test('connectivity diagnostics render stable compact tables before the first run
|
||||
assert.doesNotMatch(rule('.client-diagnostics-feedback'), /min-height:/);
|
||||
assert.match(rule('.client-diagnostics-table'), /table-layout:\s*fixed/);
|
||||
assert.match(diagnostics, /for \(const target of targets\)/);
|
||||
assert.match(diagnostics, /runConnectivityDiagnostics\(customServices, target\)/);
|
||||
assert.match(diagnostics, /runConnectivityDiagnostics\(target\)/);
|
||||
assert.match(diagnostics, /const target = `ip:\$\{source\.id\}`;[\s\S]*activeTarget === target/);
|
||||
assert.match(diagnostics, /activeTarget === `site:\$\{site\.id\}`/);
|
||||
assert.match(diagnostics, /data-diagnostic-target=\{target\}/);
|
||||
|
||||
@@ -39,24 +39,24 @@ const acceptedLedger = {
|
||||
counts: {
|
||||
cascadeEdges: 954,
|
||||
customProperties: 106,
|
||||
declarations: 3486,
|
||||
declarations: 3487,
|
||||
important: 0,
|
||||
keyframes: 48,
|
||||
media: 13,
|
||||
rules: 970,
|
||||
rules: 971,
|
||||
variableReferences: 837,
|
||||
},
|
||||
hashes: {
|
||||
cascadeEdges: 'd53f6a2236717d6bc27d486fc19f0f80a33169fa7f68a6df2a19536fd30be3bb',
|
||||
customProperties: 'fd6f16069f9fe3526f09d4d574431ddae67150d8e7a8a40e04c9fd2d179ec7ac',
|
||||
declarations: '1939d4a9a8b812ea799fbbb22a72befde73aa20159ef6736c9c35a5b3458d32c',
|
||||
declarations: 'e1401e80ed2ebc5d3601d4d61a3b44c2adbf91db451907ef95a0998170ac5de5',
|
||||
duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945',
|
||||
duplicateSelectors: '8982145dba05b33bf1c93b2d54cfbe76305b276ff3c657aa020144016ada5848',
|
||||
keyframes: 'a0d69c5b3e5f235d3a76ed04bdd64f67a73fafc9c59fe2635ffda603709c0ad9',
|
||||
ruleDeclarationSequences: '0a7655c97f5deb132f4c05d136e4d615a0abbbb9e86ab83c8155047bc409bfec',
|
||||
selectors: '0c1374fd3f414faac30f1519e6f0b04f63fc50aa69a05a84d53fbe33c426eccc',
|
||||
ruleDeclarationSequences: '89f5f3b302f3a10df0ba90dfbbc63a7ccbc1d8a0c784be26a475321f44dfa8e8',
|
||||
selectors: 'dc1a2687ec872d1922a39e8258977949a9b775168764a59119ae6ea93a9f88a1',
|
||||
variableReferences: '843a5f71b0fb74691ed623dbc7ebb7ddddc34e5736e1f419f17cf54e22d43073',
|
||||
witnesses: '83b49149eecdf0a67348412f7cfd49023aad76848b1572d25d6c0e9193e612d5',
|
||||
witnesses: '81676a3fdb6f3bbd63e86b275b6cb6d40e930651b9d5cbde208408f3163ab2b4',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -209,7 +209,7 @@ test('client typography uses the shared semantic scale outside the token owner',
|
||||
|
||||
test('accepted stylesheet has pinned declaration, selector, keyframe, variable, and cascade ledgers', () => {
|
||||
const witnesses = readStyleWitnesses(root);
|
||||
assert.equal(witnesses.length, 880);
|
||||
assert.equal(witnesses.length, 884);
|
||||
assert.equal(witnesses.filter((witness) => witness.unknown || witness.ancestorUnknown).length, 0);
|
||||
const ledger = createStyleLedger(readStyleSource(root), { witnesses });
|
||||
assert.deepEqual(ledger.counts, acceptedLedger.counts);
|
||||
@@ -405,8 +405,8 @@ test('main owns one public stylesheet and the regrouped production CSS is determ
|
||||
assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1);
|
||||
|
||||
const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css'));
|
||||
assert.deepEqual(assets, ['index-DDl9J3Se.css']);
|
||||
assert.deepEqual(assets, ['index-CdbGat4L.css']);
|
||||
const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0]));
|
||||
assert.equal(built.byteLength, 133627);
|
||||
assert.equal(sha256(built), '68bf7b36cf345bfb460b7f6c9d973bde54e260a0c695176d035c1fe02ba0f3a1');
|
||||
assert.equal(built.byteLength, 133670);
|
||||
assert.equal(sha256(built), '1aaa222692046eb166e5bfb8a195e53cf0ef61692a82ae53ca0119b4c8f878a2');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user