|
@@ -0,0 +1,103 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * 验证「markdown → 纯文本」(回答的复制按钮用的那份实现)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * 用户 2026-09-22 反馈:复制出来的内容还带着 `###`、`**`、`[文字](链接)` 这些语法符号。
|
|
|
|
|
+ *
|
|
|
|
|
+ * 怎么跑(先打包):
|
|
|
|
|
+ * npx esbuild harness/tools/_entry-plain-text.ts --bundle --format=esm \
|
|
|
|
|
+ * --outfile=harness/tools/_plain-text.mjs --alias:@=./src --define:import.meta.env='{}'
|
|
|
|
|
+ * node harness/tools/verify-markdown-plain-text.mjs
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+import { readFileSync } from 'node:fs';
|
|
|
|
|
+
|
|
|
|
|
+const { markdownToPlainText } = await import('./_plain-text.mjs');
|
|
|
|
|
+
|
|
|
|
|
+let pass = 0;
|
|
|
|
|
+let fail = 0;
|
|
|
|
|
+const check = (name, ok, extra = '') => {
|
|
|
|
|
+ if (ok) {
|
|
|
|
|
+ pass++;
|
|
|
|
|
+ console.log(' ok ' + name);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fail++;
|
|
|
|
|
+ console.log(' FAIL ' + name + ' ' + extra);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/** markdown 语法符号:这些出现在复制结果里就是没剥干净 */
|
|
|
|
|
+const MD_SYNTAX = [/^#{1,6}\s/m, /\*\*[^*]+\*\*/, /\[[^\]]+\]\([^)]+\)/, /^\s*[-*+]\s/m, /`[^`]+`/];
|
|
|
|
|
+
|
|
|
|
|
+const hasMdSyntax = (text) => MD_SYNTAX.find((re) => re.test(text));
|
|
|
|
|
+
|
|
|
|
|
+console.log('\n【1】用户实际复制到的那份内容的形态');
|
|
|
|
|
+{
|
|
|
|
|
+ const md = `### 一、市级通用查询与申兑
|
|
|
|
|
+
|
|
|
|
|
+**办理渠道**
|
|
|
|
|
+
|
|
|
|
|
+- “随申兑”惠企政策平台:https://zwdt.sh.gov.cn/qykj
|
|
|
|
|
+- “上海企业登记在线”的“中小企业公共资助项目”栏目
|
|
|
|
|
+
|
|
|
|
|
+**办理流程**
|
|
|
|
|
+
|
|
|
|
|
+1. 登录上述平台,按行业、规模等条件匹配可申报项目。
|
|
|
|
|
+2. 按项目要求在线提交申请。
|
|
|
|
|
+
|
|
|
|
|
+### 四、ESG惠利贷(江苏银行上海青浦支行)
|
|
|
|
|
+
|
|
|
|
|
+- 额度与期限:0-10000万元,6-12个月。
|
|
|
|
|
+
|
|
|
|
|
+[查看产品详情](https://csjsfqeqr.yddeltazwh.org.cn/financeProduct_detail?id=317)`;
|
|
|
|
|
+
|
|
|
|
|
+ const text = markdownToPlainText(md);
|
|
|
|
|
+ if (process.env.DBG) console.log(JSON.stringify(text));
|
|
|
|
|
+ check('不含任何 markdown 语法符号', !hasMdSyntax(text), JSON.stringify(text).slice(0, 160));
|
|
|
|
|
+ check('标题只留文字(去掉 ###)', text.includes('一、市级通用查询与申兑') && !text.includes('###'));
|
|
|
|
|
+ check('加粗只留文字(去掉 **)', text.includes('办理渠道') && !text.includes('**'));
|
|
|
|
|
+ check('无序列表去掉 `- `,各项各占一行', text.includes('\n“随申兑”惠企政策平台:https://zwdt.sh.gov.cn/qykj\n'), JSON.stringify(text.slice(0, 120)));
|
|
|
|
|
+ check('**有序列表保留序号**(顺序是内容,不是装饰)', text.includes('1. 登录上述平台') && text.includes('2. 按项目要求在线提交申请'));
|
|
|
|
|
+ check(
|
|
|
|
|
+ '链接写成「文字(地址)」——**地址不能丢**',
|
|
|
|
|
+ text.includes('查看产品详情(https://csjsfqeqr.yddeltazwh.org.cn/financeProduct_detail?id=317)'),
|
|
|
|
|
+ JSON.stringify(text.slice(-120))
|
|
|
|
|
+ );
|
|
|
|
|
+ check('纯文字链接保持原样', text.includes('“随申兑”惠企政策平台:https://zwdt.sh.gov.cn/qykj'));
|
|
|
|
|
+ check('段落之间空一行', text.includes('一、市级通用查询与申兑\n\n办理渠道'));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+console.log('\n【2】边界');
|
|
|
|
|
+{
|
|
|
|
|
+ check('空串 → 空串', markdownToPlainText('') === '' && markdownToPlainText(' ') === '');
|
|
|
|
|
+ check('纯文本原样返回', markdownToPlainText('就是一句话。') === '就是一句话。');
|
|
|
|
|
+ check('行内代码去掉反引号', markdownToPlainText('用 `npm run dev` 启动') === '用 npm run dev 启动');
|
|
|
|
|
+ check('斜体去掉星号', markdownToPlainText('这是 *重点* 内容') === '这是 重点 内容');
|
|
|
|
|
+ check('引用去掉 > 符号', markdownToPlainText('> 引用一句') === '引用一句');
|
|
|
|
|
+ check('分隔线不留下垃圾', markdownToPlainText('甲\n\n---\n\n乙') === '甲\n\n乙', JSON.stringify(markdownToPlainText('甲\n\n---\n\n乙')));
|
|
|
|
|
+ check('data URL 图片不复制出去', !markdownToPlainText('看图 ').includes('base64'));
|
|
|
|
|
+ check('链接文字与地址相同时不重复', markdownToPlainText('[https://a.cn](https://a.cn)') === 'https://a.cn');
|
|
|
|
|
+ check('多余空行被收敛', markdownToPlainText('甲\n\n\n\n乙') === '甲\n\n乙');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+console.log('\n【3】拿真实回答跑一遍(DMS 里存的那一份)');
|
|
|
|
|
+{
|
|
|
|
|
+ // 真实内容:前面几轮从 DMS 读回来过的那类长回答(含标题/加粗/列表/链接)
|
|
|
|
|
+ const file = 'harness/tools/_real-answer.txt';
|
|
|
|
|
+ let md = '';
|
|
|
|
|
+ try {
|
|
|
|
|
+ md = readFileSync(file, 'utf8');
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ /* 没有就跳过 */
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!md.trim()) {
|
|
|
|
|
+ console.log(` ⏭ 没有 ${file}(把 DMS 里的 c_answer 落盘到这里即可跑这条)`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const text = markdownToPlainText(md);
|
|
|
|
|
+ const hit = hasMdSyntax(text);
|
|
|
|
|
+ check('真实回答里没有残留的 md 语法符号', !hit, hit ? String(hit) : '');
|
|
|
|
|
+ check('真实回答的文字没被吃掉(长度接近)', text.length > md.length * 0.5, `${md.length} → ${text.length}`);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+console.log(`\n===== 通过 ${pass} 项,失败 ${fail} 项 =====`);
|
|
|
|
|
+process.exit(fail ? 1 : 0);
|