Chat-Baustein: Option requestHeaders fuer Hochladen und Entfernen (Ticketsystem mit JWT)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-01 23:30:29 +00:00
1 parent 48dd19b75a
commit a6ac4510cd
2 files changed
+111 -2

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
// Playwright-Checks Chat-Baustein: Option requestHeaders (Ticketsystem mit JWT) fuer Hochladen und Entfernen.
// Nicht Teil von npm test. Aufruf: node test/ui/composer-headers.ui.mjs
import http from 'node:http'
import { readFile } from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { chromium } from '/root/.npm/_npx/db89d7302a373f10/node_modules/playwright-core/index.mjs'
const pub = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../public')
// Testseite: ?mode=ok (async Header), none (ohne Option), fail (Header verworfen)
const PAGE = `<!doctype html><html lang="de"><head><meta charset="utf-8"><link rel="stylesheet" href="/chat-composer.css"></head>
<body style="background:#111;color:#fff"><div id="zone"><div id="root"></div></div><script src="/chat-composer.js"></script><script>
const mode = new URLSearchParams(location.search).get('mode')
const opts = { root: document.getElementById('root'), dropZone: document.getElementById('zone'), uploadUrl: '/upload',
send: async () => ({ id: 'm1', text: '', attachments: [] }) }
if (mode === 'ok') opts.requestHeaders = async () => { await new Promise((r) => setTimeout(r, 20)); return { Authorization: 'Bearer test' } }
if (mode === 'fail') opts.requestHeaders = () => Promise.reject(new Error('JWT weg'))
window.composer = ChatComposer.create(opts)
</script></body></html>`
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, 'http://x')
if (url.pathname === '/composer-test.html') {
res.writeHead(200, { 'content-type': 'text/html' })
return res.end(PAGE)
}
try {
const file = path.join(pub, url.pathname)
const data = await readFile(file)
res.writeHead(200, { 'content-type': file.endsWith('.js') ? 'text/javascript' : 'text/css' })
res.end(data)
} catch {
res.writeHead(404)
res.end()
}
})
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve))
const base = `http://127.0.0.1:${server.address().port}`
let failed = 0
const check = (name, ok, detail = '') => {
console.log(`${ok ? 'OK ' : 'FAIL'} ${name}${detail ? ` (${detail})` : ''}`)
if (!ok) failed += 1
}
const browser = await chromium.launch()
try {
const run = async (mode) => {
const page = await browser.newPage()
page.setDefaultTimeout(30000)
const calls = []
await page.route('**/upload**', (route) => {
const req = route.request()
calls.push({ method: req.method(), path: new URL(req.url()).pathname, auth: req.headers().authorization || null })
if (req.method() === 'DELETE') return route.fulfill({ status: 204, body: '' })
return route.fulfill({ status: 201, contentType: 'application/json', body: JSON.stringify({ attachment: { id: 'a1', name: 'notiz.txt', type: 'text/plain', size: 5, kind: 'file' } }) })
})
await page.goto(`${base}/composer-test.html?mode=${mode}`)
await page.locator('#root input[type=file]:not([accept])').setInputFiles({ name: 'notiz.txt', mimeType: 'text/plain', buffer: Buffer.from('hallo') })
await page.waitForTimeout(800)
return { page, calls }
}
const ok = await run('ok')
const up = ok.calls.find((c) => c.method === 'POST')
check('mit_header_upload', up?.auth === 'Bearer test', JSON.stringify(up))
await ok.page.click('.chat-tray-remove')
await ok.page.waitForTimeout(500)
const del = ok.calls.find((c) => c.method === 'DELETE')
check('mit_header_delete', del?.path === '/upload/a1' && del?.auth === 'Bearer test', JSON.stringify(del))
const none = await run('none')
const up2 = none.calls.find((c) => c.method === 'POST')
check('ohne_option_kein_header', Boolean(up2) && up2.auth === null, JSON.stringify(up2))
const fail = await run('fail')
const text = await fail.page.textContent('#root')
check('header_fehler', fail.calls.length === 0 && text.includes('JWT weg'), `${fail.calls.length} Aufrufe, Text: ${text.slice(0, 120)}`)
} finally {
await browser.close()
server.close()
}
console.log(failed ? `${failed} Check(s) fehlgeschlagen` : 'alle Checks ok')
process.exit(failed ? 1 : 0)