GDevelop 编辑器 UI 如何用 visual-tests 视觉冒烟测试验证不回归
2026/9/13 11:41:27 网站建设 项目流程

GDevelop 编辑器 UI 如何用 visual-tests 视觉冒烟测试验证不回归

【免费下载链接】GDevelop🎮 Open-source, cross-platform 2D/3D/multiplayer game engine designed for everyone.项目地址: https://gitcode.com/GitHub_Trending/gd/GDevelop

改完 GDevelop 编辑器的前端代码后,有一类 bug 单测覆盖不到:界面显示的和工程实际包含的对不上、某个操作作用在了错误的对象上、或者一次未捕获的报错把整个编辑器拖垮——它们只有在"一切真正跑起来"时才出现。仓库中的newIDE/visual-tests就是为此准备的视觉冒烟测试(visual monkey tests):它在真实浏览器里像用户一样点击、拖拽编辑器,并在每一次操作之后检查编辑器没有被破坏。本文介绍如何在本地运行这套测试、如何判断"没有回归",以及如何给你正在改动的 UI 部件补上测试。

适用对象:对newIDE/app下编辑器界面代码做过修改、想在提交前确认 UI 行为不回归的开发者。

两个测试套件:先选对入口

README 把测试分成两个套件,二者覆盖的精度不同(见 newIDE/visual-tests/README.md):

storybook-tests/editor-tests/
运行对象单个编辑器的 Storybook story真实打包应用打开一个真实示例游戏
检查内容界面显示的内容 vs. 工程实际包含的内容编辑器存在且没有抛出任何错误
定位对单个编辑器做精确测试冒烟测试:"没有明显坏掉"

Storybook 套件能检查更多,因为 story 可以暴露正在编辑的对象本身;editor 套件跑的是真实应用,但只能"看页面",凡是通过菜单(桌面应用的原生菜单)、文件选择器或外部编辑器窗口的操作都不在它能力范围内,留给 Storybook 套件覆盖。

选型建议:改的是某个具体编辑器(如 Sprite 编辑器、对象列表),优先跑 Storybook 套件;改的可能是全局行为,或想确认整包应用没坏,再跑 editor 套件或--suite=all

环境准备与第一次运行

cd newIDE/visual-tests npm install

npm install除了安装 package.json 中的依赖(puppeteeradm-zipminimistserve-handler),还会下载测试所用的 Chrome。

安装后先列出全部测试,确认能看到各套件下的用例:

node run.js --list

--list按套件分组输出每个测试名,并标注它是随机会话(N random sessions of M manipulations)还是固定步骤(N manipulations)。

运行入口是 run.js,README 给出的主路径命令:

node run.js --suite=storybook # builds Storybook if needed, then runs node run.js --suite=editor # downloads the latest portable build node run.js --suite=all

首次运行有明确的副作用,运行前先知道:

  • Storybook 套件:如果newIDE/app/build-storybook/iframe.html不存在,会先在newIDE/app目录执行npm run build-storybook(lib/StorybookServer.js 注释说明需要几分钟),然后把构建产物作为静态服务起在本地端口(--storybook-port,默认 9010),测试通过iframe.html?id=<story>逐个打开 story。
  • editor 套件:默认下载master分支最新的 portable Linux 构建(lib/RealEditor.js 中说明来源为 S3 上传的最新构建),并把测试用到的示例游戏(如platformer,sparse-clone 自 GDevelop-examples 仓库的main分支)克隆到工作目录,再启动这个 Electron 应用打开工程。下载与克隆的产物默认放在./work--work-dir可改)。

也可以用 package.json 的 npm scripts 等价触发:npm run listnpm run test(等于--suite=all)、npm run test-storybooknpm run test-editor

只跑与你改动相关的测试

全量跑之前,常用这几个筛选参数(均见 README 的参数表与 run.js):

