Improve device tag editing and version clients
Build and Deploy Gateway / build-and-push (push) Successful in 24s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-31 02:26:24 +03:00
parent 116686a138
commit f4882c53c2
6 changed files with 215 additions and 108 deletions
+16 -4
View File
@@ -243,16 +243,22 @@ test('device tags keep filtering, assignment and management accessible at every
assert.match(panel, /deviceFilterCounts\(allDevices, tags\)/);
assert.match(panel, /По этим фильтрам устройств нет\.[\s\S]*Сбросить фильтры/);
assert.match(panel, /deviceTags = tags\.filter[\s\S]*client-device-tag-trigger[\s\S]*\+\{deviceTags\.length - 1\}[\s\S]*'\+ тег'/);
assert.match(panel, /role="dialog"[\s\S]*aria-modal="true"[\s\S]*Теги устройства \{tagPopover\.title\}/);
assert.match(panel, /role="dialog"[\s\S]*aria-modal="true"[\s\S]*aria-label="Теги"/);
assert.match(panel, /client-device-tag-popover-options[\s\S]*aria-pressed=\{selected\}[\s\S]*toggleDeviceTag\(tag\.id\)/);
assert.match(panel, /tagMutationPending = useRef\(false\)[\s\S]*if \(!tagPopover \|\| tagMutationPending\.current \|\| tagSavingId === tagPopover\.deviceId\) return[\s\S]*if \(tagMutationPending\.current && !immediate\) return[\s\S]*if \(!tagPopover \|\| tagMutationPending\.current\) return/);
assert.match(panel, /current\?\.deviceId === device\.id[\s\S]*baseline: next, draft: next[\s\S]*current\?\.deviceId === device\.id[\s\S]*draft: baseline[\s\S]*tagMutationPending\.current = false[\s\S]*setTagMutationCycle/);
assert.match(panel, /aria-disabled=\{Boolean\(tagSavingId\)[\s\S]*if \(tagSavingId \|\| \(!selected && tagPopover\.draft\.length >= 8\)\) return/);
assert.doesNotMatch(panel, /client-device-tag-popover-actions|type="checkbox"|<h3>Теги устройства/);
assert.match(panel, /contentRef\.current\.inert = true[\s\S]*layoutRef\.current\.inert = true/);
assert.match(panel, /FOCUSABLE[\s\S]*event\.key === 'Escape'[\s\S]*document\.activeElement === first/);
assert.match(panel, /catalogChanged[\s\S]*Список тегов изменился\. Откройте теги устройства снова\./);
assert.match(panel, /Теги доступны после обновления Gateway\./);
assert.match(panel, /Управление тегами[\s\S]*Переименовать тег[\s\S]*Удалить тег/);
assert.match(panel, /placeholder="Найти устройство"[\s\S]*Результатов: \{devices\.length\}/);
assert.match(panel, />Назад<\/button>[\s\S]*Не удалось сохранить теги\./);
assert.match(panel, />Назад<\/button>[\s\S]*\+ Добавить тег/);
assert.match(panel, /Не удалось изменить тег\./);
assert.match(panel, /restoreTagTriggerFocus[\s\S]*searchRef\.current,[\s\S]*filterButtonRef\.current,[\s\S]*candidate\?\.isConnected && !candidate\.closest\('\[inert\]'\)/);
assert.match(panel, /updateDeviceTags\(device, tagPopover\.draft, tagPopover\.baseline\)/);
assert.match(panel, /setTagPopover\(\(current\) => current && \(\{ \.\.\.current, draft: next \}\)\)[\s\S]*updateDeviceTags\(device, next, baseline\)[\s\S]*baseline: next, draft: next[\s\S]*draft: baseline/);
assert.match(feature, /updateDeviceTags\(device: Device, tagIds: string\[\], baselineTagIds: string\[\]\)[\s\S]*sameStringList\(currentDevice\.tagIds, baselineTagIds\)[\s\S]*sameStringList\(latestDevice\.tagIds, baselineTagIds\)/);
assert.match(feature, /renameTag\(tag: DeviceTag, name: string, baselineName: string\)[\s\S]*snapshot\.tags\.find[\s\S]*name !== baselineName[\s\S]*latestTag\.name !== baselineName/);
assert.match(panel, /Тег больше не будет доступен для назначения\. Устройства и маршруты не изменятся\./);
@@ -265,9 +271,15 @@ test('device tags keep filtering, assignment and management accessible at every
assert.match(styles, /client-device-tag-popover-in 160ms ease[\s\S]*translateY\(-6px\)[\s\S]*client-device-tag-popover-out 160ms ease forwards/);
assert.match(styles, /client-devices-rail-backdrop \{[\s\S]*opacity: 0[\s\S]*opacity 260ms cubic-bezier\(0\.16, 1, 0\.3, 1\)[\s\S]*client-devices-rail-backdrop\.is-open[\s\S]*opacity: 1/);
assert.match(styles, /client-device-tag-trigger \{[\s\S]*height: 14px[\s\S]*flex: 0 0 auto/);
assert.match(styles, /\.client-devices-tag-create input,[\s\S]*\.client-devices-tag-manager-row input \{[\s\S]*border: 0;[\s\S]*border-bottom: 1px solid var\(--client-border\);[\s\S]*border-radius: 0;[\s\S]*background: transparent/);
assert.match(panel, /client-devices-tag-edit[\s\S]*is-writing[\s\S]*client-row-delete client-devices-tag-delete[\s\S]*client-row-delete-lid/);
assert.match(styles, /\.client-devices-tag-manager-row form button,\n\.client-devices-tag-edit,\n\.client-device-tag-popover button,/);
assert.match(styles, /\.client-devices-manage-tags,[\s\S]*\.client-devices-filter-empty button \{[\s\S]*border: 0;[\s\S]*background: transparent/);
assert.match(styles, /\.client-devices-tag-edit\.is-writing svg \{[\s\S]*client-device-pencil-write 620ms/);
assert.match(styles, /\.client-device-tag-popover-options button \{[\s\S]*color: var\(--client-muted\)[\s\S]*button\[aria-pressed="true"\] \{[\s\S]*color: var\(--tag-tone\)[\s\S]*drop-shadow/);
assert.match(styles, /@media \(hover: hover\)[\s\S]*client-device-tag-trigger:not\(\.has-tag\)[\s\S]*opacity: 0/);
assert.match(styles, /@media \(hover: none\)[\s\S]*client-device-tag-trigger:not\(\.has-tag\)[\s\S]*opacity: 1/);
assert.match(styles, /prefers-reduced-motion: reduce[\s\S]*client-devices-rail[\s\S]*client-devices-rail-view[\s\S]*client-device-tag-popover/);
assert.match(styles, /prefers-reduced-motion: reduce[\s\S]*client-devices-rail[\s\S]*client-devices-rail-view[\s\S]*client-devices-tag-create input[\s\S]*client-devices-tag-edit[\s\S]*client-device-tag-popover/);
});
test('Gateway Home reuses the canonical device snapshot for applied route and global traffic', () => {
+14 -14
View File
@@ -39,26 +39,26 @@ const expectedImports = [
const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex');
const acceptedLedger = {
counts: {
cascadeEdges: 1093,
cascadeEdges: 1120,
customProperties: 115,
declarations: 4433,
declarations: 4455,
important: 0,
keyframes: 52,
media: 19,
rules: 1188,
variableReferences: 1130,
rules: 1193,
variableReferences: 1138,
},
hashes: {
cascadeEdges: '6676dd45ca19923561ef30b2057a9952a795fd142d83b612aaba70132378e91d',
cascadeEdges: 'd8eeaf20637638f97dd826ae7486469367fb7f5c7e2d32a6fb1d64647e8b52e0',
customProperties: '81d1e70737ae5a8e4b20d4c1be1b24685975a404863444da4a8821be1f0d5097',
declarations: 'fcecaa0fb211a14826577bf9629e261b0b4b63b4601febebc43e8d757f6a07f1',
declarations: '89b645d94c44f0dcfea1a7c5f653a8124f0c87057a9898437419ddce813271d2',
duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945',
duplicateSelectors: '8982145dba05b33bf1c93b2d54cfbe76305b276ff3c657aa020144016ada5848',
keyframes: '405688c9a452aa9d54e9c50dd30abb13143fdb5910f63f4474ecefcc800311e6',
ruleDeclarationSequences: '231bd9880d19c8d29e6d9538ccb934e135bd58b33155175e317b743916d93ef7',
selectors: '159b53f5cdd6c4aa6d892b9ed314d9a100345290d4c95e31a4c6be98b609c8b6',
variableReferences: 'd8f4abdb1ea6e9077283f342efef0ea920068165d0fc2ac4e7a9a22e16b31461',
witnesses: 'a84c2ee1c55cb68873f62f230aeec39f88093bcbb50493aaf47d852b13e6985c',
ruleDeclarationSequences: '12a5a2deec8a8521f49551d9a29c1950a01bce225f1b387def47304fcfe1c960',
selectors: 'fa7cc612da0fc1e3804a884032e9ab99e0bb0776b29a9e40d44a2891de198067',
variableReferences: '1cbaf891cd2df7c91003d8879a205075211678ad40a817b746c4dde54830ad6d',
witnesses: 'bae0329346060aeeef91dee449ccee7187e68501c0b5b082026312e7c21a4798',
},
};
@@ -211,7 +211,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, 1209);
assert.equal(witnesses.length, 1198);
assert.equal(witnesses.filter((witness) => witness.unknown || witness.ancestorUnknown).length, 0);
const ledger = createStyleLedger(readStyleSource(root), { witnesses });
assert.deepEqual(ledger.counts, acceptedLedger.counts);
@@ -408,8 +408,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-fn-ai4xB.css']);
assert.deepEqual(assets, ['index-C2yOT86L.css']);
const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0]));
assert.equal(built.byteLength, 168021);
assert.equal(sha256(built), 'a160cdc915a4a8e0b95e360ed721e6037cbeda28df4e0815ab6be43f1004176d');
assert.equal(built.byteLength, 169625);
assert.equal(sha256(built), 'a9bb2c83ab62798e762f4338be1869a6d29366903f83f90d4abcaa2855e44d8e');
});