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,143 @@
|
||||
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-document-editor/package/', import.meta.url)
|
||||
|
||||
test('Settings Document Editor is an optional DSH web 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-document-editor')
|
||||
assert.equal(metadata.dshDocker.description.zh, '在浏览器中查看和编辑 DSH 配置文件。')
|
||||
assert.equal(metadata.dsh.client.platform, 'web')
|
||||
assert.ok(metadata.dsh.client.inject.includes('@deepseek-ai/dsh-api-remotes'))
|
||||
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-document-editor.plugin')
|
||||
})
|
||||
|
||||
test('Settings Document Editor checked-in bundle matches its source', async () => {
|
||||
const bundle = await readFile(new URL('lib/client.bundle.js', root), 'utf8')
|
||||
const rebuilt = await buildSystemPluginClient({
|
||||
pluginId: '@dsh-docker/settings-document-editor',
|
||||
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\(/)
|
||||
})
|
||||
|
||||
test('Settings Document Editor replaces and restores both pathless DSH open actions', async () => {
|
||||
const source = await readFile(new URL('lib/client.js', root), 'utf8')
|
||||
assert.match(source, /\/_dsh_platform\/plugin-api\/v1\/settings-document/)
|
||||
assert.doesNotMatch(source, /\/_dsh_platform\/api\/v1\/settings-document/)
|
||||
assert.match(source, /interceptDocumentOpen\([\s\S]*connection\.api\.settings,[\s\S]*'openDocument'/)
|
||||
assert.match(source, /interceptDocumentOpen\([\s\S]*child\.remote\.settings,[\s\S]*'openSettingsDocument'/)
|
||||
assert.match(source, /ctx\.inject\(\['remote\.settings'\]/)
|
||||
assert.match(source, /\{ ok: true, value: \{ opened: true \} \}/)
|
||||
assert.match(source, /\{ result: \{ ok: true, value: \{ opened: true \} \} \}/)
|
||||
assert.doesNotMatch(source, /openDocument\(.*path|body:.*path/s)
|
||||
assert.match(source, /settings\.dshDocumentEditor/)
|
||||
assert.match(source, /shell\.overlay/)
|
||||
assert.match(source, /createPortal\([\s\S]*document\.body\)/)
|
||||
assert.match(source, /zh: \{[\s\S]*en: \{/)
|
||||
assert.match(source, /error\.status === 409/)
|
||||
assert.match(source, /event\.key\.toLowerCase\(\) === 's'/)
|
||||
assert.match(source, /className: css\.lineNumbers/)
|
||||
assert.match(source, /lineNumbers\.current\.scrollTop = event\.currentTarget\.scrollTop/)
|
||||
assert.match(source, /wrap: 'off'/)
|
||||
})
|
||||
|
||||
test('Settings Document Editor intercepts old and getter-backed open methods with their native result shapes', async () => {
|
||||
const source = await readFile(new URL('lib/client.js', root), 'utf8')
|
||||
const definition = source.slice(source.indexOf('function interceptDocumentOpen'), source.indexOf('\n\nexport function apply'))
|
||||
const interceptDocumentOpen = new Function(`${definition}; return interceptDocumentOpen`)()
|
||||
let opens = 0
|
||||
const controller = { open: async () => { opens += 1 } }
|
||||
|
||||
for (const [method, result, getterBacked] of [
|
||||
['openDocument', { result: { ok: true, value: { opened: true } } }, false],
|
||||
['openSettingsDocument', { ok: true, value: { opened: true } }, true],
|
||||
]) {
|
||||
const original = async () => ({ original: true })
|
||||
const target = getterBacked ? {} : { [method]: original }
|
||||
if (getterBacked) Object.defineProperty(target, method, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: () => original,
|
||||
})
|
||||
const restore = interceptDocumentOpen(target, method, controller, result)
|
||||
assert.deepEqual(await target[method](), result)
|
||||
restore()
|
||||
assert.equal(target[method], original)
|
||||
}
|
||||
assert.equal(opens, 2)
|
||||
})
|
||||
|
||||
test('Settings Document Editor follows DSH tokens and has a mobile layout', async () => {
|
||||
const style = await readFile(new URL('lib/style.module.css', root), 'utf8')
|
||||
assert.match(style, /--dsw-alias-label-primary/)
|
||||
assert.match(style, /--dsw-alias-border-l2/)
|
||||
assert.match(style, /@media \(max-width: 640px\)/)
|
||||
assert.match(style, /height: 100dvh/)
|
||||
assert.match(style, /\.editorFrame \{[\s\S]*grid-template-columns: auto minmax\(0, 1fr\)/)
|
||||
assert.match(style, /\.lineNumbers \{[\s\S]*text-align: right/)
|
||||
assert.match(style, /\.editorFrame:focus-within \{ border-color: var\(--dsw-alias-border-l2\); \}/)
|
||||
assert.match(style, /\.lineNumbers \{[\s\S]*background: color-mix\(in srgb, var\(--dsw-alias-bg-base\) 72%, var\(--dsw-alias-bg-module-platform\)\)/)
|
||||
assert.match(style, /\.overlay,[\s\S]*?\.overlay \*::after \{[^}]*corner-shape: round;/)
|
||||
assert.doesNotMatch(style, /--dsw-alias-state-business-primary/)
|
||||
assert.equal((style.match(/ui-monospace/g) ?? []).length, 2)
|
||||
assert.doesNotMatch(style, /#[0-9a-f]{3,8}\b/i)
|
||||
})
|
||||
|
||||
test('Settings Document Editor state discards stale loads and rejects stale saves', async () => {
|
||||
const source = await readFile(new URL('lib/client.js', root), 'utf8')
|
||||
const definition = source.slice(source.indexOf('class DocumentController'), source.indexOf('function Editor('))
|
||||
const calls = []
|
||||
const responses = [
|
||||
{ content: 'language: zh\n', revision: 'a'.repeat(64), exists: true },
|
||||
{ content: 'language: en\n', revision: 'b'.repeat(64), exists: true },
|
||||
]
|
||||
const DocumentController = new Function('request', `${definition}; return DocumentController`)(async (method, body) => {
|
||||
calls.push([method, body])
|
||||
return responses.shift()
|
||||
})
|
||||
const controller = new DocumentController()
|
||||
await controller.open()
|
||||
assert.equal(controller.state.content, 'language: zh\n')
|
||||
controller.edit('language: en\n')
|
||||
await controller.save()
|
||||
assert.equal(controller.state.content, 'language: en\n')
|
||||
assert.equal(controller.state.savedContent, 'language: en\n')
|
||||
assert.deepEqual(calls.map(call => call[0]), ['GET', 'PUT'])
|
||||
|
||||
controller.close()
|
||||
controller.state.content = 'stale'
|
||||
const conflict = Object.assign(new Error('changed'), { status: 409 })
|
||||
const FailingController = new Function('request', `${definition}; return DocumentController`)(async () => { throw conflict })
|
||||
const failing = new FailingController()
|
||||
failing.state.content = 'old content'
|
||||
failing.state.savedContent = 'old content'
|
||||
failing.state.revision = 'c'.repeat(64)
|
||||
await failing.open()
|
||||
assert.equal(failing.state.content, '')
|
||||
assert.equal(failing.state.revision, null)
|
||||
assert.equal(failing.state.error, 'changed')
|
||||
|
||||
let conflictCall = 0
|
||||
const ConflictController = new Function('request', `${definition}; return DocumentController`)(async () => {
|
||||
conflictCall += 1
|
||||
if (conflictCall === 1) return { content: 'one: 1\n', revision: 'd'.repeat(64), exists: true }
|
||||
throw conflict
|
||||
})
|
||||
const stale = new ConflictController()
|
||||
await stale.open()
|
||||
stale.edit('one: 2\n')
|
||||
await stale.save()
|
||||
assert.equal(stale.state.conflict, true)
|
||||
assert.equal(stale.state.savedContent, 'one: 1\n')
|
||||
assert.equal(stale.state.content, 'one: 2\n')
|
||||
})
|
||||
Reference in new issue
Block a user