参数用途
--test=<part of a name>只运行名字包含该子串的测试
--only-changed --base-ref=<ref>只运行与改动文件相关的测试;--base-ref缺省为origin/master
--list-names/--tests-file=<path>列出测试名 / 只运行文件里列出的测试,用于把测试拆分到 CI 并行容器
--verbose记录随机会话的每一次操作
--headful显示浏览器窗口,肉眼观察测试在做什么

--only-changed的判定规则来自各 helper 的声明:每个 helper 在自己的paths字段里列出它的测试所监视的源码文件,只有这些文件有改动时对应测试才运行;而改动测试文件本身会让所有测试都与改动相关。CI 在分支上就走这条路径:只跑与改动相关的测试,没有相关改动则什么都不构建;在master上全量运行,因为其他地方的改动同样可能弄坏一个编辑器。

其他常用控制项:

  • --storybook-url=<url>:复用已在运行的 Storybook,跳过构建与自带服务。
  • --rebuild-storybook:即使已有构建产物也强制重新构建 Storybook。
  • --gdevelop-zip=<path>:用本地 portable 构建替代下载,例如 run.js 头部注释给出的node run.js --suite=editor --gdevelop-zip=../electron-app/dist/gdevelop.zip
  • --gdevelop-branch=<branch>:下载指定分支的最新 portable 构建(缺省master)。
  • --editor-monkey-steps=<n>:真实应用上随机会话的操作次数(缺省 30)。
  • --chrome-path=<path>:指定运行测试所用的 Chrome。
  • --junit-path=<path>:把结果写成 JUnit 文件,供 CI 展示与负载均衡。

如何判定"没有回归"

运行结束时,lib/Reporter.js 会输出逐测试结果与汇总。每个测试一行,格式是:

<N> manipulations performed, <M> skipped, ✅ no crash, and the editor stayed consistent

出现失败时同一位置显示❌ N problem(s),命中已知问题时显示⚠️ stopped by a known issue(以上为该报告代码输出的实际格式,具体数字随运行变化)。结尾的汇总段输出X/Y passed, Z manipulations performed in total.

判定口径以进程退出码为准(run.js 末尾):0全部通过、1存在失败、2测试运行本身出错。也就是说,"不回归"的机器可读结论是退出码为0

每次运行还产出可人工复查的工件,默认写入./artifacts/<suite>/--artifacts-dir可改):

  • 日志文件<suite>-visual-tests.log与同名.json结果文件;
  • 每个测试一张截图,文件名由测试名派生;editor 套件的测试在关键节点(如打开场景后、操作完成后)额外截图,失败时截failure图。

而"每次操作之后"具体检查什么,README 列了五条:

  1. 页面没有抛出任何错误(未捕获错误会弄垮编辑器);
  2. 编辑器仍然显示在页面上;
  3. 它显示的内容与工程包含的一致(helper 能读取时);
  4. 这次操作确实改变了某些东西,保证测试不可能"什么都没做"就通过;
  5. 有精确结果预期(expect)的操作会被检查,helper 声明的不变量(stepChecks)保持成立。

editor 套件更粗:它读不到应用内对象的真实数据,所以只确认编辑器在、且没有东西抛出——这正是它"冒烟测试"的定位。

已知问题:大声报告,但不阻塞运行

测试发现的、但不属于它们要守护的问题可以登记在 lib/KnownIssues.js:这类问题会被"大声"报告(日志和汇总里明确列出、JUnit 中记为 skipped),但不让本次运行失败,避免分支被它没有引入的问题卡住。文件头注释要求:问题修复后立刻删除对应条目,这样它一旦复现,运行会重新失败。当前文件里登记的就是 react-dnd 丢失拖拽源(Invariant Violation: Expected sourceIds to be registered)这一条,且说明它在引入这些测试之前就会出现,不是编辑器本身的回归。

为新改动的 UI 部件补测试

给编辑器加测试时,测试文件导出一个测试数组,测试按套件各有一个形态(示例直接取自 README):

