|
@@ -19,8 +19,15 @@
|
|
|
|
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
|
|
|
|
-const { buildAnsweredQuestionCardsContent, applyQuestionCardsAnswers, hasAnsweredQuestionCards } =
|
|
|
|
|
- await import('./_question-cards.mjs');
|
|
|
|
|
|
|
+const {
|
|
|
|
|
+ buildAnsweredQuestionCardsContent,
|
|
|
|
|
+ applyQuestionCardsAnswers,
|
|
|
|
|
+ hasAnsweredQuestionCards,
|
|
|
|
|
+ StreamXMLFilter,
|
|
|
|
|
+ RecordRowTypes,
|
|
|
|
|
+ parseQuestionsFromContent,
|
|
|
|
|
+ parseAnswersFromContent,
|
|
|
|
|
+} = await import('./_question-cards.mjs');
|
|
|
|
|
|
|
|
let pass = 0;
|
|
let pass = 0;
|
|
|
let fail = 0;
|
|
let fail = 0;
|
|
@@ -51,29 +58,25 @@ console.log('\n【1】把答案写回问卷行(写回后才会走只读渲染
|
|
|
const answers = [{ header: '本次是否需要结合公司情况?', selected: ['不需要公司信息'], freeText: '', skipped: false }];
|
|
const answers = [{ header: '本次是否需要结合公司情况?', selected: ['不需要公司信息'], freeText: '', skipped: false }];
|
|
|
const written = applyQuestionCardsAnswers(CONTENT, answers);
|
|
const written = applyQuestionCardsAnswers(CONTENT, answers);
|
|
|
|
|
|
|
|
- // QuestionCard 的只读分支:parseAnswersFromContent(row.content) → data.answers
|
|
|
|
|
- const rowContent = written.slice(
|
|
|
|
|
- written.indexOf('<question-cards>') + '<question-cards>'.length,
|
|
|
|
|
- written.lastIndexOf('</question-cards>')
|
|
|
|
|
- );
|
|
|
|
|
- let parsed = null;
|
|
|
|
|
- try {
|
|
|
|
|
- parsed = JSON.parse(rowContent);
|
|
|
|
|
- } catch {
|
|
|
|
|
- /* 下面断言会报出来 */
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // ⚠️ 这里必须用**组件真正用的那个解析函数**(`parseAnswersFromContent`,它会解包标记)。
|
|
|
|
|
+ // 上一版测试是手工把 `<question-cards>` 去掉再 JSON.parse —— 于是"渲染层交给组件的 content
|
|
|
|
|
+ // 是带标记的"这个事实被测试掩盖了,放过了真实的「卡片渲染成空」bug(见【5】)。
|
|
|
|
|
+ const parsedAnswers = parseAnswersFromContent(written);
|
|
|
|
|
|
|
|
- check('写回后仍是合法 JSON(只读分支靠 JSON.parse 读)', !!parsed, JSON.stringify(rowContent)?.slice(0, 120));
|
|
|
|
|
- check('含 answers 数组(只读分支的判定条件)', Array.isArray(parsed?.answers) && parsed.answers.length === 1);
|
|
|
|
|
|
|
+ check('写回后能被组件的只读分支解析出 answers', Array.isArray(parsedAnswers) && parsedAnswers.length === 1,
|
|
|
|
|
+ JSON.stringify(written)?.slice(0, 140));
|
|
|
check('答案形状与只读渲染一致(header / selected / freeText)',
|
|
check('答案形状与只读渲染一致(header / selected / freeText)',
|
|
|
- parsed?.answers?.[0]?.header === '本次是否需要结合公司情况?' &&
|
|
|
|
|
- JSON.stringify(parsed?.answers?.[0]?.selected) === JSON.stringify(['不需要公司信息']) &&
|
|
|
|
|
- parsed?.answers?.[0]?.freeText === '',
|
|
|
|
|
- JSON.stringify(parsed?.answers?.[0]));
|
|
|
|
|
|
|
+ parsedAnswers?.[0]?.header === '本次是否需要结合公司情况?' &&
|
|
|
|
|
+ JSON.stringify(parsedAnswers?.[0]?.selected) === JSON.stringify(['不需要公司信息']) &&
|
|
|
|
|
+ parsedAnswers?.[0]?.freeText === '',
|
|
|
|
|
+ JSON.stringify(parsedAnswers?.[0]));
|
|
|
|
|
+ check('写回后不代表"还有题目要作答"(交互分支不会再渲染)',
|
|
|
|
|
+ parseQuestionsFromContent(written).length === 0);
|
|
|
check('问卷外的前后内容没被动(scope 行仍在前、标签成对)',
|
|
check('问卷外的前后内容没被动(scope 行仍在前、标签成对)',
|
|
|
written.startsWith('<scope title="思考中">\n</scope>\n<question-cards>') && written.endsWith('</question-cards>'));
|
|
written.startsWith('<scope title="思考中">\n</scope>\n<question-cards>') && written.endsWith('</question-cards>'));
|
|
|
check('hasAnsweredQuestionCards 能识别', hasAnsweredQuestionCards(written) === true);
|
|
check('hasAnsweredQuestionCards 能识别', hasAnsweredQuestionCards(written) === true);
|
|
|
check('作答前不算已作答(只读分支不会误触发)', hasAnsweredQuestionCards(CONTENT) === false);
|
|
check('作答前不算已作答(只读分支不会误触发)', hasAnsweredQuestionCards(CONTENT) === false);
|
|
|
|
|
+ check('作答前能解析出题目(交互分支照常渲染)', parseQuestionsFromContent(CONTENT).length === 1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
console.log('\n【2】边界:不能破坏别人的内容');
|
|
console.log('\n【2】边界:不能破坏别人的内容');
|
|
@@ -133,5 +136,36 @@ console.log('\n【4】接线(源码级:这两处都在 Vue 组件 / hook 里
|
|
|
/const chosenByInteraction = !!isFreeformActive\.value\[cardIdx\]/.test(card));
|
|
/const chosenByInteraction = !!isFreeformActive\.value\[cardIdx\]/.test(card));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+console.log('\n【5】完整往返:真实切行器 → 组件拿到的 content → 解析(这条会抓「卡片渲染成空」)');
|
|
|
|
|
+{
|
|
|
|
|
+ const answers = [{ header: '(单选)本次是否需要结合公司情况?', selected: ['不需要公司信息'], freeText: '', skipped: false }];
|
|
|
|
|
+
|
|
|
|
|
+ /** 跑真实切行器,取问卷行的 content —— 渲染层就是这么把内容交给 QuestionCard 的 */
|
|
|
|
|
+ const questionCardsRowContent = (content) => {
|
|
|
|
|
+ const rows = new StreamXMLFilter({}).processChunk(content);
|
|
|
|
|
+ const rowsOfType = rows.filter((row) => row.type === RecordRowTypes.QuestionCards);
|
|
|
|
|
+ return rowsOfType.length ? rowsOfType[rowsOfType.length - 1].content : '';
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const rowBefore = questionCardsRowContent(CONTENT);
|
|
|
|
|
+ check('切行器产出的问卷行 content **带 `<question-cards>` 标记**(所以解析必须先解包)',
|
|
|
|
|
+ rowBefore.startsWith('<question-cards>') && rowBefore.endsWith('</question-cards>'), rowBefore.slice(0, 60));
|
|
|
|
|
+ check('未作答:交互分支解析得到题目', parseQuestionsFromContent(rowBefore).length === 1);
|
|
|
|
|
+
|
|
|
|
|
+ const rowAfter = questionCardsRowContent(applyQuestionCardsAnswers(CONTENT, answers));
|
|
|
|
|
+ const parsedAnswers = parseAnswersFromContent(rowAfter);
|
|
|
|
|
+ const parsedQuestions = parseQuestionsFromContent(rowAfter);
|
|
|
|
|
+ check('作答后:只读分支解析得到答案', Array.isArray(parsedAnswers) && parsedAnswers.length === 1,
|
|
|
|
|
+ JSON.stringify(rowAfter)?.slice(0, 140));
|
|
|
|
|
+ check('作答后:交互分支不再有题目', parsedQuestions.length === 0);
|
|
|
|
|
+ // ★ 这条是"卡片渲染成空"的直接守卫:两个分支至少有一个能渲染出东西
|
|
|
|
|
+ // (组件模板:`v-if="isReadonly && contentAnswers.length > 0"` / `v-else-if="questions.length > 0"`)
|
|
|
|
|
+ check('**至少有一个分支能渲染**(否则整张卡片是空的 —— 用户报的「提交完直接看不见了」)',
|
|
|
|
|
+ (parsedAnswers?.length || 0) > 0 || parsedQuestions.length > 0,
|
|
|
|
|
+ `answers=${parsedAnswers?.length ?? 'null'} questions=${parsedQuestions.length}`);
|
|
|
|
|
+ check('直接传「标记内 JSON」也要能解析(兼容不带标记的调用方)',
|
|
|
|
|
+ parseAnswersFromContent(JSON.stringify({ answers }))?.length === 1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
console.log(`\n===== 通过 ${pass} 项,失败 ${fail} 项 =====`);
|
|
console.log(`\n===== 通过 ${pass} 项,失败 ${fail} 项 =====`);
|
|
|
process.exit(fail ? 1 : 0);
|
|
process.exit(fail ? 1 : 0);
|