mirror of
https://github.com/hansjone/netxops.git
synced 2026-10-13 09:20:45 +08:00
107 lines
4.7 KiB
JavaScript
107 lines
4.7 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { readFileSync, existsSync } from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
import { homedir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { runInNewContext } from 'node:vm'
|
|
import { Context, Service } from '@deepseek-ai/cordis'
|
|
|
|
const require = createRequire(import.meta.url)
|
|
// The shell seeds React from its own dependency tree, not the plugin's local
|
|
// development junction. Use that same installed peer tree for this built smoke.
|
|
const profileHome = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
const renderer = ['', 'desktop', 'web'].map(profile => join(profileHome, 'profiles', profile, 'node_modules',
|
|
'@deepseek-ai', 'dsh-client-ui-renderer', 'package.json')).find(existsSync)
|
|
const sharedRequire = renderer ? createRequire(renderer) : require
|
|
function loadClient() {
|
|
let client
|
|
runInNewContext(readFileSync(new URL('../lib/client.js', import.meta.url), 'utf8'), {
|
|
window: { __ModuleLoader__: { load({ factory }) { client = factory(spec =>
|
|
spec === 'react' || spec.startsWith('react/') ? sharedRequire(spec) : require(spec)) } } },
|
|
setTimeout, clearTimeout, setInterval, clearInterval, console, AbortController,
|
|
})
|
|
return client
|
|
}
|
|
const pause = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
|
|
function hostForm() {
|
|
let snapshot = { status: 'ready', writable: true, value: { toolsEnabled: true, groupOpsPresets: 'standard', apiUrl: 'http://127.0.0.1:8890' },
|
|
user: {}, base: {}, revision: 1 }
|
|
const listeners = new Set(), writes = []
|
|
return {
|
|
writes, getSnapshot: () => snapshot,
|
|
subscribe(fn) { listeners.add(fn); return () => listeners.delete(fn) },
|
|
async set(field, value) {
|
|
writes.push([field, value])
|
|
snapshot = { ...snapshot, value: { ...snapshot.value, [field]: value }, user: { ...snapshot.user, [field]: value }, revision: snapshot.revision + 1 }
|
|
for (const fn of listeners) fn()
|
|
return true
|
|
},
|
|
async unset(field) {
|
|
const user = { ...snapshot.user }; delete user[field]
|
|
snapshot = { ...snapshot, user, revision: snapshot.revision + 1 }
|
|
for (const fn of listeners) fn()
|
|
return true
|
|
},
|
|
}
|
|
}
|
|
|
|
async function fixture(t, legacy = false) {
|
|
const root = new Context(), owners = [], slots = new Map(), actual = hostForm()
|
|
class Slots extends Service {
|
|
constructor(ctx) { super(ctx, 'slots') }
|
|
inject(name, register) { return register() }
|
|
register(spec) {
|
|
const key = `${spec.name}:${spec.id ?? spec.key}`
|
|
slots.set(key, spec)
|
|
return () => slots.delete(key)
|
|
}
|
|
}
|
|
class Locale extends Service {
|
|
constructor(ctx) { super(ctx, 'locale') }
|
|
register() { return () => {} }
|
|
bind() { return key => key }
|
|
}
|
|
class Forms extends Service {
|
|
constructor(ctx) { super(ctx, legacy ? 'settingsScope' : 'configForms') }
|
|
get(id) { return id === 'netxops' ? actual : { getSnapshot: () => ({ status: 'unavailable', writable: false }) } }
|
|
bind({ namespace }) { return this.get(namespace) }
|
|
whileServed(ids, register) { return register(new Set(['netxops'])) }
|
|
}
|
|
t.after(async () => { for (const owner of owners.reverse()) await owner.dispose() })
|
|
for (const Provider of [Slots, Locale, Forms]) owners.push(await root.plugin(Provider))
|
|
const client = loadClient()
|
|
const owner = await root.plugin({ ...client, name: 'netxops-client-binding' })
|
|
owners.push(owner)
|
|
return { root, owner, slots, actual }
|
|
}
|
|
|
|
test('built settings card uses optional lookup in real Cordis and saves through the Host form', async t => {
|
|
const { owner, slots, actual } = await fixture(t)
|
|
assert.equal(owner.state, 2 /* ACTIVE */)
|
|
assert.throws(() => owner.ctx.configForms, /without inject/)
|
|
const section = slots.get('settings.section:dsh-netxops')
|
|
assert.ok(section)
|
|
const face = section.inject()
|
|
assert.equal(face.hooks.netxopsCard.getSnapshot().available, false)
|
|
await pause(80)
|
|
assert.equal(face.hooks.netxopsCard.getSnapshot().available, true)
|
|
assert.equal(face.hooks.netxopsCard.getSnapshot().writable, true)
|
|
assert.equal(face.hooks.netxopsCard.getSnapshot().groupOpsPresets.text, 'standard')
|
|
face.edit('toolsEnabled', 'false')
|
|
await pause(20)
|
|
assert.deepEqual(actual.writes, [['toolsEnabled', false]])
|
|
assert.equal(face.hooks.netxopsCard.getSnapshot().toolsEnabled.text, 'false')
|
|
assert.ok(slots.has('settings.plugin.item:netxops'))
|
|
})
|
|
|
|
test('built settings card still follows the legacy Host settings scope', async t => {
|
|
const { slots, actual } = await fixture(t, true)
|
|
const face = slots.get('settings.section:dsh-netxops').inject()
|
|
await pause(80)
|
|
assert.equal(face.hooks.netxopsCard.getSnapshot().available, true)
|
|
face.edit('groupOpsPresets', '')
|
|
await pause(20)
|
|
assert.deepEqual(actual.writes, [['groupOpsPresets', '']])
|
|
})
|