Update VPN client and gateway behavior
This commit is contained in:
@@ -3,6 +3,7 @@ import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import postcss from 'postcss';
|
||||
|
||||
import {
|
||||
analyzeSelectorList,
|
||||
@@ -36,26 +37,26 @@ const expectedImports = [
|
||||
const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex');
|
||||
const acceptedLedger = {
|
||||
counts: {
|
||||
cascadeEdges: 819,
|
||||
customProperties: 31,
|
||||
declarations: 2975,
|
||||
cascadeEdges: 749,
|
||||
customProperties: 103,
|
||||
declarations: 3193,
|
||||
important: 0,
|
||||
keyframes: 56,
|
||||
media: 13,
|
||||
rules: 919,
|
||||
variableReferences: 328,
|
||||
rules: 916,
|
||||
variableReferences: 788,
|
||||
},
|
||||
hashes: {
|
||||
cascadeEdges: '1a3882545e488b80ac5454cc8fa236fd242d15eb8a27ac3371876af866b6c6e8',
|
||||
customProperties: 'c7dd331e4bad898c450568999d8c9c6837e275a79c365c7680e143026fde4545',
|
||||
declarations: '3972e7fd7da24d872a16cfa9dad9e9355861f489708fd9643a8610422e712825',
|
||||
cascadeEdges: '1f48ed1c4272092b7698d94ea8d0fc1ebfb66454a6a715c9b684515bbff74fb8',
|
||||
customProperties: 'fc8401b40b8d2cc8a1fc1716360ba8e5baccb694cdabde68427d3c92c04a84d4',
|
||||
declarations: '8507f8575ae0bd512449c411f0a553f6478ab18fdf217534a32617a8abb9aeb2',
|
||||
duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945',
|
||||
duplicateSelectors: 'c0ec5c96e48cd2ceed32aa5050383bcf29c3c0b8f1b440ed6883435e3e9b964d',
|
||||
keyframes: '39b6fc9c9ab451d63bb77afd90caf63d2d74a30cb351a8f86503de380d76a936',
|
||||
ruleDeclarationSequences: '99469fb4eeb3af72a12cc4365f135e988ab95ebcf194572779d62e5185778d20',
|
||||
selectors: 'b2564f9e6c078233b388e84680181cbd5f21d55e963907990b237b14c5bc250f',
|
||||
variableReferences: 'c8348faf26b2174f02b9760aaa5812e22e02a72da7902fb5897f0c23e7f4e939',
|
||||
witnesses: '3c44c91b2cc2a03049b328b0f7a974b1c189c66d2a778dbb386601eed82994e8',
|
||||
duplicateSelectors: '419962e669cbfdf5c8186318cb26688477fc0199cae98df8e1cd9bf5cddd76c2',
|
||||
keyframes: 'fb859c4d0d1bfd2f5a18904e30a5f79c6ce6931d74fe88cbd506fb28c334b7ac',
|
||||
ruleDeclarationSequences: 'a1d47956771b1503fad124768e3157fe924c6c76914d219885e2cc2dab4413bb',
|
||||
selectors: '6dd352f47606c21b5c07817e185753baa8b3168ab92d3cfef4b28f772b989cc7',
|
||||
variableReferences: '700137c05be5f12722a7ebec90d06be70dde203a754a9312d45c03ff05ff08ec',
|
||||
witnesses: 'f51f070cc2c7dba0f8d08518cdeee49eaf176aac82e3571cdd8498315ba8db83',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -98,7 +99,7 @@ test('tokens, shared primitives, and feature styles have one explicit owner', ()
|
||||
}
|
||||
|
||||
for (const keyframe of [
|
||||
'client-local-rule-enter', 'client-local-rule-leave', 'client-delete-strike',
|
||||
'client-row-enter', 'client-row-leave', 'client-delete-strike',
|
||||
'client-delete-content-dim', 'client-spin', 'client-copy-fade',
|
||||
]) {
|
||||
const owners = Object.entries(sources)
|
||||
@@ -108,6 +109,104 @@ test('tokens, shared primitives, and feature styles have one explicit owner', ()
|
||||
}
|
||||
});
|
||||
|
||||
test('client typography uses the shared semantic scale outside the token owner', () => {
|
||||
const tokenRoot = postcss.parse(fs.readFileSync(path.join(stylesRoot, 'tokens.css'), 'utf8'));
|
||||
const tokens = new Map();
|
||||
tokenRoot.walkDecls((declaration) => tokens.set(declaration.prop, declaration.value));
|
||||
const roleValues = {
|
||||
micro: ['0.5rem', 'var(--font-weight-body) var(--font-size-micro)/var(--line-height-micro) var(--font-family-client)', '1.45', 'var(--tracking-normal)', 'var(--text-transform-none)'],
|
||||
label: ['0.5625rem', 'var(--font-weight-bold) var(--font-size-label)/var(--line-height-label) var(--font-family-client)', '1.2', 'var(--tracking-label)', 'var(--text-transform-label)'],
|
||||
control: ['0.625rem', 'var(--font-weight-bold) var(--font-size-control)/var(--line-height-control) var(--font-family-client)', '1.2', 'var(--tracking-control)', 'var(--text-transform-none)'],
|
||||
body: ['0.6875rem', 'var(--font-weight-body) var(--font-size-body)/var(--line-height-body) var(--font-family-client)', '1.7', 'var(--tracking-normal)', 'var(--text-transform-none)'],
|
||||
data: ['0.75rem', 'var(--font-weight-strong) var(--font-size-data)/var(--line-height-data) var(--font-family-client)', '1.2', 'var(--tracking-normal)', 'var(--text-transform-none)'],
|
||||
'item-title': ['0.875rem', 'var(--font-weight-bold) var(--font-size-item-title)/var(--line-height-item-title) var(--font-family-client)', '1.2', 'var(--tracking-title)', 'var(--text-transform-none)'],
|
||||
'section-title': ['1rem', 'var(--font-weight-bold) var(--font-size-section-title)/var(--line-height-section-title) var(--font-family-client)', '1.25', 'var(--tracking-title)', 'var(--text-transform-none)'],
|
||||
'state-title': ['1.125rem', 'var(--font-weight-bold) var(--font-size-state-title)/var(--line-height-state-title) var(--font-family-client)', '1.3', 'var(--tracking-tight)', 'var(--text-transform-none)'],
|
||||
'drawer-title': ['1.375rem', 'var(--font-weight-bold) var(--font-size-drawer-title)/var(--line-height-drawer-title) var(--font-family-client)', '1.15', 'var(--tracking-tight)', 'var(--text-transform-none)'],
|
||||
};
|
||||
for (const [role, [size, recipe, lineHeight, tracking, transform]] of Object.entries(roleValues)) {
|
||||
assert.equal(tokens.get(`--font-size-${role}`), size, role);
|
||||
assert.equal(tokens.get(`--type-${role}`), recipe, role);
|
||||
assert.equal(tokens.get(`--line-height-${role}`), lineHeight, role);
|
||||
assert.equal(tokens.get(`--type-${role}-tracking`), tracking, role);
|
||||
assert.equal(tokens.get(`--type-${role}-transform`), transform, role);
|
||||
}
|
||||
assert.equal(tokens.get('--type-tooltip'),
|
||||
'var(--font-weight-strong) var(--font-size-control)/var(--line-height-tooltip) var(--font-family-client)');
|
||||
assert.equal(tokens.get('--line-height-tooltip'), '1.35');
|
||||
|
||||
const allowed = new Map([
|
||||
['font', new Set(['inherit', 'var(--type-icon-close)', 'var(--type-tooltip)', ...Object.keys(roleValues).map((role) => `var(--type-${role})`)])],
|
||||
['font-family', new Set(['var(--font-family-client)'])],
|
||||
['font-size', new Set([
|
||||
'var(--font-size-brand)', 'var(--font-size-brand-tooltip)', 'var(--font-size-brand-tooltip-strong)',
|
||||
'var(--font-size-icon-chevron)', 'var(--font-size-icon-delete)',
|
||||
])],
|
||||
['font-weight', new Set(['var(--font-weight-bold)', 'var(--font-weight-brand)', 'var(--font-weight-strong)'])],
|
||||
['line-height', new Set(['var(--line-height-micro)'])],
|
||||
['letter-spacing', new Set([
|
||||
'inherit', 'var(--tracking-normal)', 'var(--tracking-title)',
|
||||
...Object.keys(roleValues).map((role) => `var(--type-${role}-tracking)`),
|
||||
'var(--type-tooltip-tracking)',
|
||||
])],
|
||||
['text-transform', new Set([
|
||||
...Object.keys(roleValues).map((role) => `var(--type-${role}-transform)`),
|
||||
'var(--type-tooltip-transform)',
|
||||
])],
|
||||
['font-variant-numeric', new Set(['var(--numeric-tabular)'])],
|
||||
]);
|
||||
for (const leaf of styleLeafPaths(root).filter((file) => !file.endsWith('/tokens.css'))) {
|
||||
const stylesheet = postcss.parse(fs.readFileSync(leaf, 'utf8'), { from: leaf });
|
||||
stylesheet.walkDecls((declaration) => {
|
||||
if (allowed.has(declaration.prop)) {
|
||||
assert.ok(allowed.get(declaration.prop).has(declaration.value),
|
||||
`${path.relative(root, leaf)}:${declaration.source.start.line} ${declaration.toString()}`);
|
||||
}
|
||||
});
|
||||
stylesheet.walkRules((rule) => {
|
||||
const font = rule.nodes.find((node) => node.type === 'decl' && node.prop === 'font');
|
||||
const role = /^var\(--type-(micro|label|control|tooltip|body|data|item-title|section-title|state-title|drawer-title)\)$/.exec(font?.value || '')?.[1];
|
||||
if (!role) return;
|
||||
const declarations = new Map(rule.nodes
|
||||
.filter((node) => node.type === 'decl')
|
||||
.map((declaration) => [declaration.prop, declaration.value]));
|
||||
assert.equal(declarations.get('letter-spacing'), `var(--type-${role}-tracking)`, rule.selector);
|
||||
assert.equal(declarations.get('text-transform'), `var(--type-${role}-transform)`, rule.selector);
|
||||
});
|
||||
}
|
||||
|
||||
const styles = postcss.parse(readStyleSource(root));
|
||||
for (const selector of [
|
||||
'.client-instructions-header h2', '.client-devices-header h2',
|
||||
'.client-local-rules-header h2', '.client-profiles-header h2',
|
||||
]) {
|
||||
const owner = styles.nodes.find((node) => node.type === 'rule' && node.selector === selector);
|
||||
assert.equal(owner?.nodes.find((node) => node.type === 'decl' && node.prop === 'font')?.value,
|
||||
'var(--type-drawer-title)', selector);
|
||||
}
|
||||
|
||||
for (const selector of [
|
||||
'.client-duration', '.client-power-section.is-gateway .client-duration', '.client-proxy-address',
|
||||
'.client-device-ip', '.client-device-traffic strong',
|
||||
'.client-device-traffic-point-tooltip', '.client-device-traffic-point-tooltip time',
|
||||
'.client-device-traffic-point-tooltip strong', '.client-gateway-traffic-total > strong',
|
||||
'.client-gateway-traffic-speed', '.client-diagnostics-table code',
|
||||
'.client-server-health', '.harbor-versions', '.harbor-version-code', '.harbor-version-number',
|
||||
]) {
|
||||
const owners = [];
|
||||
styles.walkRules((rule) => {
|
||||
if (rule.selectors.includes(selector)) owners.push(rule);
|
||||
});
|
||||
assert.ok(owners.length > 0, selector);
|
||||
assert.ok(owners.some((owner) => owner.nodes.some((node) => node.type === 'decl'
|
||||
&& node.prop === 'font-variant-numeric' && node.value === 'var(--numeric-tabular)')), selector);
|
||||
for (const owner of owners.filter((rule) => rule.nodes.some((node) => node.type === 'decl' && node.prop === 'font'))) {
|
||||
assert.equal(owner.nodes.find((node) => node.type === 'decl' && node.prop === 'font-variant-numeric')?.value,
|
||||
'var(--numeric-tabular)', selector);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('accepted stylesheet has pinned declaration, selector, keyframe, variable, and cascade ledgers', () => {
|
||||
const witnesses = readStyleWitnesses(root);
|
||||
assert.equal(witnesses.length, 730);
|
||||
@@ -306,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-kNiIGd5E.css']);
|
||||
assert.deepEqual(assets, ['index-OzbtuDco.css']);
|
||||
const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0]));
|
||||
assert.equal(built.byteLength, 112296);
|
||||
assert.equal(sha256(built), 'cbda70aff44215a69dc7c87ceaa4166ae573131bc53aaea7ddd935950b2c983f');
|
||||
assert.equal(built.byteLength, 123218);
|
||||
assert.equal(sha256(built), 'e462ddaffffecd18bbc0f14daebc11e6c0df149e6322e1356433d4052550389b');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user