const { test } = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const vm = require('node:vm') const ts = require('typescript') const vue = require('vue') const { renderToString } = require('@vue/server-renderer') const { parse, compileScript } = require('@vue/compiler-sfc') function component(filename, globals = {}) { const { descriptor } = parse(fs.readFileSync(filename, 'utf8'), { filename }) const compiled = compileScript(descriptor, { id: filename, inlineTemplate: true }).content const source = ts.transpileModule(compiled, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: true } }).outputText const context = vm.createContext({ exports: {}, require, ...globals }) vm.runInContext(source, context) return context.exports.default } // Resolve the configured renderer, so accidentally wiring the unrelated "active" renderer fails this test. const storeSource = fs.readFileSync(`${__dirname}/../stores/data.js`, 'utf8') const store = ts.createSourceFile('data.js', storeSource, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS) let chatColumn function visit(node) { if (ts.isObjectLiteralExpression(node) && node.properties.some(property => ts.isPropertyAssignment(property) && property.name.getText(store) === 'key' && property.initializer.getText(store) === '"chatEnabled"')) chatColumn = node ts.forEachChild(node, visit) } visit(store) const rendererName = chatColumn.properties.find(property => property.name?.getText(store) === 'component').initializer.getText(store) const rendererImport = store.statements.find(statement => ts.isImportDeclaration(statement) && statement.importClause?.name?.text === rendererName) const renderer = component(path.resolve(__dirname, '..', rendererImport.moduleSpecifier.text.replace(/^~\//, ''))) const column = vm.runInNewContext(`(${chatColumn.getText(store)})`, { [rendererName]: renderer }) test('project list renders the actual chat setting, independently of an unrelated active flag', async () => { for (const [row, expected] of [ [{ chatEnabled: true }, 'Ja'], [{ chatEnabled: true, active: false }, 'Ja'], [{ chatEnabled: false, active: true }, 'Nein'], [{ chatEnabled: false }, 'Nein'], [{ active: true }, '–'], ]) { const html = await renderToString(vue.createSSRApp(column.component, { row })) assert.match(html, new RegExp(`>${expected}`), JSON.stringify(row)) } }) test('project information shows Ja and Nein after loading the saved setting', async () => { const information = component(`${__dirname}/../components/EntityShowSubInformation.vue`, { useDataStore: () => ({ dataTypes: { projects: { templateColumns: [column] } } }), useTempStore: () => ({}), useRouter: () => ({}), }) for (const chatEnabled of [true, false]) { const app = vue.createSSRApp(information, { item: { id: 1, chatEnabled }, topLevelType: 'projects', platform: 'default' }) app.component('UCard', { setup: (_, { slots }) => () => vue.h('section', slots.default?.()) }) app.component('UAlert', { render: () => null }) app.component('TelephonyCallButton', { render: () => null }) const html = await renderToString(app) assert.match(html, /Projektchat aktiviert/) assert.match(html, new RegExp(`>${chatEnabled ? 'Ja' : 'Nein'}`)) } })