import test from 'node:test'; import assert from 'node:assert/strict'; import { collectInlineScriptHashes, decorateMindSpacePublishedHtml, handleMindSpaceLongImageDownload, } from './mindspace-public-delivery.mjs'; test('collectInlineScriptHashes returns sha256 hashes for inline scripts only', () => { const html = ` `; const hashes = collectInlineScriptHashes(html); assert.equal(hashes.length, 2); assert.match(hashes[0], /^[A-Za-z0-9+/]+=*$/); assert.notEqual(hashes[0], hashes[1]); }); test('handleMindSpaceLongImageDownload renders and downloads when requested', async () => { const calls = []; const res = { headersSent: false, set(key, value) { calls.push(['set', key, value]); return this; }, download(filePath, filename, cb) { calls.push(['download', filePath, filename]); cb?.(); }, status(code) { calls.push(['status', code]); return this; }, type(value) { calls.push(['type', value]); return this; }, send(value) { calls.push(['send', value]); return this; }, json(value) { calls.push(['json', value]); return this; }, }; const handled = await handleMindSpaceLongImageDownload({ query: { download: 'long-image' }, filePath: '/tmp/report.html', pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/report.html', res, isLongImageDownloadRequest: () => true, longImagePathForHtml: () => '/tmp/report.long.png', renderLongImage: async (...args) => { calls.push(['render', ...args]); }, }); assert.equal(handled, true); assert.deepEqual(calls[0], ['render', { url: 'https://m.tkmind.cn/MindSpace/u/public/report.html', outputPath: '/tmp/report.long.png', }]); assert.deepEqual(calls[1], ['set', 'Cache-Control', 'no-store']); assert.deepEqual(calls[2], ['download', '/tmp/report.long.png', 'report.long.png']); }); test('handleMindSpaceLongImageDownload falls back to local html path without page url', async () => { const calls = []; const res = { headersSent: false, set() { return this; }, download() {}, status() { return this; }, type() { return this; }, send() { return this; }, }; await handleMindSpaceLongImageDownload({ query: { download: 'long-image' }, filePath: '/tmp/report.html', res, isLongImageDownloadRequest: () => true, longImagePathForHtml: () => '/tmp/report.long.png', renderLongImage: async (...args) => { calls.push(['render', ...args]); }, }); assert.deepEqual(calls[0], ['render', { htmlPath: '/tmp/report.html', outputPath: '/tmp/report.long.png' }]); }); test('decorateMindSpacePublishedHtml returns decorated html and csp', () => { const pageScript = "document.body.dataset.ready='1'"; const shareScript = "document.body.dataset.share='1'"; let sharedIsOwner; const result = decorateMindSpacePublishedHtml({ html: `demo`, embed: false, pageDataContext: { pageId: 'page-abc', accessMode: 'password' }, context: { origin: 'https://m.tkmind.cn', pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/report.html', pageDirUrl: 'https://m.tkmind.cn/MindSpace/u/public/', fallbackImageUrl: 'https://m.tkmind.cn/MindSpace/u/public/report.thumbnail.png', }, userAgent: 'MicroMessenger', preparePublicationHtmlForEmbed: (value) => value, injectOgTags: (value, meta) => `${value}`, injectWechatShareBridge: (value, meta) => `${value}`, injectPublicFileShareButton: (value, options) => { sharedIsOwner = options?.isOwner; return { html: value.replace('', ``), scriptHashes: ['ignored'], }; }, publishedPageCsp: (_value, options) => JSON.stringify(options), isWechatUserAgent: () => true, }); assert.match(result.html, /og:/); assert.match(result.html, /wechat:/); assert.match(result.html, /dataset\.share/); assert.match(result.html, /data-mindspace-image-retry="1"/); assert.match(result.html, /__MINDSPACE_PAGE_DATA__/); assert.equal(result.allowEmbedFrame, false); assert.equal(sharedIsOwner, true); const options = JSON.parse(result.csp); assert.equal(options.embed, false); assert.equal(options.wechatShare, true); assert.deepEqual(options.scriptHashes, collectInlineScriptHashes(result.html)); assert.equal(options.scriptHashes.length, 4); }); test('decorateMindSpacePublishedHtml removes preview CSP before published delivery', () => { const result = decorateMindSpacePublishedHtml({ html: '', context: { origin: '', pageUrl: '', pageDirUrl: '', fallbackImageUrl: '' }, preparePublicationHtmlForEmbed: (value) => value, injectOgTags: (value) => value, injectWechatShareBridge: (value) => value, injectPublicFileShareButton: (value) => ({ html: value, scriptHashes: [] }), publishedPageCsp: (value) => value, isWechatUserAgent: () => false, }); assert.doesNotMatch(result.html, /Content-Security-Policy/i); assert.match(result.html, /window\.ready=1/); }); test('decorateMindSpacePublishedHtml forwards isOwner=false to the share button injector', () => { let sharedIsOwner; decorateMindSpacePublishedHtml({ html: 'demo', embed: false, isOwner: false, context: { origin: '', pageUrl: '', pageDirUrl: '', fallbackImageUrl: '' }, preparePublicationHtmlForEmbed: (value) => value, injectOgTags: (value) => value, injectWechatShareBridge: (value) => value, injectPublicFileShareButton: (value, options) => { sharedIsOwner = options?.isOwner; return { html: value, scriptHashes: [] }; }, publishedPageCsp: (_value, options) => JSON.stringify(options), isWechatUserAgent: () => false, }); assert.equal(sharedIsOwner, false); }); test('decorateMindSpacePublishedHtml supports embed mode without share injection', () => { const result = decorateMindSpacePublishedHtml({ html: '', embed: true, context: { origin: '', pageUrl: '', pageDirUrl: '', fallbackImageUrl: '' }, preparePublicationHtmlForEmbed: (value) => `${value}`, injectOgTags: (value) => value, injectWechatShareBridge: (value) => value, injectPublicFileShareButton: (value) => ({ html: value, scriptHashes: [] }), publishedPageCsp: (_value, options) => JSON.stringify(options), isWechatUserAgent: () => false, }); assert.match(result.html, /embed/); assert.equal(result.allowEmbedFrame, true); assert.equal(result.csp, JSON.stringify({ embed: true, wechatShare: false, scriptHashes: [] })); }); test('decorateMindSpacePublishedHtml does not break inline scripts containing ', () => { const pageScript = "var x=''+tb+'';"; const result = decorateMindSpacePublishedHtml({ html: `

demo

`, embed: false, isOwner: true, context: { origin: '', pageUrl: '', pageDirUrl: '', fallbackImageUrl: '' }, preparePublicationHtmlForEmbed: (value) => value, injectOgTags: (value) => value, injectWechatShareBridge: (value) => value, injectPublicFileShareButton: (value) => ({ html: value, scriptHashes: [] }), publishedPageCsp: () => '', isWechatUserAgent: () => false, }); assert.ok(result.html.includes(pageScript)); assert.doesNotThrow(() => { const inline = [...result.html.matchAll(/]*\bsrc)[^>]*>([\s\S]*?)<\/script>/gi)].pop()[1]; new Function(inline); }); });