fix: judge evidence alt text with asset metadata, not syntax
The parser has no asset catalogue, so it cannot know whether an evidence figure is decorative. Move the empty-alt rejection from parse-time (a syntax error) to publish validation, where the mock treats static evidence assets as decorative: false since the static registry predates the Asset capability. The real rule is that the server judges alt against Asset.decorative.
This commit is contained in:
@@ -165,6 +165,11 @@ export function validateWorkingCopy(document: WorkingCopy, dependencies: Validat
|
||||
for (const block of parseCaseContent(document.bodyMarkdown)) if (block.type === "EVIDENCE_FIGURE") {
|
||||
if (!isSupportedEvidenceKey(block.key)) error("EVIDENCE_UNSUPPORTED", "/bodyMarkdown", `지원하지 않는 Evidence: ${block.key}`);
|
||||
else if (!dependencies.catalog.some((entry) => entry.type === "EVIDENCE" && (entry.id === block.key || entry.label === block.key || entry.publicPath === `/media/${block.key}.svg`))) error("EVIDENCE_NOT_FOUND", "/bodyMarkdown", `Evidence 없음: ${block.key}`);
|
||||
else if (block.alt.trim().length === 0) {
|
||||
// 정적 evidence 자산은 장식용이 아니다. Asset 기반 경로에서는 서버가
|
||||
// Asset.decorative로 같은 판정을 한다.
|
||||
error("EVIDENCE_ALT_REQUIRED", "/bodyMarkdown", `Evidence에 대체 텍스트가 필요합니다: ${block.key}`);
|
||||
}
|
||||
}
|
||||
} catch { error("CONTENT_FORMAT_INVALID", "/bodyMarkdown", "지원하는 문법을 사용하세요."); }
|
||||
if (!document.lastVerifiedOn) error("LAST_VERIFIED_ON_REQUIRED", "/lastVerifiedOn", "검증일을 입력하세요."); else if (dependencies.now.getTime() - Date.parse(`${document.lastVerifiedOn}T00:00:00Z`) > 30 * 86_400_000) warning("VERIFICATION_OLDER_THAN_30_DAYS", "/lastVerifiedOn", "30일이 지났습니다.");
|
||||
|
||||
@@ -514,7 +514,9 @@ function directiveBlock(
|
||||
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(attributes.key)) {
|
||||
invalid(node, `unsafe evidence key: ${attributes.key}`);
|
||||
}
|
||||
if (!attributes.alt) invalid(node, "evidence alt text is required");
|
||||
// alt 필요 여부는 Asset의 `decorative`에 달려 있어 여기서는 판단할 수
|
||||
// 없다. 빈 alt는 구문 오류가 아니며 publish 검증이 Asset metadata와 함께
|
||||
// 판정한다(설계 §7.7).
|
||||
if (attributes.zoom !== "true" && attributes.zoom !== "false") {
|
||||
invalid(node, "evidence zoom must be true or false");
|
||||
}
|
||||
|
||||
@@ -138,6 +138,32 @@ describe("Content Format v1", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("parses a decorative evidence figure with an empty alt", () => {
|
||||
const source =
|
||||
':::evidence key="fetch-strategy-boundary" alt="" caption="" zoom="false"\n:::';
|
||||
const [block] = parseCaseContent(source);
|
||||
|
||||
expect(block?.type).toBe("EVIDENCE_FIGURE");
|
||||
if (block?.type !== "EVIDENCE_FIGURE") return;
|
||||
expect(block.alt).toBe("");
|
||||
});
|
||||
|
||||
it("round-trips an empty alt without reintroducing it as a parse error", () => {
|
||||
const source =
|
||||
':::evidence key="fetch-strategy-boundary" alt="" caption="" zoom="false"\n:::';
|
||||
const parsed = parseCaseContent(source);
|
||||
|
||||
expect(() => parseCaseContent(serializeCaseContent(parsed))).not.toThrow();
|
||||
});
|
||||
|
||||
it("still rejects an unsafe evidence key", () => {
|
||||
expect(() =>
|
||||
parseCaseContent(
|
||||
':::evidence key="../etc" alt="x" caption="" zoom="false"\n:::',
|
||||
),
|
||||
).toThrow(/CONTENT_FORMAT_INVALID/);
|
||||
});
|
||||
|
||||
it("derives stable table column and row IDs", () => {
|
||||
const [table] = parseCaseContent(`:::table id="metrics" caption="측정" rowHeaderColumn="none"
|
||||
| 이름 | 수치 |
|
||||
|
||||
Reference in New Issue
Block a user