dsh 0.2.0-rc.2: in-container update (experimental channel), path-agnostic compose, upstream patches adapted to 0.1.x/0.2.x
This commit is contained in:
commit
e6dcef9bf5
302 files changed
+75178
No files matched your search
@@ -0,0 +1,117 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import test from 'node:test'
|
||||
import { buildSystemPluginClient } from '../tools/build-system-plugin-client.mjs'
|
||||
|
||||
const root = new URL('../../environment/resources/plugins/settings-navigation/package/', import.meta.url)
|
||||
|
||||
test('Settings Navigation is a browser-only optional System Plugin', async () => {
|
||||
const metadata = JSON.parse(await readFile(new URL('package.json', root)))
|
||||
const patch = JSON.parse(await readFile(new URL('cordis.patch.json', root)))
|
||||
assert.equal(metadata.name, '@dsh-docker/settings-navigation')
|
||||
assert.equal(metadata.dshDocker.description.zh, '优化 DSH 设置目录滚动和窄屏分级导航。')
|
||||
assert.equal(metadata.dsh.client.platform, 'web')
|
||||
assert.equal(metadata.dsh.client.inject.includes('@deepseek-ai/dsh-client-runtime'), false)
|
||||
assert.equal(metadata.peerDependencies['@deepseek-ai/dsh-client-runtime'], undefined)
|
||||
assert.equal(metadata.exports['./client'], './lib/client.bundle.js')
|
||||
assert.equal(patch[0].insert[0].id, 'dsh-docker.settings-navigation.plugin')
|
||||
assert.equal(patch[0].insert[0].name, metadata.name)
|
||||
})
|
||||
|
||||
test('Settings Navigation checked-in bundle matches its source', async () => {
|
||||
const bundle = await readFile(new URL('lib/client.bundle.js', root), 'utf8')
|
||||
const style = await readFile(new URL('lib/style.module.css', root), 'utf8')
|
||||
const rebuilt = await buildSystemPluginClient({
|
||||
pluginId: '@dsh-docker/settings-navigation',
|
||||
sourcePath: new URL('lib/client.js', root),
|
||||
stylePath: new URL('lib/style.module.css', root),
|
||||
})
|
||||
assert.equal(bundle, rebuilt)
|
||||
assert.doesNotThrow(() => new Function(bundle))
|
||||
assert.match(bundle, /^window\.__ModuleLoader__\.load\(/)
|
||||
assert.doesNotMatch(style, /corner-shape/)
|
||||
assert.doesNotMatch(style, /\.header button:not\(\[aria-label\]\)/)
|
||||
})
|
||||
|
||||
test('Settings Navigation recognizes the Settings shell without text or generated classes', async () => {
|
||||
const source = await readFile(new URL('lib/client.js', root), 'utf8')
|
||||
const matcher = source.slice(source.indexOf('function directChildren'), source.indexOf('\nfunction findSettingsDialog'))
|
||||
const matchSettingsDialog = new Function(`${matcher}; return matchSettingsDialog`)()
|
||||
|
||||
class Element {
|
||||
constructor(tagName, ownerDocument) {
|
||||
this.tagName = tagName.toUpperCase()
|
||||
this.ownerDocument = ownerDocument
|
||||
this.children = []
|
||||
this.attributes = new Map()
|
||||
}
|
||||
append(...children) { this.children.push(...children) }
|
||||
contains(target) { return this === target || this.children.some(child => child.contains(target)) }
|
||||
getAttribute(name) { return this.attributes.get(name) ?? null }
|
||||
setAttribute(name, value) { this.attributes.set(name, value) }
|
||||
querySelector(selector) {
|
||||
return this.querySelectorAll(selector)[0] ?? null
|
||||
}
|
||||
querySelectorAll(selector) {
|
||||
const result = []
|
||||
for (const child of this.children) {
|
||||
if (selector === 'button' && child.tagName === 'BUTTON') result.push(child)
|
||||
result.push(...child.querySelectorAll(selector))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
const ids = new Map()
|
||||
const document = { getElementById: id => ids.get(id) ?? null }
|
||||
const dialog = new Element('div', document)
|
||||
dialog.setAttribute('role', 'dialog')
|
||||
dialog.setAttribute('aria-modal', 'true')
|
||||
dialog.setAttribute('aria-labelledby', 'settings-title')
|
||||
const nav = new Element('nav', document)
|
||||
const title = new Element('div', document)
|
||||
ids.set('settings-title', title)
|
||||
const list = new Element('div', document)
|
||||
const selected = new Element('button', document)
|
||||
selected.setAttribute('aria-current', 'true')
|
||||
list.append(selected)
|
||||
nav.append(title, list)
|
||||
const content = new Element('div', document)
|
||||
const header = new Element('div', document)
|
||||
header.append(new Element('button', document))
|
||||
content.append(header, new Element('div', document))
|
||||
dialog.append(nav, content)
|
||||
|
||||
assert.equal(matchSettingsDialog(dialog)?.current, selected)
|
||||
dialog.append(new Element('aside', document))
|
||||
assert.equal(matchSettingsDialog(dialog), null)
|
||||
dialog.children.pop()
|
||||
dialog.setAttribute('aria-labelledby', 'unknown')
|
||||
assert.equal(matchSettingsDialog(dialog), null)
|
||||
})
|
||||
|
||||
test('Settings Navigation owns compact navigation without moving native nodes', async () => {
|
||||
const source = await readFile(new URL('lib/client.js', root), 'utf8')
|
||||
const style = await readFile(new URL('lib/style.module.css', root), 'utf8')
|
||||
assert.match(source, /const COMPACT_WIDTH = 640/)
|
||||
assert.match(source, /shell\.overlay/)
|
||||
assert.match(source, /IconChevronLeftOutline14/)
|
||||
assert.match(source, /MutationObserverImpl/)
|
||||
assert.match(source, /ResizeObserverImpl/)
|
||||
assert.match(source, /attributeFilter: \['aria-current', 'aria-labelledby'\]/)
|
||||
assert.match(source, /\['dialog', 'nav', 'navTitle', 'navList', 'content', 'header', 'options'\]/)
|
||||
assert.match(source, /\.every\(key => next\[key\] === this\.match\[key\]\)/)
|
||||
assert.match(source, /this\.view = match\.buttons\.indexOf\(match\.current\) > 0 \? 'detail' : 'directory'/)
|
||||
assert.match(source, /this\.modeInitialized && compact && !this\.compact/)
|
||||
assert.match(source, /this\.directoryScrollTop = this\.match\.navList\.scrollTop/)
|
||||
assert.match(source, /current\?\.focus\(\{ preventScroll: true \}\)/)
|
||||
assert.match(source, /this\.requestFrame\(\(\) => \{ navList\.scrollTop = scrollTop \}\)/)
|
||||
assert.match(source, /delete this\.match\.dialog\.dataset\.dshSettingsNavigationMode/)
|
||||
assert.match(source, /delete this\.match\.dialog\.dataset\.dshSettingsNavigationView/)
|
||||
assert.match(source, /node\.classList\.remove\(className\)/)
|
||||
assert.doesNotMatch(source, /textContent|innerHTML|appendChild\(match\.|replaceChild/)
|
||||
assert.match(style, /scrollbar-gutter: stable/)
|
||||
assert.match(style, /data-dsh-settings-navigation-view='directory'/)
|
||||
assert.match(style, /data-dsh-settings-navigation-view='detail'/)
|
||||
assert.match(style, /--dsw-alias-label-primary/)
|
||||
assert.doesNotMatch(style, /#[0-9a-f]{3,8}\b/i)
|
||||
})
|
||||
Reference in new issue
Block a user