ee8677cc6d
Wire delivery path detection through portal static routes and strip baked share chrome from learn parent/child HTML. Co-authored-by: Cursor <cursoragent@cursor.com>
308 lines
11 KiB
JavaScript
308 lines
11 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
collectInlineScriptHashes,
|
|
decorateMindSpacePublishedHtml,
|
|
handleMindSpaceLongImageDownload,
|
|
shouldSkipPublicShareWidget,
|
|
} from './mindspace-public-delivery.mjs';
|
|
|
|
test('collectInlineScriptHashes returns sha256 hashes for inline scripts only', () => {
|
|
const html = `<html><body>
|
|
<script>console.log('page')</script>
|
|
<script src="https://cdn.example/app.js"></script>
|
|
<script>console.log('share')</script>
|
|
</body></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: `<html><body><script>${pageScript}</script>demo</body></html>`,
|
|
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}<!--og:${meta.pageUrl}-->`,
|
|
injectWechatShareBridge: (value, meta) => `${value}<!--wechat:${meta.pageUrl}-->`,
|
|
injectPublicFileShareButton: (value, options) => {
|
|
sharedIsOwner = options?.isOwner;
|
|
return {
|
|
html: value.replace('</body>', `<script>${shareScript}</script></body>`),
|
|
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: '<html><head><meta http-equiv="Content-Security-Policy" content="script-src \'none\'"></head><body><script>window.ready=1</script></body></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: '<html><body>demo</body></html>',
|
|
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('shouldSkipPublicShareWidget matches learning assistant delivery paths', () => {
|
|
assert.equal(
|
|
shouldSkipPublicShareWidget({
|
|
pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/learning-assistant.html',
|
|
}),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldSkipPublicShareWidget({
|
|
htmlFilePath: '/Users/john/Project/Memind/public/learn/child.html',
|
|
}),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldSkipPublicShareWidget({
|
|
relativePath: 'public/learning-assistant.html',
|
|
}),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldSkipPublicShareWidget({
|
|
requestPath: '/MindSpace/u/public/learning-assistant-parent.html',
|
|
}),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldSkipPublicShareWidget({
|
|
pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/report.html',
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test('decorateMindSpacePublishedHtml skips plaza share widget on learning assistant pages', () => {
|
|
let shareInjected = false;
|
|
const baked = `<html><body><h1>Learn</h1><style id="mindspace-public-share-style">x</style><script>x</script></body></html>`;
|
|
const result = decorateMindSpacePublishedHtml({
|
|
html: baked,
|
|
embed: false,
|
|
htmlFilePath: '/tmp/MindSpace/u/public/learning-assistant.html',
|
|
context: {
|
|
origin: 'https://m.tkmind.cn',
|
|
pageUrl: 'https://m.tkmind.cn/MindSpace/u/public/learning-assistant.html',
|
|
pageDirUrl: 'https://m.tkmind.cn/MindSpace/u/public/',
|
|
fallbackImageUrl: '',
|
|
},
|
|
preparePublicationHtmlForEmbed: (value) => value,
|
|
injectOgTags: (value) => value,
|
|
injectWechatShareBridge: (value) => value,
|
|
injectPublicFileShareButton: (value) => {
|
|
shareInjected = true;
|
|
return { html: `${value}<!--share-->`, scriptHashes: [] };
|
|
},
|
|
publishedPageCsp: (value) => value,
|
|
isWechatUserAgent: () => false,
|
|
});
|
|
|
|
assert.equal(shareInjected, false);
|
|
assert.doesNotMatch(result.html, /mindspace-public-share/);
|
|
});
|
|
|
|
test('decorateMindSpacePublishedHtml supports embed mode without share injection', () => {
|
|
const result = decorateMindSpacePublishedHtml({
|
|
html: '<html></html>',
|
|
embed: true,
|
|
context: { origin: '', pageUrl: '', pageDirUrl: '', fallbackImageUrl: '' },
|
|
preparePublicationHtmlForEmbed: (value) => `${value}<!--embed-->`,
|
|
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 injects noindex when seo geo enabled for private publication', () => {
|
|
const result = decorateMindSpacePublishedHtml({
|
|
html: '<html><head><title>Demo</title></head><body></body></html>',
|
|
embed: false,
|
|
seoGeoConfig: {
|
|
enabled: true,
|
|
seo: { enabled: true, canonical: true },
|
|
geo: { enabled: true, jsonLd: true },
|
|
},
|
|
publicationSnapshot: {
|
|
status: 'online',
|
|
accessMode: 'password',
|
|
userConfirmedAt: Date.now(),
|
|
},
|
|
context: {
|
|
origin: 'https://m.tkmind.cn',
|
|
pageUrl: 'https://m.tkmind.cn/u/john/pages/demo',
|
|
pageDirUrl: 'https://m.tkmind.cn/u/john/pages/',
|
|
fallbackImageUrl: '',
|
|
},
|
|
preparePublicationHtmlForEmbed: (value) => value,
|
|
injectOgTags: (value) => value,
|
|
injectWechatShareBridge: (value) => value,
|
|
injectPublicFileShareButton: (value) => ({ html: value, scriptHashes: [] }),
|
|
publishedPageCsp: () => 'csp',
|
|
isWechatUserAgent: () => false,
|
|
});
|
|
|
|
assert.match(result.html, /noindex, nofollow/);
|
|
assert.equal(result.robotsHeader, 'noindex, nofollow');
|
|
});
|
|
|
|
test('decorateMindSpacePublishedHtml does not break inline scripts containing </body>', () => {
|
|
const pageScript = "var x='<html><body>'+tb+'</body></html>';";
|
|
const result = decorateMindSpacePublishedHtml({
|
|
html: `<html><body><script>${pageScript}</script><p>demo</p></body></html>`,
|
|
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(/<script(?![^>]*\bsrc)[^>]*>([\s\S]*?)<\/script>/gi)].pop()[1];
|
|
new Function(inline);
|
|
});
|
|
});
|