Persist configurable connectivity diagnostics
This commit is contained in:
@@ -59,6 +59,7 @@ test('connectivity diagnostics endpoint is available in Connect and Gateway thro
|
||||
assert.match(server, /const localConnectivityDiagnostics = !remoteDataplane/);
|
||||
assert.doesNotMatch(server, /settings\.appMode !== 'gateway'[\s\S]{0,120}ENDPOINT_NOT_FOUND/);
|
||||
assert.match(server, /runConnectivityDiagnostics\(services, target\)/);
|
||||
assert.match(server, /sendState: \(res\) => stateRoute\.send\(res\)/);
|
||||
assert.match(server, /createConnectivityDiagnosticsUseCase\(\{/);
|
||||
assert.match(server, /createConnectivityDiagnosticsRoute\(\{/);
|
||||
assert.match(server, /connectivityDiagnosticsRoute\.handle\(req, res\)/);
|
||||
@@ -69,6 +70,12 @@ test('connectivity diagnostics endpoint is available in Connect and Gateway thro
|
||||
test('connectivity use case captures applied server before probes and preserves result fields', async () => {
|
||||
const events = [];
|
||||
let state = {
|
||||
revision: 3,
|
||||
diagnostics: {
|
||||
configured: true,
|
||||
customServices: [{ id: 'custom-saved', label: 'Saved', url: 'https://example.com/' }],
|
||||
hiddenServiceIds: [],
|
||||
},
|
||||
desiredProfileId: 'primary',
|
||||
appliedProfileId: 'primary',
|
||||
appliedServerId: 'applied',
|
||||
@@ -90,9 +97,12 @@ test('connectivity use case captures applied server before probes and preserves
|
||||
assessment: { summary: 'available' },
|
||||
};
|
||||
const useCase = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => {
|
||||
events.push('state');
|
||||
return state;
|
||||
state: {
|
||||
read: () => {
|
||||
events.push('state');
|
||||
return state;
|
||||
},
|
||||
update: () => { throw new Error('unexpected update'); },
|
||||
},
|
||||
runDiagnostics: async (services, target) => {
|
||||
events.push(['probe', services, target]);
|
||||
@@ -100,12 +110,12 @@ test('connectivity use case captures applied server before probes and preserves
|
||||
return sourceResult;
|
||||
},
|
||||
});
|
||||
const resultPromise = useCase.run({ raw: true }, 42);
|
||||
const resultPromise = useCase.run(42);
|
||||
state.profiles[0].servers[0].label = 'Changed during probe';
|
||||
releaseProbe();
|
||||
const result = await resultPromise;
|
||||
|
||||
assert.deepEqual(events, ['state', ['probe', { raw: true }, 42]]);
|
||||
assert.deepEqual(events, ['state', ['probe', state.diagnostics.customServices, 42]]);
|
||||
assert.deepEqual(result, {
|
||||
...sourceResult,
|
||||
vpn: {
|
||||
@@ -119,52 +129,98 @@ test('connectivity use case captures applied server before probes and preserves
|
||||
test('connectivity use case keeps applied priority, selected fallback and error identity', async () => {
|
||||
const result = { vpn: { available: false }, marker: true };
|
||||
const selected = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => ({
|
||||
desiredProfileId: 'primary',
|
||||
appliedServerId: '',
|
||||
profiles: [{
|
||||
id: 'primary',
|
||||
desiredServerId: 'selected',
|
||||
servers: [{ id: 'selected', label: 'Selected' }],
|
||||
}],
|
||||
}),
|
||||
state: {
|
||||
read: () => ({
|
||||
desiredProfileId: 'primary',
|
||||
appliedServerId: '',
|
||||
profiles: [{
|
||||
id: 'primary',
|
||||
desiredServerId: 'selected',
|
||||
servers: [{ id: 'selected', label: 'Selected' }],
|
||||
}],
|
||||
}),
|
||||
update: () => { throw new Error('unexpected update'); },
|
||||
},
|
||||
runDiagnostics: async () => result,
|
||||
});
|
||||
assert.deepEqual((await selected.run(null, null)).vpn.server, {
|
||||
assert.deepEqual((await selected.run(null)).vpn.server, {
|
||||
id: 'selected',
|
||||
label: 'Selected',
|
||||
});
|
||||
|
||||
const missingApplied = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => ({
|
||||
desiredProfileId: 'primary',
|
||||
appliedProfileId: 'primary',
|
||||
appliedServerId: 'missing',
|
||||
profiles: [{
|
||||
id: 'primary',
|
||||
desiredServerId: 'selected',
|
||||
servers: [{ id: 'selected', label: 'Selected' }],
|
||||
}],
|
||||
}),
|
||||
state: {
|
||||
read: () => ({
|
||||
desiredProfileId: 'primary',
|
||||
appliedProfileId: 'primary',
|
||||
appliedServerId: 'missing',
|
||||
profiles: [{
|
||||
id: 'primary',
|
||||
desiredServerId: 'selected',
|
||||
servers: [{ id: 'selected', label: 'Selected' }],
|
||||
}],
|
||||
}),
|
||||
update: () => { throw new Error('unexpected update'); },
|
||||
},
|
||||
runDiagnostics: async () => result,
|
||||
});
|
||||
assert.equal((await missingApplied.run([], null)).vpn.server, null);
|
||||
assert.equal((await missingApplied.run(null)).vpn.server, null);
|
||||
|
||||
const stateError = new Error('state failed');
|
||||
let probes = 0;
|
||||
const brokenState = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => { throw stateError; },
|
||||
state: {
|
||||
read: () => { throw stateError; },
|
||||
update: () => { throw new Error('unexpected update'); },
|
||||
},
|
||||
runDiagnostics: async () => { probes += 1; return result; },
|
||||
});
|
||||
await assert.rejects(brokenState.run([], null), (error) => error === stateError);
|
||||
await assert.rejects(brokenState.run(null), (error) => error === stateError);
|
||||
assert.equal(probes, 0);
|
||||
|
||||
const probeError = new Error('probe failed');
|
||||
const brokenProbe = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => ({ servers: [] }),
|
||||
state: {
|
||||
read: () => ({ profiles: [] }),
|
||||
update: () => { throw new Error('unexpected update'); },
|
||||
},
|
||||
runDiagnostics: async () => { throw probeError; },
|
||||
});
|
||||
await assert.rejects(brokenProbe.run([], null), (error) => error === probeError);
|
||||
await assert.rejects(brokenProbe.run(null), (error) => error === probeError);
|
||||
});
|
||||
|
||||
test('diagnostics settings validate and replace one canonical revision', () => {
|
||||
let state = {
|
||||
revision: 7,
|
||||
diagnostics: { configured: false, customServices: [], hiddenServiceIds: [] },
|
||||
};
|
||||
const useCase = createConnectivityDiagnosticsUseCase({
|
||||
state: {
|
||||
read: () => state,
|
||||
update: (mutator) => {
|
||||
state = { ...mutator(state), revision: state.revision + 1 };
|
||||
return state;
|
||||
},
|
||||
},
|
||||
runDiagnostics: async () => ({ vpn: {} }),
|
||||
});
|
||||
useCase.updateSettings({
|
||||
customServices: [{ id: 'custom-status', label: 'Status', url: 'https://example.com/status' }],
|
||||
hiddenServiceIds: ['speedtest'],
|
||||
}, 7);
|
||||
assert.deepEqual(state.diagnostics, {
|
||||
configured: true,
|
||||
customServices: [{ id: 'custom-status', label: 'Status', url: 'https://example.com/status' }],
|
||||
hiddenServiceIds: ['speedtest'],
|
||||
});
|
||||
assert.equal(state.revision, 8);
|
||||
assert.throws(() => useCase.updateSettings({ customServices: [], hiddenServiceIds: [] }, 7), {
|
||||
code: 'STATE_CONFLICT',
|
||||
});
|
||||
assert.throws(() => useCase.updateSettings({
|
||||
customServices: [{ id: 'bad', label: 'Router', url: 'http://router.local/' }],
|
||||
hiddenServiceIds: [],
|
||||
}, 8), { code: 'REQUEST_INVALID' });
|
||||
});
|
||||
|
||||
function routeResponse() {
|
||||
@@ -189,34 +245,46 @@ test('connectivity route preserves exact URL, defaults and raw response', async
|
||||
calls.push(args);
|
||||
return { checkedAt: 'now', vpn: { server: null } };
|
||||
},
|
||||
updateSettings: (...args) => calls.push(['settings', ...args]),
|
||||
},
|
||||
readBody: async () => {
|
||||
bodyReads += 1;
|
||||
return body;
|
||||
},
|
||||
sendState: async (response) => {
|
||||
response.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
|
||||
response.end(JSON.stringify({ success: true, state: {} }));
|
||||
},
|
||||
});
|
||||
const res = routeResponse();
|
||||
assert.equal(await route.handle({
|
||||
method: 'POST',
|
||||
url: '/api/diagnostics/connectivity',
|
||||
}, res), true);
|
||||
assert.deepEqual(calls, [[[], null]]);
|
||||
assert.deepEqual(calls, [[null]]);
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(res.headers['content-type'], 'application/json; charset=utf-8');
|
||||
assert.deepEqual(res.payload, { checkedAt: 'now', vpn: { server: null } });
|
||||
|
||||
body = { services: null, target: 17 };
|
||||
await route.handle({ method: 'POST', url: '/api/diagnostics/connectivity' }, routeResponse());
|
||||
assert.deepEqual(calls.at(-1), [null, 17]);
|
||||
assert.deepEqual(calls.at(-1), [17]);
|
||||
|
||||
body = { settings: { customServices: [], hiddenServiceIds: [] }, expectedRevision: 4 };
|
||||
const settingsResponse = routeResponse();
|
||||
assert.equal(await route.handle({ method: 'PUT', url: '/api/diagnostics/settings' }, settingsResponse), true);
|
||||
assert.deepEqual(calls.at(-1), ['settings', body.settings, 4]);
|
||||
assert.equal(settingsResponse.payload.success, true);
|
||||
|
||||
for (const [method, url] of [
|
||||
['GET', '/api/diagnostics/connectivity'],
|
||||
['POST', '/api/diagnostics/connectivity?target=all'],
|
||||
['POST', '/api/diagnostics/settings'],
|
||||
['POST', '/api/diagnostics/other'],
|
||||
]) {
|
||||
assert.equal(await route.handle({ method, url }, routeResponse()), false);
|
||||
}
|
||||
assert.equal(bodyReads, 2);
|
||||
assert.equal(bodyReads, 3);
|
||||
});
|
||||
|
||||
test('a targeted IP row uses three samples and keeps the majority address', async () => {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { createServerId } from '../../dist/shared/serverIdentity.js';
|
||||
import { HARBOR_VERSIONS } from '../../dist/shared/versions.js';
|
||||
import { normalizeSubscriptionConfig } from '../../dist/server/subscription.js';
|
||||
import { STATE_SCHEMA_VERSION } from '../../dist/server/services/stateStore.js';
|
||||
|
||||
const root = path.resolve(import.meta.dirname, '../..');
|
||||
|
||||
@@ -305,7 +306,7 @@ setInterval(() => {}, 60_000);
|
||||
assert.equal(initial.route.localRulesPendingRestart, false);
|
||||
assert.equal(JSON.stringify(initial).includes(subscriptionUrl), false);
|
||||
const migratedState = JSON.parse(fs.readFileSync(path.join(dir, 'state.json'), 'utf8'));
|
||||
assert.equal(migratedState.schemaVersion, 6);
|
||||
assert.equal(migratedState.schemaVersion, STATE_SCHEMA_VERSION);
|
||||
assert.equal(migratedState.profiles.length, 1);
|
||||
assert.equal(migratedState.profiles[0].subscriptionUrl, subscriptionUrl);
|
||||
assert.equal(migratedState.profiles[0].desiredServerId, testServerId);
|
||||
@@ -318,6 +319,7 @@ setInterval(() => {}, 60_000);
|
||||
'apiVersion',
|
||||
'configExists',
|
||||
'connection',
|
||||
'diagnostics',
|
||||
'fetchedAt',
|
||||
'gatewayAuto',
|
||||
'generatedAt',
|
||||
|
||||
@@ -74,7 +74,7 @@ test('schema v2 state migrates built-in .ru into a normal enabled rule', (t) =>
|
||||
assert.equal(JSON.parse(fs.readFileSync(filePath, 'utf8')).schemaVersion, STATE_SCHEMA_VERSION);
|
||||
});
|
||||
|
||||
test('schema v5 migrates saved and applied rules to v6 with an exact backup', (t) => {
|
||||
test('schema v5 migrates rules and diagnostics settings with an exact backup', (t) => {
|
||||
const filePath = fixture(t);
|
||||
const legacy = {
|
||||
schemaVersion: 5,
|
||||
@@ -97,7 +97,12 @@ test('schema v5 migrates saved and applied rules to v6 with an exact backup', (t
|
||||
});
|
||||
const migrated = store.read();
|
||||
|
||||
assert.equal(migrated.schemaVersion, 6);
|
||||
assert.equal(migrated.schemaVersion, STATE_SCHEMA_VERSION);
|
||||
assert.deepEqual(migrated.diagnostics, {
|
||||
configured: false,
|
||||
customServices: [],
|
||||
hiddenServiceIds: [],
|
||||
});
|
||||
assert.equal(migrated.routeRulesRevision, 7);
|
||||
assert.deepEqual(migrated.routeRules.map(({ outbound }) => outbound), ['direct', 'direct']);
|
||||
assert.deepEqual(migrated.appliedRouteRules.map(({ type, outbound }) => [type, outbound]), [
|
||||
@@ -105,7 +110,7 @@ test('schema v5 migrates saved and applied rules to v6 with an exact backup', (t
|
||||
['domain', 'direct'],
|
||||
]);
|
||||
assert.equal(store.migration.fromVersion, 5);
|
||||
assert.equal(store.migration.toVersion, 6);
|
||||
assert.equal(store.migration.toVersion, STATE_SCHEMA_VERSION);
|
||||
assert.match(store.migration.backupPath, /\.backup-v5-/);
|
||||
assert.equal(fs.readFileSync(store.migration.backupPath, 'utf8'), bytes);
|
||||
});
|
||||
|
||||
@@ -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