Files
2026-09-11 08:56:39 +08:00

264 lines
17 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
set -eu
SKILL_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
NODE=${NODE:-node}
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/gongwen-test.XXXXXX")
trap 'rm -rf "$TMP_DIR"' EXIT
GEN="$SKILL_DIR/scripts/generate_gongwen_docx.mjs"
VERIFY="$SKILL_DIR/scripts/verify_gongwen_docx.mjs"
FIXTURE="$SKILL_DIR/tests/fixture.md"
"$NODE" --check "$GEN"
"$NODE" --check "$VERIFY"
"$NODE" --check "$SKILL_DIR/tests/measure-header-coordinates.mjs"
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/layout.docx" --format ordinary --org "不应变红头的单位" --title "公文格式回归测试" --page-number center
"$NODE" "$VERIFY" --input "$TMP_DIR/layout.docx" --profile ordinary
unzip -t "$TMP_DIR/layout.docx" >/dev/null
! unzip -p "$TMP_DIR/layout.docx" word/document.xml | grep -q 'FF0000'
unzip -p "$TMP_DIR/layout.docx" word/styles.xml | grep -q 'styleId="Heading4"'
unzip -p "$TMP_DIR/layout.docx" word/document.xml | grep -q 'w:pStyle w:val="Heading4"'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/standard.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --org "示例单位文件" --doc-no "示例发〔20261号" --title "公文格式回归测试" --page-number standard
"$NODE" "$VERIFY" --input "$TMP_DIR/standard.docx" --profile formal --letterhead preprinted
! unzip -p "$TMP_DIR/standard.docx" word/document.xml | grep -q '示例单位文件'
unzip -p "$TMP_DIR/standard.docx" word/document.xml | grep -q '示例发〔20261号'
unzip -p "$TMP_DIR/standard.docx" word/document.xml | grep -q 'w:snapToGrid w:val="false"'
python3 - "$TMP_DIR/standard.docx" <<'PY'
import re
import sys
import zipfile
with zipfile.ZipFile(sys.argv[1]) as archive:
xml = archive.read("word/document.xml").decode()
paragraphs = re.findall(r'<w:p>[\s\S]*?</w:p>', xml)
doc_no_index = next(i for i, paragraph in enumerate(paragraphs) if '示例发〔20261号' in paragraph)
gap = paragraphs[doc_no_index - 2:doc_no_index]
assert len(gap) == 2
assert all('w:line="560"' in paragraph and 'w:lineRule="exact"' in paragraph and 'w:snapToGrid w:val="false"' in paragraph for paragraph in gap)
PY
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/preprinted-minimum.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 37 --doc-no "示例发〔20267号" --title "最小预印留白测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/preprinted-minimum.docx" --profile formal --letterhead preprinted
python3 - "$TMP_DIR/preprinted-minimum.docx" "$TMP_DIR/preprinted-malformed.docx" <<'PY'
import sys, zipfile
source, target = sys.argv[1:]
with zipfile.ZipFile(source) as src, zipfile.ZipFile(target, 'w', zipfile.ZIP_DEFLATED) as dst:
for item in src.infolist():
data = src.read(item.filename)
if item.filename == 'word/document.xml':
text = data.decode()
old = '<w:spacing w:before="0" w:after="0" w:line="1" w:lineRule="exact"/>'
assert text.count(old) == 1
text = text.replace(old, '<w:spacing w:before="0" w:after="0" w:line="560" w:lineRule="exact"/>', 1)
data = text.encode()
dst.writestr(item, data)
PY
if "$NODE" "$VERIFY" --input "$TMP_DIR/preprinted-malformed.docx" --profile formal >"$TMP_DIR/preprinted-malformed.out" 2>&1; then
echo "malformed preprinted reserve unexpectedly passed" >&2
exit 1
fi
grep -q 'Preprinted letterhead reserve' "$TMP_DIR/preprinted-malformed.out"
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/digital.docx" --format formal --letterhead digital --org "示例单位文件" --doc-no "示例发〔20261号" --title "电子红头测试" --page-number standard
"$NODE" "$VERIFY" --input "$TMP_DIR/digital.docx" --profile formal --letterhead digital
unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q '示例单位文件'
unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'FF0000'
unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'height:0.5mm'
unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'w:framePr.*w:hAnchor="margin".*w:vAnchor="margin".*w:xAlign="center".*w:y="1984"'
unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'w:before="0"'
unzip -p "$TMP_DIR/digital.docx" word/document.xml | grep -q 'w:snapToGrid w:val="false"'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/digital-fields.docx" --format formal --letterhead digital --copy-no 7 --secret "机密★3年" --urgent "特急" --org "示例单位文件" --doc-no "示例发〔20263号" --title "版头字段定位测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/digital-fields.docx" --profile formal --letterhead digital
unzip -p "$TMP_DIR/digital-fields.docx" word/document.xml | grep -q 'w:framePr.*w:hAnchor="margin".*w:vAnchor="margin".*w:xAlign="left".*w:y="0"'
unzip -p "$TMP_DIR/digital-fields.docx" word/document.xml | grep -q 'w:framePr.*w:hAnchor="margin".*w:vAnchor="margin".*w:xAlign="left".*w:y="560"'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/secret-only.docx" --format formal --letterhead digital --secret "机密★3年" --org "示例单位文件" --doc-no "示例发〔20265号" --title "密级首行留空测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/secret-only.docx" --profile formal --letterhead digital
unzip -p "$TMP_DIR/secret-only.docx" word/document.xml | grep -q 'w:framePr.*w:vAnchor="margin".*w:y="560"'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/preprinted-fields.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --copy-no 7 --secret "机密★3年" --urgent "特急" --doc-no "示例发〔20264号" --title "预印字段定位测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/preprinted-fields.docx" --profile formal --letterhead preprinted
unzip -p "$TMP_DIR/preprinted-fields.docx" word/document.xml | grep -q 'w:before="1984"'
cat >"$TMP_DIR/attachment.md" <<'EOF'
# 附件一:测试附件
附件正文。
EOF
cat >"$TMP_DIR/main-send.md" <<'EOF'
各主送机关:
主送机关只应在输出中出现一次。
EOF
"$NODE" "$GEN" --input "$TMP_DIR/main-send.md" --output "$TMP_DIR/main-send.docx" --format formal --letterhead digital --org "示例单位文件" --doc-no "示例发〔20262号" --title "主送机关匹配测试" --to "各主送机关"
test "$(unzip -p "$TMP_DIR/main-send.docx" word/document.xml | grep -o '各主送机关' | wc -l | tr -d ' ')" -eq 1
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/upward.docx" --format formal --letterhead digital --org "示例单位文件" --doc-no "示例发〔20261号" --title "上行文测试" --upward true --signer "张三"
"$NODE" "$VERIFY" --input "$TMP_DIR/upward.docx" --profile formal --letterhead digital --upward true
unzip -p "$TMP_DIR/upward.docx" word/document.xml | grep -q '签发人:'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/letter.docx" --format letter --org "示例机关" --doc-no "示例〔20261号" --title "信函测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/letter.docx" --profile letter
unzip -l "$TMP_DIR/letter.docx" | grep -q 'word/footerLetter.xml'
unzip -p "$TMP_DIR/letter.docx" word/footerLetter.xml | grep -q 'w:fill="FF0000"'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/with-attachment.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔20261号" --title "附件测试" --attachment-note "1. 测试附件" --attachment-file "$TMP_DIR/attachment.md"
unzip -p "$TMP_DIR/with-attachment.docx" word/document.xml | grep -q '附件:1. 测试附件'
unzip -p "$TMP_DIR/with-attachment.docx" word/document.xml | grep -q 'w:pageBreakBefore'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/detached-attachment.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔20261号" --title "分离附件测试" --attachment-note "1. 测试附件" --attachment-file "$TMP_DIR/attachment.md" --attachment-detached
unzip -p "$TMP_DIR/detached-attachment.docx" word/document.xml | grep -q '示例发〔20261号 附件1'
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/bad-attachment.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔20266号" --title "附件一致性测试" --attachment-note "1. 另一份附件" --attachment-file "$TMP_DIR/attachment.md" >"$TMP_DIR/bad-attachment.out" 2>&1; then
echo "mismatched attachment note unexpectedly succeeded" >&2
exit 1
fi
grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-attachment.out"
for bad_option in format letterhead page-number; do
output="$TMP_DIR/invalid-$bad_option.out"
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/invalid-$bad_option.docx" --format ordinary --"$bad_option" "invalid-value" >"$output" 2>&1; then
echo "invalid --$bad_option unexpectedly succeeded" >&2
exit 1
fi
grep -q -- "--$bad_option" "$output"
done
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/command.docx" --format command --org "示例机关命令" --doc-no "第1号" --title "命令正文"
"$NODE" "$VERIFY" --input "$TMP_DIR/command.docx" --profile command
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/bad-command.docx" --format command --org "示例机关" --doc-no "第1号" --title "命令正文" >"$TMP_DIR/bad-command.out" 2>&1; then
echo "command without 命令/令 unexpectedly succeeded" >&2
exit 1
fi
grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-command.out"
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/minutes.docx" --format minutes --org "示例机关纪要" --title "纪要正文" --attendees "张三、李四" --absent "王五"
"$NODE" "$VERIFY" --input "$TMP_DIR/minutes.docx" --profile minutes
minutes_xml=$(unzip -p "$TMP_DIR/minutes.docx" word/document.xml)
test "${minutes_xml%%出席:*}" != "$minutes_xml"
test "${minutes_xml%%w:sectPr*}" != "$minutes_xml"
test "${minutes_xml%%出席:*}" != "${minutes_xml%%w:sectPr*}"
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/seal-layout.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --title "签章位置测试" --sender "示例机关" --date "2026年9月10日" --seal-mode seal
python3 - "$TMP_DIR/seal-layout.docx" <<'PY'
import sys, zipfile
from xml.etree import ElementTree as ET
ns={'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
with zipfile.ZipFile(sys.argv[1]) as z:
root=ET.fromstring(z.read('word/document.xml'))
for p in root.findall('.//w:p', ns):
if ''.join(p.itertext()).strip() == '2026年9月10日':
assert p.find('./w:pPr/w:ind', ns).attrib['{http://schemas.openxmlformats.org/wordprocessingml/2006/main}right'] == '1280'
break
else:
raise SystemExit('seal date paragraph with four-character right indent not found')
PY
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/signed-layout.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --title "签名章位置测试" --date "2026年9月10日" --seal-mode signed --signer-title "主要负责人" --signer "张三"
python3 - "$TMP_DIR/signed-layout.docx" <<'PY'
import sys, zipfile
from xml.etree import ElementTree as ET
ns={'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
with zipfile.ZipFile(sys.argv[1]) as z:
root=ET.fromstring(z.read('word/document.xml'))
for p in root.findall('.//w:p', ns):
if ''.join(p.itertext()).strip() == '2026年9月10日':
assert p.find('./w:pPr/w:ind', ns).attrib['{http://schemas.openxmlformats.org/wordprocessingml/2006/main}right'] == '1280'
break
else:
raise SystemExit('signed date paragraph with four-character right indent not found')
PY
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/bad-doc-no.docx" --format formal --letterhead digital --org "示例单位文件" --doc-no "示例发〔202601号" --title "非法文号" >"$TMP_DIR/bad-doc-no.out" 2>&1; then
echo "invalid document number unexpectedly succeeded" >&2
exit 1
fi
grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-doc-no.out"
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/bad-date.docx" --format formal --letterhead preprinted --date "2026年09月10日" --title "非法日期" >"$TMP_DIR/bad-date.out" 2>&1; then
echo "invalid date unexpectedly succeeded" >&2
exit 1
fi
grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-date.out"
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/letter-fields.docx" --format letter --org "示例机关" --copy-no 7 --secret "机密★3年" --urgent "特急" --doc-no "示例〔20262号" --title "信函字段边界"
"$NODE" "$VERIFY" --input "$TMP_DIR/letter-fields.docx" --profile letter
unzip -p "$TMP_DIR/letter-fields.docx" word/document.xml | grep -q '000007'
unzip -p "$TMP_DIR/letter-fields.docx" word/document.xml | grep -q '机密★3年'
"$NODE" "$GEN" --input "$SKILL_DIR/tests/fixtures/visual-horizontal.md" --output "$TMP_DIR/horizontal-table.docx" --format horizontal-table --title "横排表格测试" --page-number standard
"$NODE" "$VERIFY" --input "$TMP_DIR/horizontal-table.docx" --profile horizontal-table
unzip -p "$TMP_DIR/horizontal-table.docx" word/document.xml | grep -q 'w:orient="landscape"'
unzip -p "$TMP_DIR/horizontal-table.docx" word/document.xml | grep -q 'w:tblW w:w="12756"'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/joint.docx" --format formal --letterhead digital --org "主办机关文件" --joint-org "协办机关" --doc-no "示例发〔20263号" --title "联合行文测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/joint.docx" --profile formal --letterhead digital
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q '主办机关'
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q '协办机关'
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q '文件'
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q 'w:sz w:val="48"'
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/joint-four-agencies.docx" --format formal --letterhead digital --org "主办机关文件" --joint-org "协办机关一" --joint-org "协办机关二" --joint-org "协办机关三" --doc-no "示例发〔20264号" --title "四机关联合行文测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/joint-four-agencies.docx" --profile formal --letterhead digital
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/joint-without-file-suffix.docx" --format formal --letterhead digital --org "主办机关" --joint-org "协办机关" --doc-no "示例发〔20265号" --title "无文件后缀联合行文测试"
"$NODE" "$VERIFY" --input "$TMP_DIR/joint-without-file-suffix.docx" --profile formal --letterhead digital
python3 - "$TMP_DIR/joint.docx" "$TMP_DIR/joint-four-agencies.docx" <<'PY'
import re
import sys
import zipfile
for filename, expected_count in ((sys.argv[1], 2), (sys.argv[2], 4)):
with zipfile.ZipFile(filename) as archive:
xml = archive.read("word/document.xml").decode()
tables = re.findall(r'<w:tbl>[\s\S]*?<w:tblpPr[^>]*w:tblpY="1984"[\s\S]*?</w:tbl>', xml)
assert len(tables) == 1, filename
table = tables[0]
assert table.count('<w:vAlign w:val="center"/>') >= 2, filename
assert table.count('<w:tc>') == 2, filename
assert table.count('<w:p>') == expected_count + 1, filename
assert '<w:t xml:space="preserve">文件</w:t>' in table, filename
PY
python3 - "$TMP_DIR/joint-without-file-suffix.docx" <<'PY'
import re
import sys
import zipfile
with zipfile.ZipFile(sys.argv[1]) as archive:
xml = archive.read("word/document.xml").decode()
tables = re.findall(r'<w:tbl>[\s\S]*?<w:tblpPr[^>]*w:tblpY="1984"[\s\S]*?</w:tbl>', xml)
assert len(tables) == 1
table = tables[0]
assert table.count('<w:vAlign w:val="center"/>') >= 1
assert '<w:t xml:space="preserve">文件</w:t>' not in table
PY
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/missing-joint-name.docx" --format formal --letterhead digital --org "主办机关文件" --joint-org --doc-no "示例发〔20264号" --title "缺少联署机关名称" >"$TMP_DIR/missing-joint-name.out" 2>&1; then
echo "missing joint agency name unexpectedly succeeded" >&2
exit 1
fi
grep -q 'GENERATOR ERROR: --joint-org requires a value' "$TMP_DIR/missing-joint-name.out"
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/strict.docx" --format formal --letterhead digital --org "示例单位文件" --title "严格字体测试" --require-standard-fonts >"$TMP_DIR/strict.out" 2>&1; then
if ! fc-list -f '%{family}\n' | grep -Eq '方正小标宋简体|方正小标宋_GBK|FZXiaoBiaoSong'; then
echo "strict font mode unexpectedly succeeded without a small-standard-title font" >&2
exit 1
fi
elif ! grep -q 'FONT ERROR:' "$TMP_DIR/strict.out"; then
cat "$TMP_DIR/strict.out" >&2
echo "strict font mode failed for an unexpected reason" >&2
exit 1
fi
if command -v soffice >/dev/null 2>&1; then
mkdir -p "$TMP_DIR/pdf"
soffice --headless --convert-to pdf --outdir "$TMP_DIR/pdf" "$TMP_DIR/layout.docx" >/dev/null 2>&1
test -s "$TMP_DIR/pdf/layout.pdf"
fi
printf '%s\n' "gongwen regression tests passed"