const spriteEditor = require('../helpers/SpriteEditor'); module.exports = [ { name: 'sprite-editor/delete-animations', helper: spriteEditor, story: 'objecteditor-spriteeditormanipulations--manipulations', steps: [ ['selectFrames', { row: 1, frames: [0, 1] }], ['deleteAnimation', { row: 0 }], ], }, { name: 'sprite-editor/monkey', helper: spriteEditor, story: 'objecteditor-spriteeditormanipulations--manipulations', monkey: { seeds: [1, 2], steps: 60 }, }, ];

两种写法:steps是固定操作序列;monkey是随机会话,seeds使随机序列可复现。editor 测试则是打开一个真实示例游戏并驱动它:

{ name: 'editor/add-a-behavior-from-the-store', example: 'platformer', // sparse-cloned from GDevelop-examples helpers: [spriteEditor], // page helpers to install (optional) run: async ({ page, reporter, screenshot, runSteps, runMonkey }) => { ... }, }

真正了解某个编辑器的地方是helpers/下的helperlib/里的东西都是通用的:驱动页面、运行与检查操作、两个套件与报告,任何一样都不知道具体编辑器的存在)。helper 需要提供的字段:

字段职责
installPageHelpers在页面内运行:找到编辑器的控件并读取其显示内容,注册 resolver 使目标(一行、一帧……)可以像通用目标一样使用
actions可执行的操作,包括随机会话如何挑选它们(pick)以及操作后必须为真的断言(expect
describe/check编辑器显示了什么、以及显示是否与工程一致
snapshot(可选)操作可能改变什么,用来区分"没起效的操作"和"生效的操作"
stepChecks(可选)编辑器的不变量:每个不变量在其名字被标记的每次操作后检查
summarize(可选)打开 story 时记录一行描述当前显示
paths该测试监视的源码文件:只有这些文件变化时测试才运行(--only-changed的依据)

helpers/SpriteEditor.js 是完整示例,helpers/ObjectsList.jshelpers/BehaviorsEditor.jshelpers/PropertiesPanel.js是更小的例子,editor 测试用它们来触达和操作应用的其他部分。已有的用例可参考 storybook-tests/sprite-editor.js 与 editor-tests/sprite-editor.js:前者对 Sprite 编辑器覆盖了增删动画、拖拽、右键菜单、外部编辑器交互、锁定列表等场景及多个随机会话;后者在真实应用里逐个打开示例游戏场景中每个对象的编辑器,确认都能打开并关闭。

编写测试时的快速反馈回路

README 给出的最快迭代方式是:一边在newIDE/appnpm run storybook跑开发版 Storybook,一边执行

node run.js --storybook-url=http://localhost:9009 --headful --test=<name>

--test=<name>中的<name>换成你正在写的测试名(或名字片段),只跑这一个用例并在可见浏览器里观察它的每一步,而不是等完整构建。

边界与限制

  • editor 套件测的不是你分支上的应用改动。在分支上,editor 套件针对的是master的最新 portable 构建(分支本身不构建应用),所以它执行的是你分支上的测试代码,而不是你分支对应用本身的改动——那些改动由master上同一套件在build-linux之后来测。Storybook 套件则直接基于你本地构建的产物运行。
  • 原生菜单、文件选择器、外部编辑器窗口驱动不了:桌面应用的原生菜单不可操控,这类操作由 Storybook 套件覆盖。
  • editor 套件面向 portable Linux 构建,依赖网络下载(可用--gdevelop-zip指向本地 zip 替代);示例游戏来自 GDevelop-examples 仓库的main分支。
  • 测试名、套件目录、参数名以上文 README 与 run.js 为准;--list的输出可以直接作为核对当前用例集合的方式。

【免费下载链接】GDevelop🎮 Open-source, cross-platform 2D/3D/multiplayer game engine designed for everyone.项目地址: https://gitcode.com/GitHub_Trending/gd/GDevelop

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询