Refine failover channel status layout
Build and Deploy Gateway / build-and-push (push) Successful in 43s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-31 14:52:36 +03:00
parent 3c2eefe108
commit e0bdafd25e
7 changed files with 47 additions and 36 deletions
+1
View File
@@ -8,6 +8,7 @@ data/
# Local roadmap and task workspace # Local roadmap and task workspace
/workpack/ /workpack/
/design-qa.md
# Node/Vite # Node/Vite
node_modules/ node_modules/
+2 -2
View File
@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({ export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.35.0', macClient: '0.35.1',
gatewayClient: '0.37.0', gatewayClient: '0.37.1',
gatewayBackend: '0.37.0', gatewayBackend: '0.37.0',
}); });
+11 -6
View File
@@ -482,6 +482,9 @@ export function FailoverPanel({
const selectedId = channelOptions.find(({ target }) => ( const selectedId = channelOptions.find(({ target }) => (
target.profileId === draft[channel].profileId && target.serverId === draft[channel].serverId target.profileId === draft[channel].profileId && target.serverId === draft[channel].serverId
))?.id || ''; ))?.id || '';
const failureProgress = !checking && channel === 'primary' && snapshot.reason === 'failure-window'
? `${seconds(Math.max(0, Date.now() - Date.parse(snapshot.primary.stateSince || new Date().toISOString())))} / ${seconds(draft.failureWindowMs)} с`
: '';
const healthLabel = missing const healthLabel = missing
? 'Сервер не найден' ? 'Сервер не найден'
: !draft.enabled : !draft.enabled
@@ -490,15 +493,20 @@ export function FailoverPanel({
? 'VPN выключен' ? 'VPN выключен'
: snapshot.activation === 'pending' : snapshot.activation === 'pending'
? 'Проверим после запуска VPN' ? 'Проверим после запуска VPN'
: channel === 'primary' && snapshot.reason === 'failure-window' : checking
? `Проверяем основной канал · ${seconds(Math.max(0, Date.now() - Date.parse(snapshot.primary.stateSince || new Date().toISOString())))} из ${seconds(draft.failureWindowMs)} с` ? `Проверяем ${channel === 'primary' ? 'основной' : 'резервный'} канал`
: failureProgress
? 'Проверяем основной канал'
: channel === 'primary' && snapshot.currentRole === 'reserve' && health === 'healthy' && snapshot.reason === 'recovery-hold' : channel === 'primary' && snapshot.currentRole === 'reserve' && health === 'healthy' && snapshot.reason === 'recovery-hold'
? 'Проверяем восстановление' ? 'Проверяем восстановление'
: health === 'healthy' ? 'Работает' : health === 'unhealthy' ? 'Недоступен' : health === 'not-monitoring' ? 'Проверка выключена' : 'Ждёт проверки'; : health === 'healthy' ? 'Работает' : health === 'unhealthy' ? 'Недоступен' : health === 'not-monitoring' ? 'Проверка выключена' : 'Ждёт проверки';
return <div className="client-failover-channel-slot" key={channel}><article className="client-failover-channel" data-channel={channel}> return <div className="client-failover-channel-slot" key={channel}><article className="client-failover-channel" data-channel={channel}>
<div className="client-failover-channel-title"> <div className="client-failover-channel-title">
<h3>{channel === 'primary' ? 'Основной' : 'Резервный'}</h3> <h3>{channel === 'primary' ? 'Основной' : 'Резервный'}</h3>
<strong className={health === 'healthy' ? 'is-healthy' : health === 'unhealthy' || missing ? 'is-unhealthy' : ''}>{healthLabel}</strong> </div>
<div className="client-failover-channel-status">
<strong className={healthLabel === 'Работает' ? 'is-healthy' : healthLabel === 'Недоступен' || missing ? 'is-unhealthy' : ''}>{healthLabel}</strong>
<span className="client-failover-channel-progress">{failureProgress}</span>
</div> </div>
<FailoverPicker <FailoverPicker
id={`client-failover-${channel}-options`} id={`client-failover-${channel}-options`}
@@ -510,9 +518,6 @@ export function FailoverPanel({
onOpenChange={(open) => setOpenPicker(open ? channel : '')} onOpenChange={(open) => setOpenPicker(open ? channel : '')}
onChange={(optionId) => updateTarget(channel, optionId)} onChange={(optionId) => updateTarget(channel, optionId)}
/> />
<span className={`client-failover-channel-checking${checking ? ' is-visible' : ''}`} aria-hidden="true">
Проверяем {channel === 'primary' ? 'основной' : 'резервный'} канал
</span>
</article>{channel === 'primary' && <div className="client-failover-channel-actions" aria-label="Действия с каналами"> </article>{channel === 'primary' && <div className="client-failover-channel-actions" aria-label="Действия с каналами">
<button <button
className={`client-failover-check-action client-tooltip-anchor${checking ? ' is-running' : ''}`} className={`client-failover-check-action client-tooltip-anchor${checking ? ' is-running' : ''}`}
+9 -8
View File
@@ -20,16 +20,16 @@
.client-failover-channels { display: grid; grid-template-columns: minmax(0, 1fr) 52px minmax(0, 1fr); gap: 12px; } .client-failover-channels { display: grid; grid-template-columns: minmax(0, 1fr) 52px minmax(0, 1fr); gap: 12px; }
.client-failover-channel-slot { display: contents; } .client-failover-channel-slot { display: contents; }
.client-failover-channel { position: relative; display: grid; align-content: start; gap: 7px; min-width: 0; } .client-failover-channel { position: relative; display: grid; align-content: start; gap: 7px; min-width: 0; }
.client-failover-channel-title { min-height: 24px; display: flex; align-items: baseline; justify-content: space-between; gap: 12px; } .client-failover-channel-title { min-height: 24px; }
.client-failover-channel-title h3 { margin: 0; font: var(--type-data); font-variant-numeric: var(--numeric-tabular); letter-spacing: var(--type-data-tracking); text-transform: var(--type-data-transform); } .client-failover-channel-title h3 { margin: 0; font: var(--type-data); font-variant-numeric: var(--numeric-tabular); letter-spacing: var(--type-data-tracking); text-transform: var(--type-data-transform); }
.client-failover-channel[data-channel='primary'] .client-failover-channel-title h3 { color: var(--harbor-connect); text-shadow: 0 0 9px color-mix(in oklch, var(--harbor-connect) 30%, transparent); } .client-failover-channel[data-channel='primary'] .client-failover-channel-title h3 { color: var(--harbor-connect); text-shadow: 0 0 9px color-mix(in oklch, var(--harbor-connect) 30%, transparent); }
.client-failover-channel[data-channel='reserve'] .client-failover-channel-title h3 { color: var(--harbor-gateway); text-shadow: 0 0 9px color-mix(in oklch, var(--harbor-gateway) 30%, transparent); } .client-failover-channel[data-channel='reserve'] .client-failover-channel-title h3 { color: var(--harbor-gateway); text-shadow: 0 0 9px color-mix(in oklch, var(--harbor-gateway) 30%, transparent); }
.client-failover-channel-title strong { min-width: 0; color: var(--client-muted); font: var(--type-control); letter-spacing: var(--type-control-tracking); text-transform: var(--type-control-transform); text-align: right; white-space: nowrap; transition: color 600ms ease, filter 600ms ease; } .client-failover-channel-status { min-height: 32px; display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: repeat(2, minmax(0, 1fr)); align-items: baseline; color: var(--client-muted); font: var(--type-control); letter-spacing: var(--type-control-tracking); text-transform: var(--type-control-transform); }
.client-failover-channel-title strong.is-healthy { color: var(--client-accent); } .client-failover-channel-status strong { min-width: 0; white-space: nowrap; transition: color 600ms ease, filter 600ms ease; }
.client-failover-channel-title strong.is-unhealthy { color: oklch(0.68 0.15 28); } .client-failover-channel-status strong.is-healthy { color: var(--client-accent); }
.client-failover-channel-status strong.is-unhealthy { color: oklch(0.68 0.15 28); }
.client-failover-channel-progress { min-width: 0; font-variant-numeric: var(--numeric-tabular); text-align: left; white-space: nowrap; }
.client-failover-picker { position: relative; min-width: 0; z-index: 1; } .client-failover-picker { position: relative; min-width: 0; z-index: 1; }
.client-failover-channel-checking { min-height: 16px; color: var(--client-muted); font: var(--type-control); letter-spacing: var(--type-control-tracking); text-transform: var(--type-control-transform); opacity: 0; visibility: hidden; transition: opacity 180ms ease; }
.client-failover-channel-checking.is-visible { opacity: 1; visibility: visible; }
.client-failover-channel-actions { grid-column: 2; grid-row: 1; min-height: 66px; display: grid; place-content: center; gap: 2px; } .client-failover-channel-actions { grid-column: 2; grid-row: 1; min-height: 66px; display: grid; place-content: center; gap: 2px; }
.client-failover-check-action, .client-failover-direction-action { width: 44px; height: 44px; display: grid; place-items: center; padding: 0; border: 0; background: transparent; color: var(--client-accent); cursor: pointer; } .client-failover-check-action, .client-failover-direction-action { width: 44px; height: 44px; display: grid; place-items: center; padding: 0; border: 0; background: transparent; color: var(--client-accent); cursor: pointer; }
.client-failover-direction-action.is-primary { color: var(--harbor-connect); } .client-failover-direction-action.is-primary { color: var(--harbor-connect); }
@@ -101,5 +101,6 @@
.client-failover-number-input:focus-visible { outline: 0; color: var(--client-accent); text-shadow: 0 0 9px color-mix(in oklch, var(--client-accent) 38%, transparent); } .client-failover-number-input:focus-visible { outline: 0; color: var(--client-accent); text-shadow: 0 0 9px color-mix(in oklch, var(--client-accent) 38%, transparent); }
@keyframes failover-service-in { from { opacity: 0; filter: blur(5px); transform: translateY(-4px); } to { opacity: 1; filter: blur(0); transform: translateY(0); } } @keyframes failover-service-in { from { opacity: 0; filter: blur(5px); transform: translateY(-4px); } to { opacity: 1; filter: blur(0); transform: translateY(0); } }
@keyframes failover-service-out { from { opacity: 1; filter: blur(0); transform: translateX(0) scale(1); } to { opacity: 0; filter: blur(6px); transform: translateX(16px) scale(.96); } } @keyframes failover-service-out { from { opacity: 1; filter: blur(0); transform: translateX(0) scale(1); } to { opacity: 0; filter: blur(6px); transform: translateX(16px) scale(.96); } }
@media (max-width: 560px) { .client-failover-heading { align-items: flex-start; flex-wrap: wrap; } .client-failover-heading > h2 { min-width: 0; flex: 1 1 auto; } .client-failover-header-actions { flex: 1 1 100%; justify-content: flex-start; gap: 4px 10px; } .client-failover-channels { grid-template-columns: minmax(0, 1fr); gap: 12px; } .client-failover-channel-actions { grid-column: 1; grid-row: auto; min-height: 44px; display: flex; justify-content: center; gap: 8px; } .client-failover-service { grid-template-columns: minmax(0, 1fr) 32px; gap: 6px 10px; } .client-failover-service > .client-failover-number-setting { grid-column: 1; grid-row: 2; } .client-failover-remove-service { grid-column: 2; grid-row: 1 / span 2; } .client-failover-service-editor { grid-template-columns: 1fr; gap: 2px; } } @media (max-width: 680px) { .client-failover-channels { grid-template-columns: minmax(0, 1fr); gap: 12px; } .client-failover-channel-actions { grid-column: 1; grid-row: auto; min-height: 44px; display: flex; justify-content: center; gap: 8px; } }
@media (prefers-reduced-motion: reduce) { .client-failover-channel-title strong, .client-failover-channel-checking, .client-failover-switch::before, .client-failover-switch > span, .client-failover-picker-trigger, .client-failover-picker-trigger::before, .client-failover-picker-trigger svg, .client-failover-picker-list, .client-failover-picker-list button, .client-failover-number-steps, .client-failover-number-control:hover .client-failover-number-steps, .client-failover-number-control:focus-within .client-failover-number-steps, .client-failover-remove-service, .client-failover-remove-lid, .client-failover-direction-action svg { transition: none; } .client-failover-check-action.is-running svg { animation: none; } .client-failover-service, .client-failover-service-editor { animation: none; } } @media (max-width: 560px) { .client-failover-heading { align-items: flex-start; flex-wrap: wrap; } .client-failover-heading > h2 { min-width: 0; flex: 1 1 auto; } .client-failover-header-actions { flex: 1 1 100%; justify-content: flex-start; gap: 4px 10px; } .client-failover-service { grid-template-columns: minmax(0, 1fr) 32px; gap: 6px 10px; } .client-failover-service > .client-failover-number-setting { grid-column: 1; grid-row: 2; } .client-failover-remove-service { grid-column: 2; grid-row: 1 / span 2; } .client-failover-service-editor { grid-template-columns: 1fr; gap: 2px; } }
@media (prefers-reduced-motion: reduce) { .client-failover-channel-status strong, .client-failover-switch::before, .client-failover-switch > span, .client-failover-picker-trigger, .client-failover-picker-trigger::before, .client-failover-picker-trigger svg, .client-failover-picker-list, .client-failover-picker-list button, .client-failover-number-steps, .client-failover-number-control:hover .client-failover-number-steps, .client-failover-number-control:focus-within .client-failover-number-steps, .client-failover-remove-service, .client-failover-remove-lid, .client-failover-direction-action svg { transition: none; } .client-failover-check-action.is-running svg { animation: none; } .client-failover-service, .client-failover-service-editor { animation: none; } }
+8 -5
View File
@@ -23,7 +23,8 @@ test('failover settings use Harbor switches, a two-column channel table and plai
assert.match(feature, /\['primary', 'reserve'\][\s\S]*client-failover-\$\{channel\}-options[\s\S]*updateTarget\(channel, optionId\)/); assert.match(feature, /\['primary', 'reserve'\][\s\S]*client-failover-\$\{channel\}-options[\s\S]*updateTarget\(channel, optionId\)/);
assert.match(feature, /channelLabel[\s\S]*profile\.trim\(\) === server\.trim\(\)[\s\S]*`\$\{profile} · \$\{server}`/); assert.match(feature, /channelLabel[\s\S]*profile\.trim\(\) === server\.trim\(\)[\s\S]*`\$\{profile} · \$\{server}`/);
assert.match(feature, /client-failover-channels[\s\S]*<article className="client-failover-channel"[\s\S]*<h3>\{channel === 'primary' \? 'Основной' : 'Резервный'}/); assert.match(feature, /client-failover-channels[\s\S]*<article className="client-failover-channel"[\s\S]*<h3>\{channel === 'primary' \? 'Основной' : 'Резервный'}/);
assert.match(feature, /client-failover-channel-checking[\s\S]*Проверяем \{channel === 'primary' \? 'основной' : 'резервный'} канал/); assert.match(feature, /client-failover-channel-status[\s\S]*client-failover-channel-progress/);
assert.match(feature, /checking[\s\S]*`Проверяем \$\{channel === 'primary' \? 'основной' : 'резервный'\} канал`/);
assert.match(feature, /client-live-region[\s\S]*Проверяем основной и резервный каналы/); assert.match(feature, /client-live-region[\s\S]*Проверяем основной и резервный каналы/);
assert.match(feature, /ArrowDown[\s\S]*Home[\s\S]*End[\s\S]*Escape/); assert.match(feature, /ArrowDown[\s\S]*Home[\s\S]*End[\s\S]*Escape/);
assert.match(feature, /role="listbox"[\s\S]*role="option"/); assert.match(feature, /role="listbox"[\s\S]*role="option"/);
@@ -76,7 +77,7 @@ test('failover settings use Harbor switches, a two-column channel table and plai
assert.match(styles, /\.client-failover-channels \{[^}]*grid-template-columns:\s*minmax\(0, 1fr\) 52px minmax\(0, 1fr\)/); assert.match(styles, /\.client-failover-channels \{[^}]*grid-template-columns:\s*minmax\(0, 1fr\) 52px minmax\(0, 1fr\)/);
assert.match(styles, /\.client-failover-channel-actions \{[^}]*grid-column:\s*2[^}]*grid-row:\s*1/); assert.match(styles, /\.client-failover-channel-actions \{[^}]*grid-column:\s*2[^}]*grid-row:\s*1/);
assert.match(styles, /\.client-failover-check-action, \.client-failover-direction-action \{[^}]*width:\s*44px[^}]*height:\s*44px/); assert.match(styles, /\.client-failover-check-action, \.client-failover-direction-action \{[^}]*width:\s*44px[^}]*height:\s*44px/);
assert.match(styles, /@media \(max-width: 560px\)[\s\S]*\.client-failover-channels \{ grid-template-columns:\s*minmax\(0, 1fr\)/); assert.match(styles, /@media \(max-width: 680px\)[\s\S]*\.client-failover-channels \{ grid-template-columns:\s*minmax\(0, 1fr\)/);
assert.match(styles, /\.client-failover-picker-list[\s\S]*opacity:\s*0[\s\S]*\.client-failover-picker\.is-open[\s\S]*opacity:\s*1/); assert.match(styles, /\.client-failover-picker-list[\s\S]*opacity:\s*0[\s\S]*\.client-failover-picker\.is-open[\s\S]*opacity:\s*1/);
assert.match(styles, /failover-service-out 300ms[\s\S]*@media \(prefers-reduced-motion: reduce\)/); assert.match(styles, /failover-service-out 300ms[\s\S]*@media \(prefers-reduced-motion: reduce\)/);
assert.match(styles, /\.client-failover-number-setting \{[^}]*display:\s*flex[^}]*flex-wrap:\s*wrap/); assert.match(styles, /\.client-failover-number-setting \{[^}]*display:\s*flex[^}]*flex-wrap:\s*wrap/);
@@ -92,13 +93,15 @@ test('failover settings use Harbor switches, a two-column channel table and plai
}); });
test('active channel and checks stay attached to their channel columns without a duplicate runtime block', () => { test('active channel and checks stay attached to their channel columns without a duplicate runtime block', () => {
assert.match(feature, /Проверяем основной канал ·/); assert.match(feature, /failureProgress[\s\S]*`\$\{seconds\([\s\S]*\)} \/ \$\{seconds\(draft\.failureWindowMs\)} с`/);
assert.match(feature, /failureProgress[\s\S]*'Проверяем основной канал'/);
assert.match(feature, /client-failover-direction-action[\s\S]*disabled=\{blocked \|\| !snapshot.enabled \|\| snapshot.activation !== 'active' \|\| !switchRole\}/); assert.match(feature, /client-failover-direction-action[\s\S]*disabled=\{blocked \|\| !snapshot.enabled \|\| snapshot.activation !== 'active' \|\| !switchRole\}/);
assert.match(feature, /className=\{`client-failover-direction-action client-tooltip-anchor\$\{role === 'primary'[\s\S]*role === 'reserve'/); assert.match(feature, /className=\{`client-failover-direction-action client-tooltip-anchor\$\{role === 'primary'[\s\S]*role === 'reserve'/);
assert.match(feature, /<svg className=\{role === 'primary' \? 'is-primary' : role === 'reserve' \? 'is-reserve' : undefined\}/); assert.match(feature, /<svg className=\{role === 'primary' \? 'is-primary' : role === 'reserve' \? 'is-reserve' : undefined\}/);
assert.doesNotMatch(feature, /client-failover-runtime|Сейчас используется|Новые подключения:/); assert.doesNotMatch(feature, /client-failover-runtime|Сейчас используется|Новые подключения:/);
assert.doesNotMatch(feature, /client-failover-channel-checking/);
assert.doesNotMatch(styles, /\.client-failover-runtime/); assert.doesNotMatch(styles, /\.client-failover-runtime/);
assert.match(styles, /\.client-failover-channel-checking \{[^}]*min-height:\s*16px[^}]*opacity:\s*0[^}]*visibility:\s*hidden/); assert.match(styles, /\.client-failover-channel-status \{[^}]*min-height:\s*32px[^}]*grid-template-columns:\s*minmax\(0, 1fr\)[^}]*grid-template-rows:\s*repeat\(2, minmax\(0, 1fr\)\)/);
assert.match(styles, /\.client-failover-channel-checking\.is-visible \{[^}]*opacity:\s*1[^}]*visibility:\s*visible/); assert.match(styles, /\.client-failover-channel-progress \{[^}]*min-width:\s*0[^}]*text-align:\s*left/);
assert.match(styles, /@media \(prefers-reduced-motion: reduce\)[^{]*\{[^}]*\.client-failover-number-control:hover \.client-failover-number-steps[^}]*transition:\s*none/); assert.match(styles, /@media \(prefers-reduced-motion: reduce\)[^{]*\{[^}]*\.client-failover-number-control:hover \.client-failover-number-steps[^}]*transition:\s*none/);
}); });
+2 -1
View File
@@ -104,7 +104,8 @@ test('tablet and mobile regions use normal flow with viewport-safe widths', () =
assert.match(mobile, /\.client-secondary-menu \{[\s\S]*right:\s*8px/); assert.match(mobile, /\.client-secondary-menu \{[\s\S]*right:\s*8px/);
assert.match(mobile, /\.client-power-control > \.harbor-brand \{[\s\S]*scale\(1\.35\)/); assert.match(mobile, /\.client-power-control > \.harbor-brand \{[\s\S]*scale\(1\.35\)/);
assert.match(mobile, /\.client-server-check \{[\s\S]*width:\s*44px/); assert.match(mobile, /\.client-server-check \{[\s\S]*width:\s*44px/);
assert.match(styles, /@media \(max-width: 560px\)[\s\S]*\.client-failover-heading \{[\s\S]*flex-wrap:\s*wrap[\s\S]*\.client-failover-channel-actions \{[\s\S]*grid-column:\s*1[\s\S]*grid-row:\s*auto[\s\S]*display:\s*flex[\s\S]*justify-content:\s*center/); assert.match(styles, /@media \(max-width: 560px\)[\s\S]*\.client-failover-heading \{[\s\S]*flex-wrap:\s*wrap/);
assert.match(styles, /@media \(max-width: 680px\)[\s\S]*\.client-failover-channel-actions \{[\s\S]*grid-column:\s*1[\s\S]*grid-row:\s*auto[\s\S]*display:\s*flex[\s\S]*justify-content:\s*center/);
assert.match(styles, /\.client-failover-channel-actions \{[^}]*grid-column:\s*2[^}]*display:\s*grid/); assert.match(styles, /\.client-failover-channel-actions \{[^}]*grid-column:\s*2[^}]*display:\s*grid/);
assert.match(rule('.client-drawer'), /width:\s*min\(580px, 100vw\)/); assert.match(rule('.client-drawer'), /width:\s*min\(580px, 100vw\)/);
assert.match(styles, /\.client-confirmation-dialog\s*\{[\s\S]*width:\s*min\(430px, calc\(100vw - 48px\)\)/); assert.match(styles, /\.client-confirmation-dialog\s*\{[\s\S]*width:\s*min\(430px, calc\(100vw - 48px\)\)/);
+14 -14
View File
@@ -40,26 +40,26 @@ const expectedImports = [
const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex'); const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex');
const acceptedLedger = { const acceptedLedger = {
counts: { counts: {
cascadeEdges: 1164, cascadeEdges: 1163,
customProperties: 115, customProperties: 115,
declarations: 4752, declarations: 4746,
important: 0, important: 0,
keyframes: 55, keyframes: 55,
media: 22, media: 23,
rules: 1288, rules: 1288,
variableReferences: 1240, variableReferences: 1237,
}, },
hashes: { hashes: {
cascadeEdges: 'e71264f527a1a7ec77ce9c382cc8b99e7363a379d258b5feb418ec591901b0aa', cascadeEdges: 'c1b3eaa3d0ad08e3393cf90878f90e17a5401d79eed9072d06f39b7841d8bbfd',
customProperties: '81d1e70737ae5a8e4b20d4c1be1b24685975a404863444da4a8821be1f0d5097', customProperties: '81d1e70737ae5a8e4b20d4c1be1b24685975a404863444da4a8821be1f0d5097',
declarations: 'bb96f5466d6717eb2e54ef620a275f510ac41a99c3dc282b221fabac5e96e2fb', declarations: 'e668191e548093c02463e57b81eb6c12b826991a5e10f5191d87b73e682cd852',
duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945', duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945',
duplicateSelectors: '8982145dba05b33bf1c93b2d54cfbe76305b276ff3c657aa020144016ada5848', duplicateSelectors: '8982145dba05b33bf1c93b2d54cfbe76305b276ff3c657aa020144016ada5848',
keyframes: 'b9b0bf3fad92e69b93ec0c24f01da15e78bbe89a095597d64ce4f7ef5e272fdd', keyframes: 'b9b0bf3fad92e69b93ec0c24f01da15e78bbe89a095597d64ce4f7ef5e272fdd',
ruleDeclarationSequences: 'f55ebc3544ddae34b1ed9aaba85fd4336d0e0722c7b986f6a42aafeb0f9abe3c', ruleDeclarationSequences: '2cc97550fecd8bbf46217dc378cc490fd41d977abcdcc4c93a18c7593ba42f53',
selectors: '5be446ae824d5d9adfe912fdefe3220c0bc1b26ad6faa2306d399a9f416a03e4', selectors: 'c7f4752d81bbeb04612eb6c79a42bcde875c762d81591b2cdf2fb2bbd7399ea2',
variableReferences: 'f8e1ec8c81d9890880a177a283b04222c34132719e0d954d3e1944a50ebcdb2e', variableReferences: '69c03c059f9ce107bc8be0397cff90e1b8b3749b8e37ba8e231e2d6cdcd61e46',
witnesses: '783115d6bfe4955d195d2fa8fdc5dc602903a19fdf666f89fe4938bd47bb208a', witnesses: '99713805eb7a5c67427ace32775f0cc503b3b2ac63240db717e71b36d09ddd68',
}, },
}; };
@@ -212,7 +212,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', () => { test('accepted stylesheet has pinned declaration, selector, keyframe, variable, and cascade ledgers', () => {
const witnesses = readStyleWitnesses(root); const witnesses = readStyleWitnesses(root);
assert.equal(witnesses.length, 1311); assert.equal(witnesses.length, 1312);
assert.equal(witnesses.filter((witness) => witness.unknown || witness.ancestorUnknown).length, 0); assert.equal(witnesses.filter((witness) => witness.unknown || witness.ancestorUnknown).length, 0);
const ledger = createStyleLedger(readStyleSource(root), { witnesses }); const ledger = createStyleLedger(readStyleSource(root), { witnesses });
assert.deepEqual(ledger.counts, acceptedLedger.counts); assert.deepEqual(ledger.counts, acceptedLedger.counts);
@@ -409,8 +409,8 @@ test('main owns one public stylesheet and the regrouped production CSS is determ
assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1); assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1);
const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css')); const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css'));
assert.deepEqual(assets, ['index--AY5M9TT.css']); assert.deepEqual(assets, ['index-BLRRtFYh.css']);
const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0])); const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0]));
assert.equal(built.byteLength, 180764); assert.equal(built.byteLength, 180634);
assert.equal(sha256(built), 'e36629a671854f92641bd0dfefd796336adc54e026604ef39d8389daa418beac'); assert.equal(sha256(built), '6bf3cb9c724758de39567c606c939e6041edde82837f826e8d2d4e31ebd1d8a8');
}); });