feat: support simultaneous Tailscale and LAN communication for Yahboom cameras

This commit is contained in:
2026-09-30 15:37:17 +09:00
parent 9387ade537
commit 08f74575c2
38 changed files with 917 additions and 142 deletions
+40
View File
@@ -45,3 +45,43 @@ test('keeps discovered topics without endpoints and deduplicates node entries',
test('module selection and guessed topic mappings are removed from the view', () => {
assert.doesNotMatch(source, /selectedModuleKeys|moduleTopics|fallbackPublishers|fallbackSubscribers|onToggleModule|ListChecks|ListX/)
})
test('active toggle filters only topics, preserves totals and hides filtered details', () => {
const graph = { topics: [
{ name: '/active', types: [], publishers: [endpoint('/pub')], subscribers: [endpoint('/sub')] },
{ name: '/waiting', types: [], publishers: [endpoint('/pub')], subscribers: [] },
] }
function render(activeOnly) {
const states = [graph, false, activeOnly, '/waiting', true, 320]
let index = 0
const api = {}
const jsx = (type, props, key) => ({ type, props, key })
vm.runInNewContext(ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, jsx: ts.JsxEmit.ReactJSX }
}).outputText, { exports: api, require: (name) => {
if (name === 'react') return { useState: () => [states[index++], () => {}], useMemo: fn => fn(), useEffect: () => {} }
if (name === 'react/jsx-runtime') return { jsx, jsxs: jsx }
if (name === '../../i18n') return { t: value => value, useLocaleRender: () => {} }
return {}
} })
const tree = api.default({ dark: false, modules: [{ name: 'Running module', state: 'running' }], remoteWorkspace: false, rosDomainId: '77' })
const nodes = []
function visit(node) {
if (Array.isArray(node)) return node.forEach(visit)
if (!node || typeof node !== 'object') return
nodes.push(node)
visit(node.props?.children)
}
visit(tree)
return nodes
}
for (const activeOnly of [false, true]) {
const nodes = render(activeOnly)
assert.equal(nodes.filter(node => node.type === 'button' && node.key?.startsWith('/')).length, activeOnly ? 1 : 2)
const toggle = nodes.find(node => node.props?.title === '활성 연결만 보기')
assert.equal(toggle.props['aria-pressed'], activeOnly)
assert.equal(toggle.props.children.at(-1), 1)
assert.equal(nodes.find(node => node.type?.name === 'RuntimeLane').props.modules.length, 1)
assert.equal(nodes.some(node => node.type?.name === 'TopicDetailDrawer'), !activeOnly)
}
})