Zettlr 图形界面测试环境完全指南:yarn test-gui的目录结构与二次开发实战
【免费下载链接】ZettlrYour One-Stop Publication Workbench项目地址: https://gitcode.com/GitHub_Trending/ze/Zettlr
导读
本文聚焦 Zettlr 仓库中承载"图形界面(GUI)自动化/手动测试"的测试环境:当你执行yarn test-gui时,Zettlr 会通过 scripts/test-gui/index.mjs 自动构建一个全新的、带独立配置的测试实例,并把 scripts/test-gui/test-files 目录下精心设计的"虚拟文档集"复制进运行时目录,供开发者在本地反复验证 Markdown 渲染、语法高亮、导出、引用管理等能力。读完本文,你将掌握:该测试环境的目录结构与设计哲学、yarn test-gui的完整启动链路、--clean与--no-config等命令行参数的真实语义、如何新增测试用例并提交 Pull Request,以及测试文件在渲染、语法高亮、表格编辑、导出过滤器等模块中的具体覆盖范围。
1. 测试环境是什么:从yarn test-gui说起
Zettlr 的 package.json 中定义了两条与测试环境直接相关的脚本:
"start": "cross-env NODE_ENV=develop node scripts/test-gui/index.mjs", "test-gui": "cross-env NODE_ENV=develop node scripts/test-gui/index.mjs"两者的入口完全相同:以开发模式(NODE_ENV=develop)启动 scripts/test-gui/index.mjs。也就是说,Zettlr 日常的"本地起应用调试"(yarn start)本身就是启动这套 GUI 测试环境。这意味着每位开发者在克隆仓库后第一次运行应用时,见到的并不是空工作区,而是一整套预先排布好的测试目录——这正是 scripts/test-gui/test-files/README.md 所说的 "Zettlr Testing Environment"。
该 README 明确给出了两个信息点:
- 看到这个页面并不代表出错:测试文件里大量使用了 "lorem ipsum" 之类的占位文本,属于刻意为之的假数据,与测试目的无关。
- 如果在意料之外看到它,则说明环境可能有问题:正常情况下你只会因为主动运行
yarn test-gui才进入该环境。
1.1 测试文件是可写可重置的"假数据"
README 强调:测试目录中的任何文件都可以随意修改,因为它们是 dummy files(虚拟文件),每次需要重置时都会被重新复制。关键约束如下:
该目录位于应用的
./resources子目录中,因此不会被加入 git。如果你想把它重置为初始状态(比如你删光了所有文件,或做了别的"naughty thing"),可以给命令加上--clean参数:yarn test-gui --clean。
这与源码实现完全一致:scripts/test-gui/index.mjs 中定义了三个运行时路径常量:
const TEST_DIRECTORY = path.join(__dirname, '../../resources/test') const CONF_DIRECTORY = path.join(__dirname, '../../resources/test-cfg') const CONFIG_FILE = path.join(__dirname, '../../resources/test-cfg/config.json')即运行时测试目录落在仓库根目录下的resources/test,配置目录落在resources/test-cfg,二者均不在 git 版本控制范围内(与仓库内被跟踪的静态资源目录 static 严格分离)。
2. 核心命令行参数:--clean与--no-config
scripts/test-gui/index.mjs 的头部注释列出了它支持的参数:
// SUPPORTED COMMAND LINE ARGUMENTS // * --clean: Remove and recreate the test files. Adds a custom config. // * --no-config: Must be used in conjunction with --clean, does not create a // config file.2.1--clean:核平并重建测试环境
不带任何参数运行yarn test-gui时,脚本会保留resources/test与resources/test-cfg的现有内容直接启动应用,这在测试"设置持久化"(settings persistence)时非常有用——你上次手动调整过的配置会被保留下来。
而加上--clean后,入口逻辑(scripts/test-gui/index.mjs 第 37-53 行)会:
- 用
rimraf递归删除旧的resources/test测试目录(若存在); - 同样删除旧的
resources/test-cfg数据目录; - 调用
prepareEnvironment(argv)重建环境,然后启动应用。
源码中prepareEnvironment的实现顺序为:
// First, remove the ./resources/test folder try { await fs.lstat(TEST_DIRECTORY) await rimraf(TEST_DIRECTORY) success('Removed the old testing directory.') } catch (e) { // Nothing to do verbose('No old testing directory found.') }随后对CONF_DIRECTORY执行同样的清理,再通过copyFolder把测试文件复制进resources/test。如果旧目录不存在,代码会静默跳过(输出 verbose 级别的提示),因此无论环境是否被污染,--clean都能安全执行。
2.2--no-config:跳过配置文件生成
--no-config必须与--clean搭配使用,语义为"重建测试文件,但不生成新的配置文件"。在prepareEnvironment中:
if (argv.includes('--no-config')) { info('Not creating config file.') argv.splice(argv.indexOf('--no-config'), 1) return }不传此参数时,脚本会从test-config.example.yml读取模板、解析为 JSON 后写入resources/test-cfg/config.json,并注入两个关键字段:
cfg.app = { openFiles: files, // 测试目录下的所有文件,作为默认打开的文件 openWorkspaces: workspaces // 测试目录下的所有子目录,作为默认打开的工作区 } cfg.dialogPaths = { askFileDialog: TEST_DIRECTORY, askDirDialog: TEST_DIRECTORY, askLangFileDialog: TEST_DIRECTORY }这里files与workspaces来自 scripts/test-gui/copy-folder.mjs:复制完成后读取目标目录的顶层条目,按是否为目录拆分成"文件"(直接作为打开文档)与"工作区"(作为侧边栏项目根)。同时把三个文件对话框的起始路径都指向测试目录,方便在测试中快速导航。
2.3 配置文件模板:test-config.example.yml
scripts/test-gui/test-config.example.yml 是整个测试配置的源头:
# This is a test configuration file that will be read and parsed to JSON during # the preparation of the GUI-test environment. It is a stub, because Zettlr will # correctly fill in all missing values. Copy this to the file test-config.yml # and adapt to your likings. The test-config.yml will not be committed to git, # so it will stay persistent between pulls. # For all possible values, please see the file # source/app/service-providers/config/get-config-template.ts debug: true checkForBeta: true要点:
- 它是一个存根(stub):只声明
debug: true与checkForBeta: true两个键,其余所有缺失配置由 Zettlr 在启动时按默认模板补齐; - 完整取值清单见 source/app/service-providers/config/get-config-template.ts;
- 若你想自定义测试配置,把该文件复制为仓库根目录下的
test-config.yml(注意不是resources内)即可。scripts/test-gui/make-config.mjs 的加载顺序是:若根目录已存在test-config.yml则直接读取,否则复制示例文件再读取;test-config.yml不会被提交到 git,因此跨pull保持不变。
3. 启动链路:从脚本到 Electron 实例
理解完整的调用链有助于排查"测试环境起不来"的问题。index.mjs最后调用startApp:
const command = (process.platform === 'win32') ? '.\\node_modules\\.bin\\electron-forge.cmd' : 'electron-forge' const forgeArgs = [ 'start', '--', `--data-dir="${CONF_DIRECTORY}"`, ...argv ] const spawnOptions = { shell: process.platform === "win32", cwd: path.join(__dirname, '../../'), stdio: [ process.stdin, process.stdout, process.stderr ] } const proc = spawn(command, forgeArgs, spawnOptions)即:
- 按平台选择
electron-forge(Windows 下为.cmd包装); - 以仓库根目录为工作目录,执行
electron-forge start -- --data-dir="resources/test-cfg",把独立数据目录传给应用; - 子进程的 stdin/stdout/stderr 直接透传到父进程,保证开发时能实时看到日志;
- 子进程退出时打印退出码,方便脚本化检测。
这意味着测试环境的"隔离性"来自两个层面:独立的测试文件目录(resources/test)与独立的用户数据目录(resources/test-cfg),两者都不影响开发者真实的 Zettlr 配置。
4. 测试目录结构:一张覆盖全功能的"功能检查表"
scripts/test-gui/test-files 下的目录组织本身就是一份功能覆盖清单,README 中的 Getting Started 建议从两个入口开始浏览:
- A Generic Markdown Document(对应 scripts/test-gui/test-files/Rendering/Generic Document 1.md)
- Syntax Highlighting(对应 scripts/test-gui/test-files/Syntax Highlighting/Start.md)
完整结构如下:
| 目录 | 测试覆盖范围 |
|---|---|
File System Abstraction Layer.md、non-ascii image.md等 | FSAL(文件系统抽象层)、非 ASCII 文件名/图片路径处理 |
Miscellaneous/ | 导出链接移除(LUA 过滤器)、脚注、链接解析、Pandoc 标题编号、可读性、排序、reveal.js 演示、自定义 LaTeX 模板 |
Rendering/ | 通用 Markdown 渲染(含 YAML frontmatter 元数据)、图片、数学公式、引用(Citations)、强调渲染等渲染问题 |
Syntax Highlighting/ | 按 highlight.js 分类法组织的十余类语言:配置、CSS、企业级、函数式、Lisp、标记、杂项、协议/数据格式、科学、脚本、系统编程 |
Table Editor/ | 表格编辑器:真实世界示例、压力测试、基础表格操作 |
Test Project/ | 完整的多文件项目示例(1 Intro.md、2 Main Part.md),用于测试项目维度行为 |
此外test-library.json(scripts/test-gui/test-files/test-library.json)是一个 JSON 格式的引文库(CSL-JSON 结构,包含author、issued、publisher、collection-title等字段),用于测试引用管理(Citations)功能;assets/子目录则提供了渲染测试所需的图片资源。
4.1 通用 Markdown 文档:一条"语法百科"
scripts/test-gui/test-files/Rendering/Generic Document 1.md 堪称 Zettlr 渲染引擎的"语法百科",涵盖:
- YAML frontmatter:
title、多作者(author数组,含name/affiliation/email)、date、abstract、bibliography字段,用于验证元数据解析与文档属性面板; - 目录与锚点链接:
[Overview](#overview)式页面内锚点; - 文件间链接:
This is a link to the other generic markdown file这种指向同级文件的相对链接,用于验证链接解析(对应 source/common/modules/markdown-utils/plain-link-highlighter.ts 等链接处理模块); - 标题体系:Setext(下划线式)与 atx(
#式)两种风格混用; - 引用块:单段、懒标记(lazy)、嵌套引用、引用内含标题/列表/代码块;
- 列表:无序(
*/+/-混用)、有序(序号任意)、多段落列表项、列表内引用块与代码块(4/8 空格缩进规则); - 代码块:4 空格缩进式与围栏式(
```),以及代码块内 HTML 实体转义行为; - 行内元素:行内/引用式链接、单/双
*与_的强调、反引号行内代码; - 反向用例:
foo _barsome text in betweenbar_ foo这类"不应被渲染为强调"的边界情况,直接标注"Zettlr itself should not render the following"。
这些内容本质上复刻了 Markdown 官方语法文档,充当渲染回归测试的基准输入。
4.2 语法高亮目录:语言覆盖的"验收清单"
scripts/test-gui/test-files/Syntax Highlighting/Start.md 说明了该目录的编排规则:
该目录包含若干演示 Zettlr 所支持语法高亮的文件。语言按照 highlight.js 的分组方式组织,大部分示例取自该站点,另有一些来自 Wikipedia 或由我们自己编写。每当你新增一种语言时,请务必把它也加进这些测试中。
它给出 11 个入口文件:Config.md、CSS.md、Enterprise.md、Functional.md、Lisp.md、Markup.md、Miscellaneous.md、Protocols.md、Scientific.md、Scripting.md、System.md。
与之配套的仓库证据:Zettlr 在依赖中引入了大量 CodeMirror 语言包(@codemirror/lang-*系列,见 package.json),如lang-markdown、lang-css、lang-python、lang-rust、lang-go等,这些正是高亮测试所覆盖语言的后端实现。
4.3 导出测试:LUA 过滤器与模板
Miscellaneous 目录集中了导出链路的测试:
- Export Link Removal.md:用四种排列(独立行无空格链接
[[file]]、独立行含空格链接[[this is some file]]、行内无空格、行内含空格)测试导出时删除内部链接的 LUA 过滤器; - Pandoc Heading Numbering.md:验证 Pandoc 导出时标题编号;
- reveal.js Test.md:配合 scripts/assets/reveal-template.htm 模板验证幻灯片导出;
- test-template.tex:自定义 LaTeX 模板的导出测试。
对应到生产实现,Zettlr 的 Pandoc 导出由 source/app/service-providers/commands/exporter 模块驱动,LUA 过滤器则位于 static/lua-filter/links.lua 与 static/lua-filter/tags.lua,导出默认配置见 static/defaults 下的一系列*.yaml(如Markdown.yaml、HTML.yaml、XeLaTeX PDF.yaml等)。
5. 如何新增测试用例:贡献者流程
README 的 Adding more Test Cases 一节给出了明确的贡献指引:
如果你发现某个行为存在缺陷(broken behaviour),欢迎提交 Pull Request,通过新增/修改文件来覆盖这些边界情况(edge cases),以便我们在本地开发环境中轻松复现与测试。
实操步骤可以归纳为:
- 在 scripts/test-gui/test-files 的对应功能目录(Rendering / Syntax Highlighting / Table Editor / Miscellaneous …)下新增或修改 Markdown 测试文件;
- 若属于新的语法高亮语言,需同时更新 Syntax Highlighting 下的分类入口,遵循"语言分组参照 highlight.js"的约定;
- 本地运行
yarn test-gui(或yarn test-gui --clean重置环境)验证行为; - 提交 Pull Request,让维护者用同一套本地开发环境复现。
需要注意:测试环境的"内容重置"与 git 无关(运行目录在resources/下且被 gitignore),因此测试用例的修改必须落在scripts/test-gui/test-files/(受版本控制)而非运行时的resources/test/。
6. 与单元测试的分工:GUI 测试的定位
仓库内还有一套纯逻辑的单元测试,位于 test 目录(*.spec.ts),通过yarn test(mocha)运行,例如:
- make-search-regex.spec.ts:搜索正则编译;
- extract-yaml-frontmatter.spec.ts:YAML frontmatter 提取;
- table-editor-rows.spec.ts:表格编辑器行逻辑。
两者的定位差异在于:单元测试直接断言函数输入输出,而yarn test-gui启动真实 Electron 窗口,让开发者用眼睛和交互去验证渲染、高亮、编辑器行为等难以用断言覆盖的 UI 层面表现。测试目录中的文件正是把"每一个 UI 能力"固化为可复现操作样例的载体——README 中的两句话可以视作这套哲学的精髓:"Browse the files to test out the behaviour"(浏览文件以测试行为)与"we might miss out some features/potential bugs"(配置示例的维护是为了不错过特性与潜在 bug,见 scripts/test-gui/index.mjs 头注释)。
7. 快速上手:三种典型使用场景
场景 A:首次运行开发环境
yarn install yarn test-gui应用会以resources/test为工作区启动,侧边栏预置 Test Project 等目录,编辑器默认打开若干测试文档。
场景 B:把测试环境弄乱后想恢复原样
yarn test-gui --clean脚本会删除并重建resources/test与resources/test-cfg,重新生成配置并启动应用。
场景 C:自定义测试配置
cp scripts/test-gui/test-config.example.yml test-config.yml # 编辑 test-config.yml,例如追加配置键 yarn test-gui --clean之后test-config.yml会持续生效(且不会被 git 跟踪),所有缺失字段由 Zettlr 按 source/app/service-providers/config/get-config-template.ts 的默认模板补齐。
结语
Zettlr 的 GUI 测试环境是一套"以目录结构为测试清单、以假数据为测试输入、以真实 Electron 实例为运行载体"的轻量回归测试方案。理解 scripts/test-gui/index.mjs 的启动链路、--clean/--no-config参数语义以及 scripts/test-gui/test-files 的目录分工,不仅能帮你快速上手本地开发,也能让你在发现渲染、高亮、导出等缺陷时,第一时间把可复现样例沉淀为测试用例并提交 Pull Request——这正是该项目为贡献者铺设的低门槛测试路径。
【免费下载链接】ZettlrYour One-Stop Publication Workbench项目地址: https://gitcode.com/GitHub_Trending/ze/Zettlr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考