- 后端
- 前端
- 开发工具
【免费下载链接】compiler-explorer
Run compilers interactively from your web browser and interact with the assembly
Compiler Explorer 是一个在浏览器中交互式运行编译器并查看汇编输出的开源项目,其前端界面高度依赖 GoldenLayout 面板、Monaco 编辑器与大量异步编译/API 请求,对端到端测试提出了独特挑战。本文以仓库内 cypress/README.md 为核心骨架,结合 cypress/support/utils.ts、cypress.config.ts 及cypress/e2e/下的真实测试用例,系统讲解如何在 Compiler Explorer 中搭建 Cypress E2E 测试环境、规避 GoldenLayout 模板元素、管理 intercept 生命周期等关键问题,读完即可上手编写稳定、可维护的前端 E2E 测试。
一、测试环境准备:启动干净的 Compiler Explorer 实例
Compiler Explorer 的所有 Cypress 测试(存放于cypress/e2e/目录)都依赖一个本地运行的 CE 实例。启动命令如下:
npm run dev -- --language c++ --no-local其中--no-local标志至关重要:它确保测试环境不会加载任何本地 properties 配置,从而让每次测试都运行在一套干净、可复现的配置之上。这一点在 docs/UsingCypress.md 中有同样的强调。--language c++指定默认语言为 C++,保证测试页初始代码与断言(如默认代码输出中包含square符号)一致。
从package.json看,dev脚本的实际定义为cross-env NODE_ENV=DEV node --no-warnings=ExperimentalWarning --import=tsx app.ts,即通过 tsx 直接启动后端应用,属于开发模式服务器。测试默认使用 C++ 语言配置,这是仓库中大多数 e2e 测试(如compile.cy.ts、multi-compiler.cy.ts)的默认语言。
二、运行 Cypress 测试的三种方式
服务启动后,在另一个终端中即可运行测试。仓库在package.json中预置了以下脚本:
"cypress": "cypress run", "ts-check:cypress": "tsc -p ./cypress/tsconfig.json --noEmit"实际使用方式有三种:
# 1. 运行全部 Cypress 测试(无头模式,CI 推荐) npm run cypress # 2. 只运行指定测试文件 npm run cypress -- run --spec "cypress/e2e/claude-explain.cy.ts" # 3. 打开 Cypress 交互式界面(开发调试推荐) npm run cypress:open使用交互式界面时,选择 "E2E Testing" 入口,再选择目标浏览器(Chrome 或 Electron 均可)。注意npm run cypress:open等价于直接执行npx cypress open(见 docs/UsingCypress.md)。
除运行测试外,仓库还提供了类型检查命令npm run ts-check:cypress,它基于 cypress/tsconfig.json(继承tsconfig.base.json,开启types: ["cypress", "node"])对测试代码做--noEmit类型检查,建议在提交测试代码前执行。
三、Cypress 配置解析
仓库根目录的 cypress.config.ts 是 Cypress 的唯一配置入口:
import {defineConfig} from 'cypress'; export default defineConfig({ viewportWidth: 1280, viewportHeight: 1024, e2e: { baseUrl: 'http://127.0.0.1:10240/', supportFile: 'cypress/support/utils.ts', }, });各配置项含义如下:
| 配置项 | 值 | 作用 |
|---|---|---|
viewportWidth | 1280 | 测试视口宽度,模拟桌面浏览器,覆盖TestingTheUi.md中"测试各种视口尺寸"的基本场景 |
viewportHeight | 1024 | 测试视口高度 |
e2e.baseUrl | http://127.0.0.1:10240/ | 所有cy.visit('/')的相对路径都会基于该地址解析,10240是本地开发服务器的默认端口 |
e2e.supportFile | cypress/support/utils.ts | 将测试辅助函数文件同时注册为 support 文件,使公共工具在测试运行前加载 |
值得注意的是,supportFile直接指向 cypress/support/utils.ts,这意味着该文件既是工具函数库,也被 Cypress 在测试执行前自动加载(其顶部import '../../static/global';会引入前端全局类型定义)。
四、测试组织规范
cypress/e2e/目录下共有 13 个测试文件,覆盖了 Compiler Explorer 的主要功能面:
compile.cy.ts:基础编译流程与源码-汇编行号联动claude-explain.cy.ts:Claude Explain AI 解释功能(含 consent、no-ai指令、错误处理)multi-compiler.cy.ts:多编译器面板操作diff.cy.ts、gccdump.cy.ts、opt-view.cy.ts、pp-view.cy.ts:各类视图面板execute.cy.ts、embed.cy.ts、frontend.cy.ts、monaco-test.cy.ts、shortcuts.cy.ts、conformance.cy.ts:执行、嵌入、快捷键等其余能力
cypress/README.md给出了明确的组织纪律:
- 与某个功能强相关的辅助函数(helper)保留在对应测试文件内部,不全局外泄;
- 只有真正可跨测试复用的工具才放入
cypress/support/utils.ts; - 辅助函数命名要具备自描述性(如
openClaudeExplainPaneWithOptions一目了然地表明"带参数打开面板"); - 相关用例用
describe块分组,同一功能的多个测试复用一致的测试数据。
以 cypress/e2e/compile.cy.ts 为例,其结构完全遵循上述纪律:beforeEach(visitPage)每次测试前全新加载页面,afterEach中断言控制台无异常输出(assertNoConsoleOutput()),用例按describe('Basic compilation')、describe('Source-assembly line linking')分组。
五、公共测试工具库速览
cypress/support/utils.ts 是仓库沉淀下来的核心测试工具集,理解它们能显著降低编写新测试的成本:
| 函数 | 用途 |
|---|---|
stubConsoleOutput(win) | 在页面加载前 stubconsole.log/warn/error,配合assertNoConsoleOutput()捕获 JS 异常 |
visitPage() | 标准beforeEach:访问首页并完成控制台 stub |
waitForCompilationToSettle() | 等待进行中的编译到达终态(成功/失败图标出现),避免编译结果重渲染导致下拉菜单按钮被中途 detach |
findPane(titleMatch) | 按可见的 GoldenLayout 标签标题文本定位面板,返回.lm_content内容区 |
sourceEditor()/compilerOutput() | 获取源码编辑器 / 编译器输出的 Monaco 编辑器 |
setMonacoEditorContent(content, editorIndex) | 绕过 DOM 输入,直接通过 Monaco APImodel.setValue()写入内容,随后等待编译结束 |
monacoEditorTextShouldContain(...) | 断言 Monaco 编辑器.view-lines文本包含(并归一化 U+00A0 不间断空格) |
setupAndWaitForCompilation() | 组合式快捷流程:等待编辑器 + 等待编译 settle + 断言输出包含square |
addCompilerFromEditor()/addCompilerFromCompilerPane() | 从源码/编译器面板的 "Add new" 下拉添加新编译器 |
openExecutor()/openGccDump()/openPreprocessor()/openOptRemarks() | 通过data-cy选择器打开对应功能面板 |
clearAllIntercepts() | 清理 Cypress intercept 的兼容工具(注意:源码注释标明该函数实际并未真正清空 routes,仅供claude-explain.cy.ts向后兼容) |
上述工具大量依赖data-cy测试选择器(如data-cy="new-editor-dropdown-btn"、data-cy="new-view-explain-btn"),这是仓库前端组件与测试代码之间的稳定契约,写测试时应优先使用data-cy而非脆弱的 CSS class。
六、重要测试模式与经验教训
cypress/README.md总结了 8 条从实战中沉淀下来的关键经验,这也是本文最值得反复阅读的部分。
1. 始终使用:visible选择器
GoldenLayout 会在 DOM 中创建不可见的模板元素,这些元素虽然存在,却可能被cy.get()选中,导致断言作用于错误的节点。所有涉及 GoldenLayout 面板内容的选择器都应追加:visible:
// ❌ 错误:可能选中模板元素 cy.get('.explain-content').should('contain', 'text'); // ✅ 正确:只选中可见元素 cy.get('.explain-content:visible').should('contain', 'text');这一模式在claude-explain.cy.ts中贯穿始终(如cy.get('.explain-consent:visible')、cy.get('span.lm_title:visible'))。cypress/support/utils.ts 中的findPane也专门使用span.lm_title:visible来规避模板元素。
2. 在afterEach中清理 intercept,避免 O(n²) 性能退化
Cypress 的 intercept 会在测试之间累积,导致后续测试请求匹配越来越慢,整体出现 O(n²) 性能退化。正确的清理姿势是在afterEach中清空路由与别名:
import {clearAllIntercepts} from '../support/utils'; afterEach(() => { // 方式一:使用工具函数 clearAllIntercepts(); // 方式二:手动清理 cy.state('routes', []); cy.state('aliases', {}); // ... 其他清理(localStorage、sessionStorage 等) });claude-explain.cy.ts的afterEach是标准范例:清空routes/aliases后,再清理localStorage、sessionStorage并重置win.compilerExplorerOptions,确保状态不跨用例泄漏。
3. Mock 的建立时机至关重要
任何可能触发请求的动作(如打开面板、点击按钮)之前,必须先把对应 API 的 mock 建立好:
// ❌ 错误:面板构造可能已经发出请求,mock 来不及生效 openClaudeExplainPane(); mockClaudeExplainAPI(); // ✅ 正确:先 mock,再触发动作 mockClaudeExplainAPI(); openClaudeExplainPane();claude-explain.cy.ts中的mockClaudeExplainAPI()在页面加载阶段(onBeforeLoad钩子)就通过win.compilerExplorerOptions.explainApiEndpoint指向http://test.localhost/fake-api/explain,随后才打开面板,正是这一原则的体现。
4. 等待异步 DOM 更新,而不只是等待 API 返回
cy.wait('@getOptions')只能保证请求完成,不能保证 DOM 已渲染完毕。更稳健的做法是等待特定的 DOM 状态(如加载占位消失):
// ❌ 错误:API 完成但 DOM 可能尚未更新 cy.wait('@getOptions'); cy.get('.dropdown').select('value'); // ✅ 正确:先等待加载占位消失,再操作 cy.wait('@getOptions'); cy.get('.dropdown option[value="loading"]').should('not.exist'); cy.get('.dropdown').select('value');claude-explain.cy.ts中的waitForDropdownsToLoad()正是该模式:断言option[value="loading"]消失后才继续操作下拉框。
5. 使用测试数据,而非生产值
所有 mock 响应应使用显而易见的假数据,如test_first、test_second、focus_a、focus_b,避免使用beginner、expert、assembly等真实业务词汇。这样做的收益有三:测试输出一眼可辨、不会与真实值混淆、杜绝误触生产 API 的可能。claude-explain.cy.ts中 audience 值统一为test_first/test_second/test_third,explanation 类型统一为focus_a/focus_b/focus_c,是这一规范的直接体现。
6. 提取辅助函数,但要分层存放
功能专属的辅助函数留在测试文件内,通用工具才放support/utils.ts:
// 测试文件内:功能专属 helper function openClaudeExplainPaneWithOptions() { mockClaudeExplainAPI(); openClaudeExplainPane(); cy.wait('@getOptions'); waitForDropdownsToLoad(); } // utils.ts 中:通用 helper export function setMonacoEditorContent(content: string) { ... }compile.cy.ts与claude-explain.cy.ts均从../support/utils导入通用函数,同时各自定义功能级 helper,分工清晰。
7. 认识静态状态与实例状态的差异
某些状态是静态的(static)——在关闭/重开面板后依然存在(如 consent、缓存);另一些是实例级的——面板关闭即丢失。设计"状态应持久化"的测试时,必须先确认该功能依赖的是哪一类状态。claude-explain.cy.ts中的should remember consent for the session用例就验证了:首次同意后关闭面板,重新打开时 consent 对话框不再出现(静态状态生效)。
8. 拦截并封锁生产 API
为防止测试误触线上服务,应显式拦截生产 API 并返回错误,这也能在测试早期暴露配置问题:
cy.intercept('https://api.compiler-explorer.com/**', { statusCode: 500, body: {error: 'BLOCKED PRODUCTION API'}, }).as('blockedProduction');claude-explain.cy.ts的setupClaudeExplainEnvironment()中即有"belt and suspenders"双保险:先clearAllIntercepts(),再封锁生产 API,随后才注入测试端点配置。
七、常见问题与解决方案对照表
cypress/README.md将最常见的问题整理成了"现象—原因—解法"对照表,这里完整保留并补充仓库中的实际依据:
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 测试运行越来越慢 | intercept 在用例间累积(O(n²)) | 在afterEach中调用clearAllIntercepts()或手动cy.state('routes', []) |
| 元素明明可见却报 "Element not found" | 选中了 GoldenLayout 的模板元素 | 使用:visible伪选择器(参考findPane的实现) |
| API mock 不生效 | mock 在请求发出之后才建立 | 在打开面板/触发动作之前完成cy.intercept设置 |
| 下拉框选择失败 | 异步数据尚未填充完成 | 先等待加载占位(如option[value="loading"])消失 |
| 测试中状态未持久化 | 混淆了静态状态与实例级状态 | 确认功能依赖 static 状态(如 consent、缓存),再编写持久化断言 |
八、调试技巧
当测试失败时,cypress/README.md给出了五条实用调试路径:
- 用
cy.log()输出实际拿到的值,确认数据与预期是否一致; - 检查 Cypress 命令日志中是否有意外发起的 API 请求;
- 关注浏览器控制台报错,它们往往暴露了 JS 层面的问题(
compile.cy.ts正是通过assertNoConsoleOutput()把控制台错误变成测试失败); - 用
.then()在特定时间点检查元素状态,定位异步时序问题; - 打开浏览器 Network 面板,确认请求命中 mock 而非生产环境。
此外,若怀疑与编译异步流程相关,可复用waitForCompilationToSettle()——它专门处理"编译结果重渲染导致按钮被 detach"这类隐蔽时序问题(详见 cypress/support/utils.ts 中该函数的注释说明)。
九、相关文档与进一步阅读
若需系统性地回归验证 UI 各组件,可参考仓库内的 docs/TestingTheUi.md 清单,它按 Modal、Dropdown、Toast、Popover、Card 等组件类型给出了详细的 UI 验证点;测试环境搭建的简要版本可参考 docs/UsingCypress.md。二者的关系是:UsingCypress.md解决"怎么跑起来",cypress/README.md解决"怎么写好测试",而TestingTheUi.md解决"测什么"。
<输出文章>
- 后端
- 前端
- 开发工具
【免费下载链接】compiler-explorer
Run compilers interactively from your web browser and interact with the assembly
相关推荐
Compiler Explorer 前端 E2E 测试实战:使用 Cypress 运行 UI 自动化测试
Compiler Explorer 前端 E2E 测试实战:使用 Cypress 运行 UI 自动化测试 本篇技术指南以 Compiler Explorer 仓
后端前端开发工具终极指南:如何使用Playwright为kkFileView构建可靠E2E测试
终极指南:如何使用Playwright为kkFileView构建可靠E2E测试 kkFileView是一款基于Spring Boot的通用文件在线预览项目,为开
后端LibrePhotos 前端 E2E 测试贡献指南:基于 Cypress + Cucumber 的 Gherkin 测试套件实战
LibrePhotos 前端 E2E 测试贡献指南:基于 Cypress + Cucumber 的 Gherkin 测试套件实战 导读 本文围绕 apps/do
后端前端移动开发计算机视觉机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考